AFTERGLOW & PERSISTENCE
Nothing on either technology goes dark on the next refresh, and they do not go dark for the same reason. Persistence is eight separate phenomena — four of them pure CSS.
AFTERGLOW AND PERSISTENCE
A gas gap that stops being driven leaves excited and metastable
atoms behind, and that population keeps emitting while it relaxes and recombines — an
afterglow that is a property of the gas and of no coating at all. A phosphor is the opposite
case: the persistence is the coating, it is what the compound was chosen for, and it
ranges over four orders of magnitude between the parts on the board above. Most of the light
is gone early either way, then a tail whose length is the whole difference between them.
.ac-afterglow is that relaxation, and it is several separate things because
persistence is eight separate phenomena. Four of them are pure CSS, two need
JavaScript outright, and two arrive by both routes — and where JavaScript is needed it
is not being withheld, the thing is genuinely not expressible in a stylesheet. Nothing in the
cascade remembers the string a readout held one frame ago; scroll velocity cannot be
derived from a scroll timeline, which exposes position only; and
startViewTransition is the one API that hands you a picture of the screen you just
replaced without navigating away from it. Those three capabilities live in
amber-console.effects.js, which is optional and separate from
amber-console.js — the stylesheet alone still decays, de-energizes, throbs,
sweeps, and drains a whole page into the next one.
How long it lasts belongs to the emitter, not to this file. A palette that knows its own decay declares it, and every timing below is derived from that one number — so the panel does not have a persistence setting, it has whatever persistence the hardware in it had. Which is also why persistence and flicker are the same number read in two directions: a screen redraws every frame, and what is still lit when the next frame arrives is the smear while what is missing is the flicker. A screen cannot be steady and smear-free at once. P11 is fully dark between frames — no persistence, maximum flicker, which is why it was fitted to tubes meant to be photographed rather than watched. P39 is still at 91% — no flicker at any refresh rate, paid for in smear, which is why it went on radar. Both come off one decay constant and cannot disagree.
WHAT A LONG PHOSPHOR COSTS
Two consequences worth knowing before the switches surprise you. The long
phosphors are capped where you wait on them and uncapped where you do not:
P7's tail is three seconds, which is correct and would be an unusable dialog close, so anything
that has to actually leave is clamped to 250ms and the ghosts run the full three. And a
long-persistence phosphor does not decay exponentially — it follows a power law, a fast knee
over a very long tail, which no single easing curve expresses. Those palettes ship a
linear() sampled from the decay model itself.
P7 is the one that is worth turning on. It is two coatings rather than one: the beam writes into a blue layer that decays in microseconds, and that layer's own photons pump a yellow-green layer behind it that holds for seconds. So the ink is blue and the halo is green — text you are reading is being re-struck sixty times a second, and text that stops being written leaves only the glow behind. It is the only palette here whose halo is a different spectrum from its ink, which inverts a rule the rest of the system relies on, and it is the reason a radar sweep is still legible when the beam comes round again.
amber-console.effects.js parks a copy at the rect it occupied.
The copy is .ac-ghost, and it
is worth knowing the name: it is a real element in your DOM for the length of the tail, it
is aria-hidden and pointer-events: none, and it is the hook to
style or suppress if a ghost lands somewhere you do not want one.
.ac-ghost--fast is the same node on the short tail, used where a value rewrites
often enough that the full decay would stack copies on top of each other. Both are removed
when they finish; nothing accumulates.
<span class="ac-ghost"> <!-- mounted and removed by effects.js -->
.ac-ghost { display: none } /* suppress it in one region, without the module */.ac-blink stays steps(1) by default; with persistence on,
only the ON edge stays hard. Instant up, curve down.@view-transition with
navigation: auto — because it is a document-level switch, and linking a
stylesheet must not rewrite how your site navigates.
Within a page — effects.js. A tab switch or
a dialog replaces most of the screen without navigating anywhere, and CSS has no hook for
that. The effects module exports transition(fn), which wraps the change in a
view transition so the same decay runs over it; amber-console.js already calls
it for tabs and dialogs. This is the half that goes dark when JS Effects is switched off —
and the signature is in REFERENCE with the
other two exported functions.--ac-persist-tail, and the asymmetry needs no rule of its own: on a fall the
ghost is wider and its trailing strip drains, and on a rise it is
narrower, so it hides underneath and nothing fades in. Watch a load bar on the
server dashboard under P39.The eighth is the one place the two tiers do different
things rather than one filling a gap the other leaves, and the difference is real. A bar falling
gradually stops driving each cell at a slightly different moment, so the honest result
is a taper behind a receding edge — which is what the CSS draws, and what a drifting value
actually does. A bar that drops suddenly stops every vacated cell at once and should
fade uniformly instead. CSS cannot hold the old width, so it cannot tell those apart;
effects.js recovers the old geometry and parks a proper uniform ghost for that
case — which is also how the radar's A-scope and echoes get theirs, since it watches for any
lit area that shrank rather than for anything named.
.ac-blink, .ac-cursor, an invalid
.ac-input and an over-range .ac-meter--alarm bar all keep their own
cycle length and pick up the same curve. The de-energizing rules use the same trick as
decay-out: the transition is declared on the UNLIT half, so going out drains and coming
on is instant.<div class="ac-screen ac-afterglow"><span class="ac-persist"></span> … </div>
THE PPI SWEEP
The one widget where persistence is the whole thing, and the one
place a decaying trail is achievable in pure CSS. The obvious way to draw a radar wake is to
stamp a mark every frame and fade each one, which is a per-pixel framebuffer problem and needs
a canvas. But a PPI trail is not a history of marks — it is one continuous falloff behind a
rotating line, and a conic-gradient already is that falloff. Rotate it and the
decay comes for free, on the GPU, in one element, with no script at all.
The angular stops are the phosphor's decay curve read in degrees rather than
milliseconds, which is a substitution rather than a pun: the beam turns at a constant rate, so
angle behind the beam is time since that bearing was painted. Same curve, same
fast-knee-long-tail shape, different axis. The rotation period comes off
--ac-persist-tail, so the sweep returns about as the trail finishes draining —
which is precisely the trade P7 and P39 were bought to make.
--ac-halo-flash against
--ac-halo-1, the two coatings doing exactly what they did on the real tube. Every
other emitter has one coating, so its leading edge is simply the brighter end of its own
trail. The line itself is a constant-width arm rather than the first few degrees of the
gradient, which would be a wedge and would thin to nothing near the middle; the center
is bright because all 360 bearings cross it and it takes the whole revolution's
dose.<div class="ac-sweep" role="img" aria-label="Search radar"><span class="ac-sweep__beam"></span></div>
Assuming a removed node ghosts itself. The observer only sees text change;
a node that stops existing has no rect left to pin a ghost to, so ghost it
before you remove it — AmberConsoleEffects.afterglow(row), then
row.remove(). Retune the decay with the three tokens it is built from, none of
which is a duration you set directly — each is the palette's own
--ac-persist under a different cap. --ac-decay is capped at 250ms and
belongs to things that must actually leave; --ac-decay-fast is capped at 60ms and
belongs to de-energizing controls and hover release, because it fires on every control the
pointer crosses; --ac-persist-tail is uncapped and belongs to the ghosts and the
residual patches, which is where a long phosphor is allowed to be long. Rewritten text is its
own case at 400ms, enough to keep one generation legible and not two.
--ac-decay-ease is the curve.