Building a Configurable Interactive Starfield as a React Component

When I was invited to contribute some creative ideas to Temporal's site, one concept that stood out was an interactive starry backdrop—something that could sit behind content and respond to the user's presence without ever stealing focus. The result is a self-contained React component that relies on two things: an HTML <canvas> for rendering and GreenSock (GSAP) for smooth animations and utility functions. The component is deliberately configurable through props, so you can tune everything from star density to interaction radius to match your site's aesthetic.

Getting the Canvas Off the Ground

The component starts by rendering a <canvas> element and capturing a reference to it inside a React useEffect. If you're working outside React, the same effect can be achieved by storing the canvas reference in a plain variable.

CSS work is minimal: the canvas simply fills the viewport and sits behind the main content layer.

Before any stars are drawn, one foundational decision is worth making. Rather than rendering "classic" pointy star shapes, we take a shortcut: each star is just a circle with varying opacity and size. Drawing one is a matter of obtaining the canvas context and invoking the arc method.

Generating and Rendering "Stars"

The core logic lives inside a LOAD function. This function handles canvas sizing and generates an array of star objects, where each object defines the star's charactariistics:

  • x: The star's horizontal position
  • y: The star's vertical position
  • size: The star's diameter in pixels
  • scale: The star's scale factor, used during interaction
  • alpha: The star's opacity, also used during interaction

GSAP's random() utility generates the randomized values for each star. The component accepts props like sizeLimit, defaultAlpha, and densityRatio to control how those values are derived, with sensible defaults provided for each.

Once the stars are generated, a RENDER function takes over. It loops over the star array and draws each one using the canvas arc method.

A note on cleanup: we include clearRect before drawing every frame. It's not strictly required for a single static render, but it's essential once interactions begin—without it, the canvas will quickly smear across itself between frames.

Handling Resize Properly

One quirk that emerges quickly is that resizing the viewport distorts the canvas. The fix is straightforward: rerun the LOAD and RENDER functions on the resize event, ideally debounced. In the React version, the event listener is added inside the effect and cleaned up in its teardown, so remounting the component never leaves stale listeners behind.

Making the Sky Reactive

The interactive layer works by tracking pointer movement and measuring the distance between the cursor and every star. As a star gets close to the pointer, it both brightens and scales up. Further away, it fades back toward its default opacity and size.

An UPDATE function does the math. It calculates the distance between the pointer and each star, then uses GSAP's mapRange() utility to derive appropriate scale and alpha values. The LOAD function creates these mapping functions and a size unit up front, and shares them with the update logic when needed.

Two additional props control the feel of this interaction: scaleLimit caps how large a star can grow, and proximityRatio defines the area around the cursor in which stars begin reacting.

Updating star properties alone won't trigger redraws. That's where gsap.ticker comes in—GSAP's built-in animation loop, similar in spirit to requestAnimationFrame. We add the RENDER function to the ticker and ensure it's removed during teardown. The ticker also lets us cap the frame rate at a "cinematic" 24fps.

Lowering the Lights on Exit

One refinement catches a forgettable lapse: when the mouse cursor leaves the canvas, stars remain bright and upscaled. To fix this, we add a pointerleave handler that tweens all stars back down to a scale of 1 and returns each to its defaultAlpha value. This ensures the visual state settles back even if the user abruptly moves away.

A Hidden Playfulness

To finish the component, we secretly added a nod to the famous Konami Code. We attach a keyboard event listener in a separate effect, watching for the sequence and keeping the user's input in a ref-based array. Once the sequence is entered, the component can trigger whatever behavior we wish—clearing the input array afterward so the easter egg can happen again later.

The included example tweens each star's position and hue, but the underlying pattern can be bent to almost anything, including random pulsing or shifting color palettes. To support color-shifting effects, each star object gains its own hue, saturation, and lightness values, and the RENDER function resolves colors using those properties.

A timeline built with GSAP moves each star to new coordinates with a staggered position parameter, and the render loop picks up those updated values on the next tick.

The end result is a small, reusable visual layer that can be dropped into most React projects or running standalone. Its combination of canvas rendering, prop-driven configuration, and GSAP's animation primitives makes it simple to remix into different visual treatments—stars are just the starting point.