Internal · SpaceMusic Design

The Mobile Control Surface

520 controls, eight pages, four levels of hierarchy — and one thumb. This is the shape we landed on, and the rules that got us there.

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 you have 732 px of body. Show a preview at 200 px and you have 532. Add the two navigation strips this design ends up needing and you have 444 px of list — about eight rows. Every page in the product has to resolve into that, and the pages are not evenly shaped: Time carries three controls, Mapping carries 222 across four levels of nesting.

Time · 3 controls
Time · 3 controlsThe shallowest page in the product. No strip at all, so the preview can stay open and the whole page still fits above the fold.
Mapping · 222 controls
Mapping · 222 controlsThe deepest. Layer, then one of eight sections, then a group, then the control — four levels, all of which have to be reachable by thumb.

The hard part was never the controls. It was the organisation — getting from a four-level tree to two fixed rows the thumb can learn once.

The visual language

The reference is creativeapplications.net: 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.

bg#0b0b0c chrome#141418 track#141417 line2#35353c ink3#5d5d66 ink2#96969d ink#ececea fill#dcdcd9

Eleven tokens, no accent colour. An earlier pass tried a second, lighter grey to mark changed-from-default values and it read as noise, so the palette went back to one fill. The two bars carry --chrome, one step off the page background, because they never move and should not travel with the scroll.

The control kit
The control kitEvery kind the engine can emit, drawn once: slider, dropdown, radio, toggle, button, multi-level dropdown, edit field, file loader, thumbnail, label, and the dashed box an unsupported control falls back to.
The same page, light
The same page, lightOne theme switch flips eleven tokens. Nothing else in the markup changes, and the fill-is-the-value rule survives the inversion.
Full-bleed variant
Full-bleed variantAn earlier draft where the control was the row. Legible, but a slider and a dropdown stopped looking like relatives, which is why the boxed frame won.

One frame for every control

The first drafts styled each control kind for itself: a thin line-and-dot slider, a text dropdown, a pill toggle. Mixed into one list they stopped looking related, and the sliders in particular vanished between their neighbours.

Everything now lands in the same box — 196 by 40 px, hard against the right edge of the row. What happens inside the box differs by kind, but the outline, the height and the right edge never move. One rule holds the family together: the fill is the value. Position in a range for a slider, position in the list for a dropdown, the whole box for a toggle that is on.

The number sits on top of the fill in two stacked layers, the upper one clipped to exactly the fill width, so it flips from light to dark as the fill passes underneath. No outline, no shadow, and it stays legible at both ends of the travel.

Step, don’t open
Step, don’t openMinus and plus walk the options with no list at all. The 2 px line under the value says how far through you are, without a number.
When the list is long
When the list is longSix options or fewer step in place. More than six — or a list the engine builds at runtime, like MIDI devices — opens this instead, with a filter.

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 shared control frame Open full size · print A3 landscape ↗

THE SCREENPREVIEWZONEINSTANCEDIVIDERTop bar · 56 pxlogo · SPACE MUSIC · free slotPreviewclosed / partial / full — the eye cycles itStrips — at most twowhich zone, and which instanceListcollapsible dividers, then the controlsBottom bar · 56 pxpreview · PAGE / sub-path · menuONE FRAME, FOUR KINDS — 196 × 40, DRAWN 1:1Slider0.620DropdownMediumToggleOFFONButtonThe outline, the height and the right edge never move,so a slider no longer disappears between two dropdowns.THE FILL IS ALWAYS THE VALUEPosition in range, position in the list, or on / off.The number sits on top and inverts as the fill passes under it.

Two numbers do most of the work. Both bars are 56 px, so the chrome is a constant 112 and the body is whatever is left. And 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.

The load-bearing decision. 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.

  1. At most two strips. One for the zone, one for which instance. Nothing deeper ever becomes a strip. Input → 1D → plugin 2
  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
  3. 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

Below is the same two rows on five pages of very different shape. Only the words change.

One strip · Output
One strip · OutputThree zones, so a strip of four cells with the fourth left empty rather than stretching three across the width. The grid stays a grid.
Uneven zones · Environment
Uneven zones · EnvironmentLights and Stages each carry instances; Camera carries none. So the strip stack is per zone, not per page — picking Camera drops the instance strip entirely.
Orphan controls · Placement
Orphan controls · PlacementAdd and Remove Placement belong to no zone. They get a MAIN tab rather than floating above the strip, so the strip is always the first thing under the preview.
Two strips · Input
Two strips · InputZone, then which of six plugins. Files, Parameters and Options lost their own strip and became dividers — you open those, you don’t switch between them.
Environment · Stages
Environment · StagesIdentical components in the identical order as Input. Only the words change — the thumb learns two fixed rows once and they behave the same on every page.
Placement · instances
Placement · instancesThis one had three strips. Files and Options carry one and two controls, so tabbing to them was silly. Now the whole structure of a placement fits without scrolling.
The ladder shrinks to fit
The ladder shrinks to fitRender Mode is flat, so there is nothing below the section strip at all. The parameter names are invented — the real ones arrive with whichever render mode is loaded.
Preview, full screen
Preview, full screenEverything except the two bars goes away. The bottom bar keeps the breadcrumb, so you still know where you land when you shrink it back.

Section spacing carries the rest of the hierarchy. Navigation dividers sit 24 px apart so a page’s whole structure fits on one screen; plain reading sections get 48.

Section spacing, sheet closed
Section spacing, sheet closedEvery separator title is the same grey, section and group alike. Size, gap and rule weight carry the hierarchy instead of colour: a section gets 48 px of empty ground and a rule above and below its header, a group gets 22 px and one rule.

What a tap opens

The list is deliberately quiet — a name, a frame, a value. 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, so you never lose your place.

The slider sheet
The slider sheetThree things: the chunky bar with the number inverting through it, then Min, Max and Reset. The page behind stays where it was, dimmed, so you never lose your place.
The dropdown sheet
The dropdown sheetPosition is stated once in the header as 04 / 06 rather than numbering every row. The bottom bar sits on top of the scrim and stays readable.

The slider sheet is down to three things: the bar, then Min, Max and Reset. There is no path, no default read-out and no fine/coarse switch — precision comes from how far your thumb sits from the bar. Dropping those shrank the sheet by 160 px, which the list got back.

The corner menu
The corner menuWhat the icon sidebar became. Full width, on the same 46 px row height as the dropdown sheet, so both sheets share one rhythm.
Direction C · index and sheet
Direction C · index and sheetThe direction that won. A quiet index of rows, and precision arrives only when you ask for it. Every screen above is built on it.

The directions we dropped

Three directions were drawn. C won on density; the other two are here because the reasons they lost are the reasons C is shaped the way it is.

A · drag in place
A · drag in placeThe slider is the row, so you drag it where it sits and hold it for the sheet. Fast, but the rows are 64 px tall — a 43-control page becomes four screens.
B · console
B · consoleA dense two-column read-out with the range printed on every row. Exactly the busyness we were trying to lose. Kept for reference, not carried forward.

One idea from the winning navigation scheme was overridden too. It proposed a fifth strip carrying ON / LOCK / MUTE / SOLO, inverted like the other strips. Three independent reviews killed it for the same reason: inversion would then mean two different things one hairline apart — you are here and this is on. Those are ordinary toggles in the list now.

Why this shape holds

Everything above reduces to four rules, and they compose rather than compete: two bars that never move, at most two strips, one frame for every control, and the fill is always the value. 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 more than it sounds, 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. A surface built out of per-page layouts does not.

Two things this design does not yet solve, stated plainly. Render Mode carries 43 flat controls with no level to split on; that is three 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 196×40 frame per control
  • Sheets for precision, not screens
  • Dark first, light theme by token swap

Next

  • Wire the grammar into /mobile
  • Safe-area inset on the top bar
  • Real render-mode parameter names
  • Long-press vs tap on a slider row

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.
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.
SpaceMusic · Mobile control surface Designed for A3 landscape print ← back to the document

Figure 1 · Screen anatomy and the shared control frame

THE SCREENPREVIEWZONEINSTANCEDIVIDERTop bar · 56 pxlogo · SPACE MUSIC · free slotPreviewclosed / partial / full — the eye cycles itStrips — at most twowhich zone, and which instanceListcollapsible dividers, then the controlsBottom bar · 56 pxpreview · PAGE / sub-path · menuONE FRAME, FOUR KINDS — 196 × 40, DRAWN 1:1Slider0.620DropdownMediumToggleOFFONButtonThe outline, the height and the right edge never move,so a slider no longer disappears between two dropdowns.THE FILL IS ALWAYS THE VALUEPosition in range, position in the list, or on / off.The number sits on top and inverts as the fill passes under it.