Orb Catcher

Orb Catcher is a browser game built from a curl-noise fluid experiment in Rust, Macroquad, and WebAssembly.

The original prototype was my way of learning how the paper Curl-Noise for Procedural Fluid Flow uses noise to create smoke-like motion. I later turned that experiment into a game: collect the target color orbs, avoid obstacle orbs, and use movement or boosts to disturb the flow.

Playable Build

How the Visuals Work

The effect looks like a fluid simulation, but the game is not solving the full equations of fluid motion. Instead, it builds a procedural velocity field that can be sampled anywhere on the screen. That field tells each tracer particle which direction to move.

The trick from the paper is this: start with a smooth noise field, treat it as a potential field, then take its curl. In 2D, that turns one scalar value into a velocity:

psi = perlin(x * scale, y * scale, time) * amplitude

dpsi_dx = (psi(x + h, y) - psi(x - h, y)) / (2 * h)
dpsi_dy = (psi(x, y + h) - psi(x, y - h)) / (2 * h)

velocity = (dpsi_dy, -dpsi_dx)

The short version: the particles are not being pushed randomly. They follow an invisible moving field. Taking the curl rotates the gradient of that field, which creates swirling paths instead of letting particles pile up into fake drains or burst out of fake sources.

The build does this each frame:

  1. Sample time-varying Perlin noise at each particle position.
  2. Estimate the nearby slope of that noise with small finite-difference offsets.
  3. Convert that slope into a 2D curl velocity.
  4. Move each tracer particle through the field every frame.
  5. Store recent particle positions in a short trail buffer.
  6. Draw each trail as tapered line segments with a head, middle, and tail color.

The game exposes a few of those choices as settings: wave speed, particle density, tracer length, background color, and the three trail colors. That makes the same math feel very different depending on how dense, fast, or long-lived the tracers are.

Interaction Layer

The player is an arrowhead moving through the field. When the player boosts, or when an orb is collected, the game adds short-lived ripple terms to the potential field. Near the player, the field also blends from the background noise into a simple rigid-body-style influence based on the player’s velocity and the particle’s relative position.

That keeps the visuals from feeling like a screensaver behind the game. Movement disturbs the flow, the flow keeps moving on its own, and the trails make those invisible vectors readable.

Recreating the Core Effect

To recreate the visual effect in another engine, you need four pieces:

Once that works, the extra polish comes from controls: tune the noise scale for larger or smaller swirls, tune particle count for density, tune trail length for softness, and add temporary ripples when important gameplay events happen.

What I Learned

The hard part was not only the formula. It was making the formula visible: choosing defaults, drawing readable trails, tuning the motion, and packaging the Rust build as WebAssembly so it runs in a browser.

View Latest Entry regarding this project