Backesc

— /playground

Small interactive things.

A rotating set of browser experiments — the kind of thing that doesn't fit in a client project but keeps the muscles loose. Everything here runs on plain canvas or CSS; no 3D libraries, no dependencies beyond what the site already ships.

Flow field

~220 particles · canvas 2d

Each particle reads a 2D vector field built from layered sine waves, moves one step along it, and draws a short segment behind itself. The background fades at low opacity every frame — no trail buffer, no extra memory, just the canvas forgetting slowly. Move the cursor to push particles away.

paused · press play

Density
Speed

How it works

  • Vector field — angle at each point is sin(x·k + t) + cos(y·k + t)·0.8 + sin((x+y)·k + t)·0.6, multiplied by π. Cheap to evaluate per-particle and flows smoothly across the canvas.
  • Trails — every frame fills the canvas with rgba(10,10,11,0.06) before drawing the new segments. Old strokes dim gradually instead of being cleared.
  • Cursor — within a 120 px radius, each particle is pushed away with a linear falloff. No pointer listeners means no redraw cost on mobile.
  • Pause as ref — the rAF loop reads playingRef.current and skips physics when paused, so toggling Play/Pause doesn't reinitialise the particles.

Evade

homing enemies · last as long as you can

Your cursor is the white dot. Red enemies spawn from the edges and steer toward you with a homing acceleration. The spawn interval shrinks and the speed cap grows with every wave. Click Play to start — it doesn't run until you ask.

— Evade

Stay alive as long as you can.

Your cursor is the white dot. Red enemies spawn from the edges and chase you. They get faster. Don't let them touch.

Needs a mouse or trackpad.

Design notes

  • Homing, not tracking — each enemy accumulates a small velocity vector toward the player (capped by a max speed). That's why fast swerves throw them off before they correct, instead of the enemy teleporting to follow.
  • Difficulty as two knobs — spawn interval decays by 1.5% per spawn (floor 14 frames); enemy max speed grows by 0.02 px/frame per spawn (ceiling 2.8). No levels, no timers — the curve does the work.
  • Score in the DOM, not React — the timer text is written directly via ref.current.textContent inside the rAF loop, so the game doesn't re-render 60 times a second just to update a number.
  • Cursor-off pause — when the pointer leaves the canvas, enemies freeze and the timer stops. No cheap deaths from the cursor wandering into the sidebar.