Power-User Systems
Rotunda
Real HTML panels on a drag-spun 3D cylinder. A hand-rolled perspective projection written onto DOM elements rather than a canvas — so the text stays selectable, the links stay clickable, and a screen reader still reads every panel while the ring turns.
Install
# Rotunda needs no npm packagesThen copy 2 files into your project:
- components/registry/rotunda/rotunda.tsx
- hooks/use-animation-loop.ts
Props
| Prop | Type | Default | Description |
|---|---|---|---|
| radius | number | 380 | Distance from the axis to each panel face. Together with the number of panels this decides whether the ring closes: too small for the count and the faces intersect, which is the one way this arrangement can look broken rather than tight. |
| perspective | number | 1200 | Depth of the projection, and the control that decides whether this reads as expensive or as a cheap paper fan. Near enough and the front panel distorts violently; far enough and the ring flattens into a row of cards. |
| autoSpin | number | 0.06 | Idle rotation while nothing is being dragged. It stops the instant the pointer arrives and does not restart until it leaves, because a carousel that keeps moving under your cursor is a carousel you cannot read. |
| friction | number | 0.94 | How much of a fling survives, and the whole feel of the throw. Applied per second rather than per frame, so the same gesture coasts the same distance on a 60Hz and a 120Hz display — the naive per-frame form silently halves the throw on better hardware. |
| snap | boolean | true | Settle to the nearest panel once a fling drops below walking pace. Off, the ring stops wherever it stops — honest for a texture, wrong for a menu. |
| snapStiffness | number | 140 | How hard the ring is pulled to the nearest face. Damping is derived from it at the critical ratio, so raising this arrives faster without ever adding the overshoot that would read as a wobble on a menu. |
| depthDim | number | 0.65 | How far the back of the ring dims toward the background. Depth cueing sells the illusion harder than the rotation does, because a CSS transform gives you no occlusion to work with. |
| depthBlur | number | 3 | Blur on panels past the halfway point. Applied to an inner wrapper rather than the panel itself: a filter on a child of a preserve-3d parent establishes a containing block and flattens the entire ring into a stack. |
| faceCamera | boolean | false | Keep every panel square to the viewer rather than tangent to the ring. This turns the carousel into a card rack, and it is the right choice the moment the panels carry real copy you expect anyone to read off-centre. |
| dragSensitivity | number | 0.35 | Degrees of rotation per pixel of drag. The honest test is whether the panel under your finger stays under your finger — too low and the ring feels geared down, too high and a small gesture spins it past the panel you were reaching for. |
What It Demonstrates
- Drag
- Moving an element by grabbing it, often with momentum when released.
- Momentum
- Motion that carries velocity, especially after a drag or interruption.
- Damping
- How quickly a spring settles. Lower damping means more bounce and oscillation.
- Perspective
- How strong the 3D effect looks — a lower value exaggerates depth, like the viewer is closer.
- 3D tilt / Flip
- Rotate in 3D space (rotateX / rotateY) to add depth.
- Spatial consistency
- Animating so an element keeps its identity and position across states, so users never lose track of where things went.
Related Components
Weave
A sheet of cloth on a position-based solver — particles and distance constraints relaxed to convergence every fixed step, with no forces, no rotations and no contacts. Drag it, load it, and pull until the constraints fail and the tear runs. Every quad is shaded by its own extension against the tear threshold, so the load path through the sheet is visible long before anything breaks.
Physics Engine
A from-scratch 2D rigid-body physics lab — fixed-timestep impulse solver, drag-to-throw bodies, force and velocity overlays, and a live energy readout on a blueprint grid.
Ascii Engine
A lit, raymarched 3D solid rendered at exactly one pixel per character cell, then quantised into a glyph ramp drawn once with fillText. Drag to orbit it through a full three-sixty. Real geometry underneath, terminal on top — and no scene graph, no mesh, and no second library to get there.
Rewind
An undo history you can scrub, and that refuses to throw the future away. Drag the head and the interface reverts live under your finger rather than on release; act from a past point and the abandoned future stays drawn on the rail as a branch you can walk back onto, instead of being silently destroyed the way every real undo stack destroys it. Controlled in the same shape as Ledger — it owns the rail, the gesture and the branch graph, and your reducer owns the states.
Infinite Canvas
A pannable, zoomable infinite board holding real DOM cards — one world element under a single translate-and-scale, so focus survives and nothing ever remounts when the camera moves. Drag the background to pan with inertia; the wheel zooms to the cursor by holding the world point under it fixed; cards drag independently and glide onto the grid on release; the corner minimap mirrors the extent and jumps the camera where you click. Camera state lives in one ref written by handlers and the loop, and every consumer — world transform, backdrop plane, minimap — derives from it in a single write pass per frame, so React never renders a camera frame. Arrow keys pan and plus and minus zoom when the board holds focus.
Black Hole
A real-time Schwarzschild black hole raytraced per-pixel in a WebGL2 shader — null geodesics bend a lensed accretion disk over the event horizon, with Doppler beaming, gravitational redshift, a photon ring, and a lensed starfield, all under a cinematic orbit.