2026-09-26 · tutorial

Your first starfield

A tiny animated starfield in pure HTML, CSS and JavaScript. Open your editor, paste the code below, and save it as starfield.html — then open it in a browser.

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Starfield</title>
</head>
<body style="margin: 0; background: black;">
    <canvas id="sky"></canvas>

    <script>
        // Grab the canvas element from the page by its id.
        const canvas = document.getElementById("sky");

        // Make the canvas fill the whole browser window.
        canvas.width  = window.innerWidth;
        canvas.height = window.innerHeight;

        // Get a 2D drawing context — everything is drawn through this object.
        const ctx = canvas.getContext("2d");

        // Store the center of the screen. It is used as the vanishing point.
        const cx = canvas.width  / 2;
        const cy = canvas.height / 2;

        // This array will hold all our stars.
        let stars = [];

        // Create 100 stars with random positions and depths.
        for (let i = 0; i < 100; i++) {
            stars.push({
                // x, y — screen position before projection.
                x: Math.random() * canvas.width,
                y: Math.random() * canvas.height,

                // z — depth. 1000 = far away, 0 = very close.
                z: Math.random() * 1000
            });
        }

        // Main animation loop. It runs once per frame.
        function draw() {
            // Fill the whole canvas with black to erase the previous frame.
            ctx.fillStyle = "black";
            ctx.fillRect(0, 0, canvas.width, canvas.height);

            // Stars are white.
            ctx.fillStyle = "white";

            // Move and draw every star.
            for (let star of stars) {

                // Move the star closer to the camera.
                star.z -= 5;

                // If the star passed the camera, respawn it far away.
                if (star.z <= 0) {
                    star.z = 1000;
                    star.x = Math.random() * canvas.width;
                    star.y = Math.random() * canvas.height;
                }

                // Project the 3D point (x, y, z) onto the 2D screen.
                // Dividing by z creates perspective: closer = bigger shift.
                const sx = (star.x - cx) / star.z * 500 + cx;
                const sy = (star.y - cy) / star.z * 500 + cy;

                // Closer stars are drawn bigger.
                const size = (1 - star.z / 1000) * 4;

                // Draw the star as a small filled square.
                ctx.fillRect(sx, sy, size, size);
            }

            // Ask the browser to call draw() again before the next repaint.
            requestAnimationFrame(draw);
        }

        // Start the animation.
        draw();
    </script>
</body>
</html>

How it works: each star has an x, y position and a z depth. Every frame we decrease z, which makes the star move toward the camera. We then project it onto the screen with (x - cx) / z * 500 + cx — a classic perspective formula. When a star reaches z = 0, we respawn it far away to keep the field infinite.