🍦 Vanilla JS Starry Night
Tags: html, css, javascript
Published: 2020-12-25 16:44:03

In this post I would like to share with you a Vanilla JS demo of twinkling stars on a smooth black canvas. Basically, this kind of challenge involves dealing with random parameters like a star’s width, height, coordinates and twinkling duration. I think it is probably better to start with the relevant portion of the CSS for this small demo and work our way to the JS part. 

Here are some links for this demo:

👉 GitHub repo:  [https://github.com/yossi-abramov/starry-night](https://github.com/yossi-abramov/starry-night)

👉 GitHub page:  [https://yossi-abramov.github.io/starry-night/](https://yossi-abramov.github.io/starry-night)

### CSS

```css
.star {
  --twinkle-duration: "";
  --twinkle-delay: "";
  --star-size: "";
  position: absolute;
  width: var(--star-size);
  height: var(--star-size);
  background: #fff;
  border-radius: 50%;
  animation: twinkle infinite alternate;
  animation-duration: var(--twinkle-duration);
  animation-delay: var(--twinkle-delay);
}

@keyframes twinkle {
    from {
        transform: scale(1);
    }

    to {
        transform: scale(1.5);
        box-shadow: 0 0 5px 0.5px rgba(150, 150, 150, 0.6);
    }
}
```

I’ve created a  `.star`  class for my stars and initialised CSS variables in it. By scoping these variables to the  `.star`  class we can make these variables behave like “arguments”. That means we can set a value to our scoped CSS property on a given star element (Just think of all the cool things you can build with CSS variables!). Next, I’ve set a  `keyframe`  animation for the “twinkling” effect. As you can see, both  `animation-duration`  and  `animation-delay`  have a CSS variable assigned to them. As for the twinkle  `keyframe`, it’s pretty straight forward: alternate infinitely between a scale of 1 and 1.5 of the star’s size and add a  `box-shadow`  for a soft glow effect.

### JS

Now, let’s go over the JS portion of the code. As mentioned above, we need to deal with some random “star” properties. For that reason the first thing we need is a function that will generate a random number between a  `min`  and a  `max`. For that purpose, we can use the mighty JS Math Object:

```javascript
const genRandomNumber = (min, max) => {
    return Math.random() * (max - min) + min;
}
```

After setting up our  `genRandomNumber`  function we can move on to defining the boundaries of our microcosmos/canvas/night sky. Remember, we need to spread our stars randomly across a space. So first we need to select our parent and give our stars coordinates relative to the selected parent. In this case, I’ve selected the  `body`  element:

```javascript
const $el = document.body;
```

Now, all we need to do is create an element, append a  `.star`  class to it and pass the required random CSS properties that we defined in our CSS. After our star element is created, we will simply append it to the parent element -  `body`  in this case. We will need to repeat this process  `x`  amount of times – so a simple  `for`  loop will do!

```javascript
for (let index = 0; index < 1000; index++) {
    const star = document.createElement("div");
    star.classList.add("star");
    
    // Star coordinates
    let x = genRandomNumber(1, $el.offsetWidth);
    let y = genRandomNumber(1, $el.offsetHeight);

    star.style.left = Math.floor(x) + "px";
    star.style.top = Math.floor(y) + "px";
    
    star.style.setProperty(
        "--star-size",
        genRandomNumber(1, 3) + "px"
    );

    star.style.setProperty(
        "--twinkle-duration",
        Math.ceil(genRandomNumber(1, 5)) + "s"
    );

    star.style.setProperty(
        "--twinkle-delay",
        Math.ceil(genRandomNumber(1, 5)) + "s"
    );

    $el.append(star);
}
```

Let's refactor this code a bit, here is another suggestion:

```javascript
// Night Sky element

const $el = document.body;

// Generate a random number between min and max values

const genRandomNumber = (min, max) => {
    return Math.random() * (max - min) + min;
}

// Generate a star <div>

const genStar = () => {
    const star = document.createElement("div");
    star.classList.add("star");

    // Gen star coordinates relative to $el size
    let x = genRandomNumber(1, $el.offsetWidth);
    let y = genRandomNumber(1, $el.offsetHeight);

    const { style } = star;
    
    style.left = Math.floor(x) + "px";
    style.top = Math.floor(y) + "px";

    style.setProperty(
        "--star-size",
        genRandomNumber(1, 3) + "px"
    );

    style.setProperty(
        "--twinkle-duration",
        Math.ceil(genRandomNumber(1, 5)) + "s"
    );

    style.setProperty(
        "--twinkle-delay",
        Math.ceil(genRandomNumber(1, 5)) + "s"
    );

    return star;
}

for (let index = 0; index < 800; index++) {
    $el.append(genStar());
}
```

✍ For more posts:
[https://yossiabramov.com/](https://yossiabramov.com/)