Grapnel
A grappling hook on a Verlet rope, and a golden-hour city to swing through.
Downloads 3.1 MB on demand
- Click / Tap a wall
- Fire the hook
- Click sky / Space
- Let go
- W / S
- Reel in / pay out
- A / D
- Lean
- Space (on ground)
- Jump
- Drag / Scroll
- Orbit / Zoom
- Built
- Stack
- TypeScript, three.js, WebGL, Vite
How it works
There is no rigid-body engine behind this. The player is one Verlet particle, the rope is thirty more, and a single distance constraint between the two ends is the whole pendulum. The rope's links can pull but never push, so it sags when slack and snaps straight when loaded. The city is a grid of towers dressed in CC0 scans with windows painted on at load time, strung with power lines and flag strings that are Verlet chains too, so the ball and the rope knock them about. Pigeons scatter off the roofs, and eight rings and a scoreboard give the swinging somewhere to go.
Notes
- Verlet integration throughout: position and previous position only, so every constraint that moves a thing has already corrected its velocity
- Rope links are one-sided constraints, pull but never push, which is what makes slack rope hang and loaded rope go straight
- The swing itself is a single anchor-to-player distance constraint solved directly, because a pendulum that stretches reads as broken long before the rope looks wrong
- Every cable in the city is the same chain class pinned at both ends, swaying in a slow wind and shoved by whatever passes through it
- The hook bites the first surface on its line from the player, not the one under the cursor, so firing past a corner lands on the corner
- Poly Haven has concrete, cladding and tile facades but nothing glazed, so a window grid is painted onto each scan's colour, normal and roughness maps at load
- Sun direction for shadows is read from the brightest pixel of the HDRI; at golden hour it is lifted to a minimum elevation so streets are not all in shadow
- The HDR's sun peaks above what a half-float environment map can hold, so it is clamped before prefiltering - one overflow and the whole map goes to NaN, which renders black
- Every rooftop tank, lamp post, parapet and antenna is queued by material and flushed into one instanced mesh; the same city as separate meshes was 1,750 draw calls, twice over with shadows
- Sixty boids on a roof that scatter when the ball comes through, then settle on a roof further from it
- One static shadow frustum over the whole city: a shadow camera that follows the player re-quantises every edge against a grid that moved, which is the crawl and blink everyone has seen and few have named
- Landing dust is a ring of soft sprites sized and faded per particle in a small shader, since a fixed-size square is precisely what a puff of dust is not
