Painting the glass and the tube
AquaTube does not display a skin drawn in Photoshop. The glass and the cathode-ray screen are computed every frame. Why re-embrace Frutiger Aero and the CRT TV in 2026, and what it changes.
An object, not an image
Old media-player skins were images. An artist drew a glossy shell in an editor, cut out the buttons, and the program pasted that image on screen. AquaTube does not work that way. The pod is not an image: it is a function, recomputed every frame by a shader.
That choice is not free. An object reacts: light slides across it as you move it, it shifts hue with the theme, it breathes with the sound. An image does none of that. For the pod to behave like an object made of glass and not like a sticker, it had to be described as an object: shapes, materials, a response to light. Here, skeuomorphism is not decoration added afterward. It is the rendering model itself.
Concrete consequence: the six shell themes are not six images, but six sets of five colors fed to the same shader. Adding a theme costs five colors, not an art board.
The glass
The pod's silhouette comes from a signed distance function (SDF): for every pixel, it says how far you are from the edge of the shape. Positive outside, negative inside, zero on the outline. That is what gives a crisp edge at any size, without aliasing, and a silhouette you can round, bevel and shade at will.
From that distance, the rest is simulating light on wet glass: a dome highlight up top, a rim light that lifts the shape off the background, a sheen, aqua bubbles rising in additive blend, and "gel" buttons with a specular hotspot, relief and drop shadow. Every effect answers the same question: how would light behave on this object, at this spot. That is what makes you read glass and not a flat colored fill.
fn sd_round_box(p: vec2<f32>, b: vec2<f32>, r: f32) -> f32 {
let q = abs(p) - b + r;
return min(max(q.x, q.y), 0.0) + length(max(q, vec2<f32>(0.0))) - r;
}The tube
The screen at the center of the pod is not a plain video area. It goes through a cathode-ray treatment. And the trap is to think it is decorative nostalgia. It is not. The CRT render recreates the physical flaws of an old tube: the curved glass, phosphor persistence, slight chromatic aberration, scanlines, vignette, grain. Flaws, precisely, that your eye used to forgive back then without even seeing them.
Phosphor persistence, for instance, keeps a fading trace of the previous frame. A single history texture is enough, blended with the current image by an attenuated maximum:
Curvature raises a problem real TVs had too: bulging the image pushes its edges out of frame (overscan). Rather than leave black bars or a stretched image at the corners, I curve then compensate, so the curved image fits the screen exactly at any curvature amount:
All of these are adjustable (curvature, scanline, aberration, vignette, grain), because a flaw pushed too hard stops being believable and becomes an Instagram filter. The default aberration ended up softened: too strong, it gave the effect away instead of selling it.
// phosphor persistence: attenuated trail from the previous frame
let out_rgb = mix(cur.rgb, max(cur.rgb, h.rgb * 0.88), scr_mask);
// simplified for illustration
fn curve(uv: vec2<f32>, amt: f32) -> vec2<f32> {
let c = uv * 2.0 - 1.0;
let off = c * dot(c, c) * amt;
return (c + c * off) / (1.0 + amt) * 0.5 + 0.5; // curve + compensate overscan
}The volume pass
A skeuomorphic object only holds up if it has relief. Flat, the pod becomes an image again. At one point it lacked volume: the shell felt too smooth, the controls too timid. I made a dedicated pass to give it body: a rounded shell (dome highlight plus form shadow), massive controls (bigger gel pebbles, better spaced, with drop shadows), thicker glass (a bevel around the rim, the screen sunk into the shell).
None of that is decided in code. It is judged on the image, one component at a time: push a parameter, capture, check whether the object gains presence or turns heavy. Relief is a matter of perception, not of values. Code only proposes; the capture decides.
What I take away
Procedural rendering is expensive to write. Describing glass as a response to light is far longer than painting a nice shell once. But that cost is an investment: once the object is described as an object, it declines almost for free (six themes in five colors), it reacts on its own (theme, sound, light), and it stays crisp at every size.
And skeuomorphism, taken seriously, is not nostalgia. It is a bet on body memory: we know how glass behaves, we know what an old TV does. Recreating those behaviors, flaws included, is speaking to that memory. A flat interface has to be learned; an object is recognized.