Internal · SpaceMusic Design
The Mobile Control Surface
520 controls, eight pages, four levels of hierarchy — and one thumb. The row is the control.
The shape of the problem
SpaceMusic is a real-time visual engine with a control surface to match: eight pages, 520 controls, 1 395 channels. On a desktop that is a sidebar and a wide window. On a phone it is 390 by 844 points, and most of them are already spoken for.
Subtract the two bars and 732 px of body is left. A 200 px preview and two navigation strips take it to about 444 px of list — roughly eight rows. Every page has to resolve into that, and the pages are not evenly shaped: Time carries three controls, Mapping carries 222 across four levels of nesting.
The visual language
Monochrome, flat, dense with information but never busy. Geist for names, Geist Mono for anything technical — values, section titles, paths. Dark first. Lines only where they are structural.
Eleven tokens, no accent colour, and a light theme by swapping their values. The two bars carry --chrome, one step off the page background, because they never move and should not travel with the scroll.
The row is the control
No boxes, no separate widget on the right of a label. The control is the row: its width is the range, its fill is the value, and you drag it where it sits. Nothing has to be opened first.
Centered and not centered
The distinction the earlier drafts missed. A range that starts at its minimum — Gain from 0 to 1 — fills from the left edge, because that is where the value actually starts. A range built around zero — X Translate from −10 to 10 — fills out of the zero tick, left or right.
That matters more than it sounds. Filling every slider from the left made −4.800 look like a positive quarter-turn. Now a negative value reads as negative, and a value of exactly zero draws no fill at all — just the cursor sitting on the tick.
Four kinds, one rule
Only the two kinds that have a value to fill ever carry white. A slider fills to its position in range; a toggle that is on fills completely. A dropdown has no quantity to show, so it stays plain and the minus / plus cells at the right edge do the work — the minus dimmed on the first option, the plus on the last. A button has nothing to fill either, so the row stays empty and only its edge cell is live, which is also what stops a stray drag from firing it.
An earlier pass did fill the dropdown to its position in the list. It read as a slider, and on the last option it read as a toggle that was on. Removing it cost nothing and settled both.
The screen grammar
Five bands, in the same order, on every page. The top bar is identity and the bottom bar is position — which is why the page name is stated once, in the bottom breadcrumb, and never repeated at the top.
Figure 1 · Screen anatomy and the four control kinds Open full size · print A3 landscape ↗
Both bars are 56 px, so the chrome is a constant 112 and the body is whatever is left. A control row is 56 px too, 58 with its gap, so seven or eight of them fill the list. The preview is the slack: a short page opens it wide, a dense one shrinks it to 104.
No strip ever holds more than four word tabs, a rule that turned out to be already true in the data: Mapping’s Layer level has exactly eight sections, which lands as a 4×2 grid, and Material, Position, Rotation, Scale and Pivot each carry exactly four groups. Number chips are the one exception; those may run to eight across.
Two strips, then dividers
A page can nest four levels deep, and the tempting move is a strip per level. Three strips eat 132 px and the list has none to spare.
- At most two strips. One for the zone, one for which instance. Nothing deeper ever becomes a strip. Input → 1D → plugin 2
- Everything below that is a collapsible divider in the scroll. Strips are for levels you switch between constantly; dividers are for levels you open, work in, and close. Two dividers can also be open at once, which a strip can never do. Files · Parameters · Options
- A strip exists only when its level has two or more items. The ladder shrinks to fit the page rather than being padded out to a fixed shape. Time shows no strip at all
What a hold opens
The list is deliberately quiet. Precision arrives only when you ask for it, and it arrives as a sheet that rises over the page rather than a screen you navigate to. The strips and the preview stay exactly where they were, dimmed, and the bottom bar sits on top of the scrim so the breadcrumb stays readable.
Why this shape holds
Everything above reduces to four rules that compose rather than compete: two bars that never move, at most two strips, one full-bleed row per control, and the fill is the value wherever there is a value to fill. A page is then fully described by which zone it has, whether it has instances, and what its dividers are called. Nothing about a new page needs a new decision.
That matters because the pages are not authored here — they are generated from the DNA, and they change. A surface built out of four rules absorbs a new section or a renamed group without a redesign.
Two things this design does not yet solve, stated plainly. Render Mode carries 43 flat controls with no level to split on; that is several screens of scrolling and no navigation scheme fixes it — it probably wants splitting in the DNA. And the render parameter names in these mockups are invented, because the real ones arrive with whichever render mode is loaded at runtime.
Settled
- Two 56 px bars, top and bottom
- At most two navigation strips
- One full-bleed 56 px row per control
- Centered and unipolar sliders drawn apart
- Only sliders and toggles carry fill
- Hold for the sheet, tap a long list for its own
Next
- Wire the grammar into /mobile
- Safe-area inset on the top bar
- Real render-mode parameter names
- Tune the long-press delay on a real phone
Later
- Split Render Mode in the DNA
- Swipe to step a dropdown
- Drag to resize the preview
Glossary
- DNA
- The authoring platform at dna.spacemusic.tv that owns the parameter tree. Pages, sections, groups and controls are exported from it — not hand-written in the UI.
- Pro UI
- The full desktop control surface. The mobile surface is a second view of the same channels, not a cut-down copy of the Pro UI.
- zone
- The first level under a page. Becomes the top strip. Input’s zones are IO, 1D, 2D, 3D.
- instance
- A dynamic, numbered item inside a zone — plugin 2, stage 1, layer 3. Becomes the second strip, as number chips.
- divider
- A collapsible section header in the scrolling list. Every level below the instance is one of these.
- strip
- A full-width row of tabs or chips directly under the preview. A page has at most two.
- channel
- The engine-side value a control is bound to. The UI never holds state of its own; it reads and writes channels.
- centered
- A slider whose range is built around zero, so its fill grows out of the middle. The opposite is a range that starts at its minimum and fills from the left.
- sheet
- An overlay that rises from the bottom edge over a dimmed page. Used for slider precision, long option lists and the menu.
- preview
- The live render at the top of the page. Cycles closed, partial and full screen from the eye icon in the bottom bar.
- render mode
- The engine plug-in that draws a mapping layer. It supplies its own parameter names at runtime, which is why the ones drawn here are placeholders.