DEV Community

Cover image for 🌌 How I Built a GROK-Inspired Starfield & Shooting Stars Using HTML Canvas ✨
MUHAMMAD USMAN AWAN
MUHAMMAD USMAN AWAN

Posted on

🌌 How I Built a GROK-Inspired Starfield & Shooting Stars Using HTML Canvas ✨

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.

GitHub ⭐

Live Demo πŸš€


🌌 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
Enter fullscreen mode Exit fullscreen mode

No build step. Just open index.html in a browser.


🧱 HTML: Minimal Canvas Setup

<canvas id="starfield"></canvas>
Enter fullscreen mode Exit fullscreen mode

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;
}
Enter fullscreen mode Exit fullscreen mode

Key ideas:

  • overflow: hidden prevents scrollbars
  • position: fixed keeps the canvas locked to the screen
  • pointer-events: none lets UI elements sit above it

🧠 JavaScript: Core Concepts

Canvas & Context

const canvas = document.getElementById("starfield");
const ctx = canvas.getContext("2d");
Enter fullscreen mode Exit fullscreen mode

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
}
Enter fullscreen mode Exit fullscreen mode

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();
}
Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode

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,
}));
Enter fullscreen mode Exit fullscreen mode

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);
Enter fullscreen mode Exit fullscreen mode

Each frame:

  1. Clear the canvas
  2. Update star positions
  3. Draw stars
  4. Possibly spawn a shooting star
  5. 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);
Enter fullscreen mode Exit fullscreen mode

This is classic circular motion math.

Flicker / Glow Effect

const flicker = 0.4 + Math.abs(Math.sin(Date.now() * 0.0015 + i)) * 0.5;
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode

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
);
Enter fullscreen mode Exit fullscreen mode

Opacity fades toward the tail, creating a natural streak effect without particles.


βš™οΈ Easy Customization

All tuning lives in script.js:

Density

const numStars = 360;
Enter fullscreen mode Exit fullscreen mode

Flicker Strength

0.4  // base brightness
0.5  // flicker amplitude
Enter fullscreen mode Exit fullscreen mode

Shooting Star Frequency

Math.random() < 0.01
Enter fullscreen mode Exit fullscreen mode

Speed

vx: 3 + Math.random() * 2
vy: 1 + Math.random() * 1.5
Enter fullscreen mode Exit fullscreen mode

πŸ’‘ 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)