SQUINT halves the render to approximate doubling the viewing distance — an approximation only
(a CSS pixel is not a device point on your monitor). GRAYSCALE proves nothing here is hue-only.
LEADEVOLVED · PERFORMANCEthree bands: section plate / approach gutter / form ribbon
EVOLVED · EDITsame tree, edit density — you edit 7 sections, not 24 bars, and the clock never stops
The clock does not stop to let you edit. The playhead is live on the same tick as the panel
beside it — edit section 4 while the music is in section 2 and the foot reports both.
SELECTION is a ring, NOW is a playhead: two marks, never merged. Tap a block to select;
the grid below shows only that section’s bars, at 58 pt — above the 44 pt
touch floor the shipped 26 pt pill misses.
THREE BANDS · THREE DISTANCES
EACH SCALE OWNED EXACTLY ONCE
BAND 1SECTION PLATE
What am I playing, how deep in. Section name 46 px Bebas
(cap 32.2 pt ≈ 17.2 arcmin @1 m — above R1’s 16-arcmin floor,
below the 20–22 band the BPM numeral owns). Bar-of-section counter 44 px Bebas
— lifted from 40: at 40 it sat at 15.0′, under the 16′ minimum R1 §0 sets
for a numeric position readout, on a page that cited that minimum.
The filament fills across the section, in ASH over a role-coloured trough —
the section burns down.
BAND 2APPROACH GUTTER
What is coming, and when. Two-stage telegraph, and in this pass it finally
reads: name 38 px Bebas (14.3′), numeral 44 px Bebas (16.5′).
v1 shipped 16 px Overpass ≈ 6.0′ — a warning nobody could read.
Geometry is constant between idle and armed; arming is a pure luminance onset
(measured below), which is R1 §5.2’s top-ranked peripheral channel and removes the
layout shift v1 pushed into the ribbon.
BAND 3FORM RIBBON
Where am I in the whole tune. Fits to width — never scrolls
(RULED, SYNTHESIS §3.3; D3 §6.1’s fork is closed), so the shape of the song is always
visible. Names engraved once per section block, not once per bar. Deliberate-look tier:
8.5 px labels are not claimed to be glanceable, and this card says so rather than
letting the reader assume otherwise.
SCALE LAWone scale, one element
beat → the dot lane (already LIT).
bar → the ribbon playhead steps.
section → the plate filament fills.
Nothing renders the same scale twice, so nothing competes for the glance.
ONE-GLOW LAW HELD. LIT #F0A040 appears in exactly two places on this screen:
the beat dot, and the BPM numeral at the instant a Song Map tempo actually changes
(the readout is alive). The playhead is ink #EAD8B0 and earns its salience by
moving — R1 §5.2 ranks motion as an independent peripheral channel, so it does not need
to be the brightest thing to be the most noticeable. Anticipation is never LIT — same call the
July warm-up mockup made.
THE TELEGRAPH · TWO STAGES
EXTENDS THE BANKED .APPROACHING CURVE — DOES NOT REPLACE IT
quiet ——————————
STAGE 1 text · STAGE 2 ramp · catch
STAGE 12 bars out
The gutter numeral lights and counts bars: IN 2 → IN 1.
A text channel — no new glow, no second light source. At 120 BPM 4/4 that is
4 seconds of warning; the July mockup’s single-bar ramp gave 2, which is a rhythm-game
budget, not a musician’s — you cannot set up a fill in two seconds.
STAGE 2final bar
The numeral drops to beat resolution — 4 · 3 · 2 · 1 — while the
banked curve runs verbatim underneath it:
opacity = 0.70 + 0.24·t², t = (currentBeat+1)/beatsPerBar,
beat-stepped, capped at 0.94, riding the gutter filament and the ribbon’s next block.
Two channels agreeing is motion-principle 1; it is not a second light source.
THE FIXre-based, not re-curved
With .future = 0.70 the curve’s beat-1 step is
1.02:1 — invisible. Re-basing .future → 0.45 makes the same curve
open at 1.43:1 and close at 1.80:1. The curve was right; its floor was too high.
RESTRAINTunchanged
Ordinary bar→bar transitions inside a section get nothing.
Triggers stay exactly as banked: section start, or isTempoChange(at:)
(SongMapPillTrack.swift:76-80). Loop wrap adds one: ↺ LOOP IN 1.
DOES THE WARNING READ? — MEASURED LIVE OFF THE RENDERED CSS
| element | size | cap | @1 m | @2 m | tier |
THE ARM IS A LUMINANCE ONSET, NOT A MOVE
WHY THE SIZES ARE THE FIX. R1 §0 files a numeric position readout under
16′ minimum / 20′ preferred and a secondary label under the
14–16′ automotive floor. v1’s gutter ran the name at 6.0′ and the
countdown at 11.3′ — both under their own category’s floor, while the page cited
those floors three cards away. Sizes here are the R1 conversion (163 pt/in, Bebas cap
ratio 0.70), measured from getComputedStyle at load, so the table cannot drift
from the stylesheet. Overpass Mono’s cap ratio is still the 0.70 working estimate —
R1 §3’s open item, flagged in every row that uses it.
STATE CHANNELS · MEASURED
WCAG RELATIVE LUMINANCE · EMBER GROUND #0C0C0E (Y=0.0037)
| claim | today | proposed |
| spread across the 23 section colours |
32.9× Y 0.0145 → 0.4777 |
5 roles hue-separated |
| a spent verse vs. the bar you are playing (outro) |
2.04× brighter 0.1287 vs 0.0630 |
impossible past = one ash token |
| .future → .playing step inside one hue |
1.33:1 |
n/a — NOW is position, not brightness |
| what carries “this bar is now” |
opacity + ring + ×1.06 scale + shadow (four decorations doing one job) |
the playhead sits on it + the plate names it |
| tap target, narrowest pill |
26 pt wide (minWidth :169) vs 44 pt HIG floor |
ribbon is display-only; editing is 58 pt tiles |
WHY 23 COLOURS CANNOT WORK. Hold every section at one luminance so that brightness is free
to mean state, and 20 distinct hues must share 360° — the computed worst neighbours land
0.3° apart (verse/one-drop) and 1.0° apart (chorus/trading-4s). Let luminance vary
to separate them and it swamps the state channel again, which is exactly what ships today.
The constraint is geometric, not aesthetic. So colour stops being the section’s identity
and becomes its ROLE; the NAME is the identity, at 46 px.
THE ROLE RAMP · 5, NOT 23
DEFERRED TO 1.2 (SYNTHESIS §3.4) — NEEDS A DEVICE PASS ON ≥2 PALETTES.
ASH SHIPS EARLIER, ON ITS OWN, WITH THE PLATE.
| role | sections | Y / CR |
| FRAME |
intro, outro, out head, rest | 0.115 / 3.07:1 |
| STATEMENT |
verse, head, theme, exposition, recap, one drop, riddim | 0.315 / 6.79:1 |
| LIFT |
pre-chorus, development, variation | 0.560 / 11.35:1 |
| PAYOFF |
chorus, hook, shout chorus | 0.330 / 7.07:1 |
| DEPARTURE |
bridge, solo, trading 4s, break, dub | 0.205 / 4.75:1 |
| ASH |
every spent bar, whatever its role | — |
ASH IS A PER-PALETTE TOKEN — COMPUTED LIVE, BOTH GROUNDS
| ground | ash | CR vs ground | vs 1.53 floor |
A CORRECTION, STATED LOUD. D3 §5 reports ASH #32323A at
1.58:1 on the Midnight ground and calls it “above the 1.53 floor.”
Recomputed here it is 1.41:1 — below the floor
(#32323A Y 0.0325, Midnight ground Y 0.0088; the Ember figure in the same
paragraph reproduces exactly, so the formula agrees and the Midnight number does not).
The fix is not a new colour family: Midnight gets #2C3A58, lifted within the
slate hue until it clears — precisely the move Theme.swift:255-256 already makes
for Midnight’s dotNorm/borderDot. D3 §5 predicted “ASH is
likely a third [Midnight deviation]”; this is it, measured.
Worth noting and not over-reading: a correctly lifted Midnight ash measures
1.58:1 — the exact figure D3 §5 prints beside the wrong hex, which reads like a
colour that got separated from its measurement. That is a guess at the provenance,
not a finding.
ASH’s rule, so the next palette derives it instead of guessing: sit at the F6
visibility floor (1.53:1, Theme.swift:120-121) — a spent bar is exactly as loud as an
unlit beat dot, never louder. On Ember that lands within a rounding step of the shipped
dotNorm #34322E.
Separation, CIE76 ΔE across the five roles: normal 33.5 · protanopia 22.9 ·
deuteranopia 16.8 · tritanopia 5.6 (LIFT/PAYOFF — the one weak pair; they differ
by ΔL* 15.4 so grayscale still splits them, and the block name is always engraved beside them).
The gold→hot-orange LIFT→PAYOFF pair is the ember DNA doing the semantic work:
the tune literally heats up into the chorus.
DENSITY LADDER · THE RIBBON UNDER STRESS
FIT-TO-WIDTH IS RULED (SYNTHESIS §3.3) — IT DEGRADES; A SCROLLER JUST CLIPS.
EVERY CASE IS SHOWN PART-PLAYED, SO THE ASH TOKEN IS TESTED AT EVERY DENSITY.
Rules, in order — and there are now four, not three: below 6 px per bar the
per-bar rules are dropped and only section blocks survive; below 34 px per block the
engraved name elides to its initial; if the full name would be wider than its block
it falls back to the 3-letter short form first; below 14 px the name is dropped
entirely and the block is colour-only. The width tier is new in this pass — v1 could hand a
10-character name (“PRE-CHORUS”) to a block that passed the 34 px test and clip it.
Nothing is ever clipped off-screen: the whole form is always present, which is the property a
horizontally scrolling strip structurally cannot have.
ASH SURVIVES THE SQUEEZE. Each ladder row is played to roughly a third, so the spent
run renders in the live --ash token even at ~4.5 px per bar — the case
where per-bar rules are gone and only the ash/role boundary and the playhead say where you are.
Toggle GRAYSCALE: the boundary holds without hue, which is the whole point of the token.
UNMAPPED IS NOT GREY MUSH. A fresh map is 8 bars of .empty
(SongMapModel.swift:23) and renders today as eight identical blank chiclets — a poor first
impression of the flagship practice surface. Unmapped renders instead as an honest amber
bar ruler: numbered, playable, obviously un-authored.