Recreating the GROK Starfield & Shooting Star Effect with HTML Canvas
If you've seen the GROK-style starfield animation, you know the vibe: slow, cinematic rotation, subtle flicker, and the occasional shooting star streaking across space. In this post, weβll break down a pure HTML + CSS + JavaScript implementation that recreates that effect β no libraries, no frameworks, just the Canvas API.
This project is lightweight, customizable, and perfect for landing pages, backgrounds, or creative coding experiments.
π What This Effect Includes
- Full-screen HTML5
<canvas> - Hundreds of rotating stars orbiting off-screen
- Natural flicker / glow simulation
- Rare, elegant shooting stars
- Responsive resize handling
All running smoothly via requestAnimationFrame.
π Project Structure
index.html β Canvas container
style.css β Fullscreen black background
script.js β Animation logic & customization
No build step. Just open index.html in a browser.
π§± HTML: Minimal Canvas Setup
<canvas id="starfield"></canvas>
Thatβs it. The canvas fills the entire viewport and acts as the rendering surface for everything we draw.
The JavaScript file is loaded at the bottom to ensure the DOM is ready before accessing the canvas.
π¨ CSS: Fullscreen, No Distractions
html, body {
margin: 0;
padding: 0;
background: black;
overflow: hidden;
width: 100%;
height: 100%;
}
#starfield {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
}
Key ideas:
-
overflow: hiddenprevents scrollbars -
position: fixedkeeps the canvas locked to the screen -
pointer-events: nonelets UI elements sit above it
π§ JavaScript: Core Concepts
Canvas & Context
const canvas = document.getElementById("starfield");
const ctx = canvas.getContext("2d");
Everything is drawn manually using the 2D rendering context.
β Star Data Model
Each star is stored as an object:
{
angle: Number,
radius: Number,
speed: Number,
size: Number
}
Instead of storing x/y positions directly, stars are defined using polar coordinates:
-
angleβ rotation position -
radiusβ distance from the center
This makes circular motion trivial.
π Canvas Resize Handling
function resizeCanvas() {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
initStars();
}
Whenever the window resizes:
- The canvas resizes
- Stars are regenerated to fit the new dimensions
This keeps everything sharp and responsive.
β¨ Initializing the Starfield
const numStars = 360;
Stars are created like this:
stars = Array.from({ length: numStars }, () => ({
angle: Math.random() * Math.PI * 2,
radius: Math.random() * Math.sqrt(canvas.width ** 2 + canvas.height ** 2),
speed: Math.random() * 0.0003 + 0.00015,
size: Math.random() * 1.2 + 0.5,
}));
Why this works:
- Random angles distribute stars evenly
- Large radius lets stars orbit beyond the viewport
- Tiny angular speeds create slow, cinematic motion
π The Animation Loop
Everything runs inside:
requestAnimationFrame(animate);
Each frame:
- Clear the canvas
- Update star positions
- Draw stars
- Possibly spawn a shooting star
- Update shooting stars
π Drawing Rotating Stars
star.angle += star.speed;
const x = centerX + star.radius * Math.cos(star.angle);
const y = centerY + star.radius * Math.sin(star.angle);
This is classic circular motion math.
Flicker / Glow Effect
const flicker = 0.4 + Math.abs(Math.sin(Date.now() * 0.0015 + i)) * 0.5;
Instead of blur or glow filters, brightness is faked by:
- Oscillating opacity
- Slight per-star phase offset
Simple, fast, and effective.
βοΈ Shooting Stars
Shooting stars are rare by design:
if (shootingStars.length === 0 && Math.random() < 0.01)
Only one can exist at a time, keeping the moment special.
Each shooting star has:
- Position
- Velocity
- Lifespan
π― Shooting Star Trail
The trail uses a linear gradient:
const grad = ctx.createLinearGradient(
s.x,
s.y,
s.x - s.vx * 35,
s.y - s.vy * 35
);
Opacity fades toward the tail, creating a natural streak effect without particles.
βοΈ Easy Customization
All tuning lives in script.js:
Density
const numStars = 360;
Flicker Strength
0.4 // base brightness
0.5 // flicker amplitude
Shooting Star Frequency
Math.random() < 0.01
Speed
vx: 3 + Math.random() * 2
vy: 1 + Math.random() * 1.5
π‘ Ideas for Extensions
- Mouse-based parallax
- Color-shifted stars
- Depth layers with different speeds
- Gradient space backgrounds
- Nebula noise overlays
π§ͺ Why This Approach Works
- Zero dependencies
- Extremely fast (simple math + canvas)
- Easy to embed anywhere
- Perfect for ambient UI backgrounds
This is a great example of how far plain JavaScript + Canvas can go when used thoughtfully.
Thanks for reading! π
Until next time, π«‘
Usman Awan (your friendly dev π)

Top comments (0)