Project writing on physics-informed design for retro arcade aesthetics.
A retro arcade-styled surfboard leaderboard inspired by Hotline Miami. Rather than applying effects arbitrarily, I wanted each visual choice to be grounded in how CRT displays and analog recording actually work.
The approach: simulate the full signal chain from light emission to human perception.
Phosphor Emission → CRT Hardware → Camera Lens → VHS Recording → Observer
| Effect | Physics | Implementation |
|---|---|---|
| Voltage Sag | High voltage drops under load, expanding raster and dimming image | Scale and brightness modulation tied to glow pulse |
| Convergence Error | RGB electron guns drift out of alignment at screen edges | Per-channel text-shadow offsets, magnitude tied to screen position |
| Beam Landing Error | Electrons hit wrong phosphors at corners, causing color tints | Radial gradients at corners with color blend mode |
| Phosphor Glow | Electron beam excites phosphor coating, creating bloom | Layered text-shadow with pulsing spread |
| CRT Flicker | 60Hz refresh cycle with exponential phosphor decay | Full-screen overlay with time-based opacity modulation |
| Scanlines + Slot Mask | Shadow mask creates gaps between phosphor triads | CSS gradients with multiply blend mode |
| RGB Phosphor Triads | Subpixel color pattern (inverse intensity with bloom) | Vertical RGB stripes with screen blend mode |
| Interlace | Alternating odd/even scanline fields | Repeating gradient dims every other line |
| Chromatic Aberration | Camera lens disperses light into RGB fringing | Dual text-shadow (red left, cyan right), intensity tied to glow |
| VHS Glitch | Tape-head misalignment and magnetic dropout cause tracking errors | Horizontal displacement on timed cycles |
| Harmonic Sway | Layered simple harmonic waves create an intoxicated effect | Multi-frequency sine waves for translation and rotation |
Effects that occur before light is emitted—beam energy, alignment, and landing.
CRT high voltage supplies aren't perfectly regulated. Bright content draws more current, which causes the HV to drop slightly. Lower voltage means slower electrons, and slower electrons deflect more in the magnetic field. The result: the raster expands and dims on bright scenes. VOGONS
Distinct from chromatic aberration. CA is lens optics—uniform across the frame. Convergence error is CRT hardware—the three electron guns physically misalign, especially at screen edges where deflection angles are greatest. Both create RGB separation, but from different causes and with different spatial patterns. CRT-Royale
Electron beams must hit their precise phosphor color. At screen edges and corners, deflection errors cause beams to land on adjacent phosphors. The shadow mask can also warp from heat at the center. The result is position-dependent color contamination: purple or yellow tints at corners, slight desaturation at edges. CRT-Royale
Light emission and decay after electrons hit the screen.
CRT phosphors are excited by the electron beam, emitting photons as electrons return to lower energy states. At high brightness, phosphor strips physically grow taller, bleeding into adjacent dark areas. The perceived halo around bright elements comes from three sources: light scattering in atmosphere, diffraction in the eye's lens, and veiling glare in camera optics. LearnOpenGL
CRT phosphors flash when hit by the electron beam, then decay exponentially until the next refresh. This creates visible flicker, especially in peripheral vision. The P22 phosphor used in most consumer CRTs had medium persistence—fast enough for motion, slow enough to reduce flicker harshness. Unlike modern strobe/BFI, CRT flicker is a rolling scan. The gradual decay feels softer than a hard on/off cycle. Blur Busters
When phosphor glow is at its brightest, the bloom fills the gaps between phosphor triads, making the subpixel pattern less visible. The phosphor overlay opacity decreases as glow increases. CRT-Royale
Physical patterns from the shadow mask and phosphor layout.
CRT displays draw images by scanning an electron beam across the screen line by line. With 240p content, only half the screen lines are illuminated per frame, leaving visible gaps. Physical gaps also exist between horizontal rows of phosphor material on the shadow mask. MiSTer FPGA JRank
Standard-definition CRTs display interlaced video: odd lines in one field, even lines in the next, at 60 fields per second. This creates 30fps full frames from 60 fields. Fine horizontal details shimmer as alternating lines refresh. libretro
Artifacts introduced by camera optics and magnetic tape.
Different wavelengths bend at different angles through glass. Blue light (~450nm) bends more than red (~650nm) because refractive index decreases with wavelength. This creates color fringing at high-contrast edges. Two types exist: longitudinal (colors focus at different distances) and transverse (colors focus at different positions in the focal plane). Wikipedia HyperPhysics
VHS stores data magnetically. Physical wear causes dropout and noise. Misalignment between tape and playback head causes horizontal displacement (tracking errors). Chroma subsampling and analog signal limitations cause colors to smear horizontally. Loss of horizontal sync signal causes lines to jump or tear. Harry Alisavakis AV Artifact Atlas
How the observer experiences the combined effects.
Objects on springs or pendulums oscillate following sine/cosine curves. Multiple frequencies with phase offsets create organic, non-repetitive motion. Degraded visuals trigger specific neural responses—the Ganzfeld effect amplifies neural noise when deprived of clear signals; top-down processing causes the brain to rely more on expectations. Combining subtle effects overwhelms normal perceptual processing, creating dreamlike states. NIH PMC Wikipedia
Technical implementation details.
Each blend mode has specific physics:
multiply: Subtractive. Black stays black, white disappears. Used for scanlines and slot mask (simulating gaps/shadows).screen: Additive. Black disappears, white stays white. Used for phosphor triads (simulating light emission).color: Affects hue/saturation only. Used for beam landing error tints.The leaderboard runs a ~60fps animation loop via setInterval. Effect functions take the current time and return CSS properties—text-shadow, transform, filter—which are applied inline. Static data and styles live outside the React component to prevent recreation on each render. Full-window overlay divs handle screen-wide effects like scanlines, phosphors, and flicker.
These happen before light is emitted—affecting beam energy, alignment, and where electrons land.
Applied to the entire board container as transform: scale() and filter: brightness(). Tied to the same glow pulse as text effects so brighter content causes more sag. A slower secondary oscillation adds the irregular "hunting" of a real power supply.
const getVoltageSag = (time) => {
const bloomPulse = Math.sin(time * 3);
const slowDrift = Math.sin(time * 0.15) * 0.3;
const sagLevel = 0.65 + bloomPulse * 0.35 + slowDrift * 0.1;
return {
transform: `scale(${1 + sagLevel * 0.0015})`,
filter: `brightness(${1 - sagLevel * 0.008})`
};
};
Returns per-channel RGB offsets that get applied as additional text-shadow layers. Each text element passes its normalized screen position (-1 to 1), so elements near edges and corners get more separation. Green stays centered as reference; red and blue drift opposite directions.
const getConvergenceError = (time, normalizedX, normalizedY) => {
const edgeFactor = Math.sqrt(normX² + normY²);
const thermalDrift = Math.sin(time * 0.0275) * 0.825;
return {
r: { x: staticR.x + dynamicR.x + thermalDrift, y: ... },
g: { x: 0, y: 0 },
b: { x: staticB.x + dynamicB.x - thermalDrift, y: ... }
};
};
A full-screen overlay with radial gradients at each corner (purple and yellow tints) plus a center gradient that shifts with thermal drift. Applied with mix-blend-mode: color so it tints without affecting luminance.
These simulate light emission and decay after electrons hit the screen.
Implemented as layered text-shadow on all text elements. White core, orange bloom layers at increasing radii. Spread values pulse with Math.sin(time * 3). Chromatic aberration shadows (red left, cyan right) are added to the same text-shadow string.
A full-screen black overlay with opacity modulated by an exponential decay curve. The decay resets 60 times per second, matching CRT refresh. Low persistence (0.4) makes the flicker visible; intensity is kept to 3% to avoid distraction.
const getCRTFlicker = (time) => {
const cyclePosition = (time * 60) % 1;
const decay = Math.pow(0.4, cyclePosition * 8);
return { brightness: 1 - (1 - decay) * 0.03 };
};
Physical patterns from the shadow mask and phosphor layout.
CSS repeating gradients create horizontal gaps (scanlines) and vertical gaps (slot mask). Applied with multiply blend mode so they darken without affecting colors.
Vertical red-green-blue stripes at 2px repeat. Applied with screen blend mode to simulate additive light. Opacity pulses inversely with glow—brighter bloom fills the gaps, making triads less visible.
A static repeating gradient that dims every other line. We tested animated field alternation but it created unwanted jitter at our frame rate.
Artifacts from camera recording and the observer's state.
A horizontal gradient band that slowly drifts up and down the screen, simulating the bright bar visible when filming a CRT.
Applied per-row as transform: translateX(). Deterministic timing based on row index creates the appearance of tracking errors without true randomness.
Per-row translation and rotation using sine waves at different frequencies (1.2, 0.8, 0.6 Hz) with phase offsets based on row index. Creates organic, non-repetitive motion.
Full-screen overlays are layered by z-index. Blend modes determine how each interacts with content below.
| z-index | Overlay | Blend Mode |
|---|---|---|
| 10 | Scanlines + Slot Mask | multiply |
| 10 | Interlace | multiply |
| 11 | RGB Phosphor Triads | screen |
| 11 | VHS Tracking Bar | normal |
| 12 | Beam Landing Error | color |
| 12 | CRT Flicker | multiply |
Most values went through several rounds of adjustment. The process: set to extreme to see what the effect actually does, then dial back until it's felt but not seen.