🌟 Starfield Zoom Effect Tutorial

Learn how to create a stunning 3D starfield flythrough in LÖVE (Lua)

What you'll learn: 3D to 2D projection, particle systems, perspective rendering, and real-time animation in game development.

Understanding the Core Concept

A starfield zoom effect simulates flying through space at high speed. Each star is positioned in 3D space, and we project it onto a 2D screen using perspective math. The closer a star gets to the camera, the larger and faster it appears to move.

Key Principle: Objects farther away appear smaller and move slower. Objects closer appear larger and move faster. This is called perspective projection.

Step-by-Step Breakdown

1Initialize the Project

Download the full code here: main.lua or follow the steps below.

First, we set up our LÖVE environment and define our variables:

function love.load()
    love.window.setTitle("Starfield Zoom")
    
    -- Screen dimensions
    width = love.graphics.getWidth()
    height = love.graphics.getHeight()
    centerX = width / 2
    centerY = height / 2
    
    -- Star configuration
    numStars = 400
    speed = 200
    stars = {}
    
    -- Create all stars
    for i = 1, numStars do
        table.insert(stars, createStar())
    end
end

What's happening: We store the screen center (our vanishing point), create an empty table to hold stars, and initialize 400 stars.

2Create Stars in 3D Space

Each star needs an X, Y, and Z position. We use polar coordinates to distribute them evenly around the center:

function createStar()
    local angle = love.math.random() * math.pi * 2
    local distance = love.math.random(1, 500)
    
    return {
        x = math.cos(angle) * distance,
        y = math.sin(angle) * distance,
        z = love.math.random(1, 100)
    }
end
Math Explanation:

3Update Star Positions

Every frame, we move stars closer to the camera by decreasing their Z value:

function love.update(dt)
    for i, star in ipairs(stars) do
        -- Move star towards camera
        star.z = star.z - speed * dt
        
        -- Reset star if it passes the camera
        if star.z <= 0 then
            local angle = love.math.random() * math.pi * 2
            local distance = love.math.random(1, 500)
            star.x = math.cos(angle) * distance
            star.y = math.sin(angle) * distance
            star.z = 100
        end
    end
end

Key points:

4The Magic: 3D to 2D Projection

This is where the illusion happens. We convert 3D coordinates to 2D screen positions:

-- Project 3D coordinates to 2D screen
local k = 128 / star.z
local px = star.x * k + centerX
local py = star.y * k + centerY
🎯 The Perspective Formula:

screenX = (3D_X × focalLength / Z) + centerX

screenY = (3D_Y × focalLength / Z) + centerY

Example: If a star is at x=100, z=50, then px = (100 × 128/50) + centerX = 256 + centerX

5Draw Stars with Motion Trails

We create a trail effect by drawing a line from the previous position to current position:

-- Calculate previous position for trail
local prevZ = star.z + speed * (1/60)
local prevK = 128 / prevZ
local prevPx = star.x * prevK + centerX
local prevPy = star.y * prevK + centerY

-- Star brightness based on distance
local brightness = 1 - (star.z / 100)
local size = brightness * 3

love.graphics.setColor(brightness, brightness, brightness, 1)
love.graphics.setLineWidth(size)
love.graphics.line(prevPx, prevPy, px, py)
love.graphics.circle("fill", px, py, size)

Why this works:

6Add Interactivity

Finally, add keyboard controls to make it interactive:

function love.keypressed(key)
    if key == "escape" then
        love.event.quit()
    elseif key == "space" then
        -- Reset all stars
        stars = {}
        for i = 1, numStars do
            table.insert(stars, createStar())
        end
    elseif key == "up" then
        speed = speed + 50
    elseif key == "down" then
        speed = math.max(50, speed - 50)
    end
end

Key Concepts to Master

1. Perspective Division

The formula k = focalLength / z is the heart of perspective. As Z increases (object moves away), k decreases, making the projected position closer to the center and smaller.

2. Delta Time (dt)

Multiplying movement by dt ensures consistent speed across different frame rates. Without it, slow computers would make stars move slower!

3. Object Pooling

Instead of creating and destroying stars, we recycle them by resetting their position when they pass the camera. This is more efficient.

4. Coordinate Systems

Tweaking Parameters

Experiment with these values:

Next Steps

Once you understand the basic starfield, try these enhancements:

More Lua tutorials