Internal · SpaceMusic Design

The Mobile Control Surface

520 controls, eight pages, four levels of hierarchy — and one thumb. One number sets every height on the screen.

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 746 px of body is left. A preview and two navigation strips take it to about eight rows of list. 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.

Time · three controls, darkTime · three controls, light
Time · three controlsThe shallowest page in the product. Nothing to switch between, so there is no strip and the preview takes six units instead of three. The list never stretches to fill a screen — the preview does.
Mapping · 222 controls, darkMapping · 222 controls, light
Mapping · 222 controlsThe deepest. Layers as chips, sections as a labelled 4×2 grid, groups as dividers. Still only two strips. Source · Channel · Mapping resolves into three indented rows.

One number defines the screen

Every height on the screen comes out of a single figure: the row unit is the UI width divided by --div. At the default of 8 on a 390 pt phone that is 48.75 px, and because the divisor also sets the column count the grid is square and eight columns wide.

The top bar, the bottom bar, every strip, every section header and every control row are exactly one unit tall. A half unit — the subgrid — carries the gap above a section, the label inside a strip, and the segmented controls in the settings panel.

Figure 1 · One number, every height Open full size · print A3 landscape ↗

EVERY HEIGHT IN UNITSTOP BARPREVIEWZONESLAYERSSECTIONBOTTOM BAR1u3u · preview2u · strips8u · list1u390 ÷ 8 = 48.75 pxsquare grid, eight columnsWORD TABS — TWO COLUMNS EACH, NEVER MORE THAN FOURRENDERSTARTOBJECTTRANSFORMFour tabs land on the same ruler as eight chips.NUMBER CHIPS — ONE COLUMN EACH123456+A digit needs one column; a word needs two.THE ONLY NUMBER YOU CHANGE÷ 665.0 px÷ 755.7 px÷ 848.75 px÷ 943.3 px÷ 1039.0 pxSet it on any artboard and the whole screen re-flows.Nothing else has to be touched — no board carries ahard-coded height anywhere.SUBGRIDHalf a unit: the gap above a section, the label insidea strip, and the segmented controls in the settings panel.

The divisor is a live property on every artboard, not a number baked into the boards. If 48.75 turns out wrong on an iPad, change 8 to 7 or 9 and the whole screen re-flows; nothing else has to be touched, because nothing carries a hard-coded height.

The overlay is a switch in the same place. It draws the grid over the design so an alignment error is visible rather than argued about.

The grid, overlay on, darkThe grid, overlay on, light
The grid, overlay onRed hairlines every 48.75 px in both directions. Nothing floats between lines: both bars, both strips, the section header and all four control rows land on it exactly. The overlay is a switch in the styling panel, not a separate build.

Six levels of type

Six hierarchy levels, the way HTML numbers them. Each level owns a family, a size, a weight and a case, and nothing in the interface picks type for itself — a section header is H4 because it is a section header, not because 11 px looked right there.

  1. H1 — page title. sans 22 / 500
  2. H2 — tile and instance names. sans 15 / 500
  3. H3 — control name. sans 14 / 400
  4. H4 — section header. mono 11 / 500 · caps
  5. H5 — strip tab, breadcrumb. mono 9 / 500 · caps
  6. H6 — group label. mono 8 / 400 · caps

Two more sit outside the ramp because they are not headings. VALUE is mono 16 with tabular figures, so the digits do not jitter as you drag a slider. STATE is the On / Off word on a toggle, mono 9 caps.

Six levels, and two that are not headings, darkSix levels, and two that are not headings, light
Six levels, and two that are not headingsEach level owns a family, a size, a weight and a case. Nothing in the UI picks type for itself. VALUE and STATE sit outside the ramp because they are not headings — VALUE uses tabular figures so digits do not jitter while you drag.

Every control treated the same

Slider, dropdown, toggle and button share a height, a type size, a padding and a tap target. Only what happens inside the row differs, and one rule governs that: the fill is the value, wherever there is a value to fill.

A slider fills to its position in range. A toggle that is on fills completely. A dropdown has no quantity to show, so it carries no fill at all and the minus / plus cells at the right edge do the work. A button has nothing to fill either, so its row stays empty and only its edge cell is live — which is also what stops a stray drag from firing it.

Centered and not centered

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. A value of exactly zero draws no fill at all, just the cursor sitting on the tick.

Every kind, one unit tall, darkEvery kind, one unit tall, light
Every kind, one unit tallThe equal-treatment rule made literal. Slider, dropdown, toggle and button share a height, a type size, a padding and a tap target. Only what happens inside differs.

Colour, and the merge test

The bars invert against the page: black on a light ground, light on a dark one. That is the starting point, and every value behind it is a token, so it can be tuned without touching a board.

One rule constrains the tuning: no element may fall together visually with another. The case that forces it is a slider scrolled up under the bottom bar. In dark mode the bars sit lighter than any fill; in light mode they sit darker than any fill. The bar is always one step further out than the most extreme thing the list can draw, so the two can never dissolve into each other in either theme.

Fill against bar, in both themes, darkFill against bar, in both themes, light
Fill against bar, in both themesThe merge test. In dark the bars are lighter than any fill; in light they are darker than any fill. A slider scrolled under a bar can never dissolve into it, in either theme.

The two bars

Six cells, and every one of them now does something.

  1. Top left — the logo. It cycles the preview: closed, partial, full.
  2. Top centre — the wordmark. SPACE over MUSIC on two lines set to the same width, which is how the mark is drawn everywhere else.
  3. Top right — styling. Deliberately not inside the app’s own Settings page: these govern how the UI looks, not what it does.
  4. Bottom left — home. Back to the current page’s starting state.
  5. Bottom centre — the breadcrumb. Page name, then the path below it.
  6. Bottom right — the menu.

In full screen both bars go and only the logo stays, in the same cell it occupies in the top bar so it does not jump when you arrive. Tap it again to come back.

Full screen, darkFull screen, light
Full screenBoth bars go. Only the logo stays, in the same cell it occupies in the top bar, so it does not jump when you arrive. Tap it again to come back.

The page home

Every page’s starting state is an overview: the level under the page as four tiles filling the list, each with an icon. It is what the bottom-left button always returns to, so there is one reliable way back no matter how deep you are.

Where a page has instances the strip stays — you still pick a layer here — and the tiles fill the rest. For that to hold on Mapping, its eight sections regroup into four: Material folds into Object, and Position, Rotation, Scale and Pivot fold into Transform. That is a change in the DNA, not a trick in the UI.

The page home, darkThe page home, light
The page homeWhere the bottom-left button goes. The instance strip stays — you still pick a layer here — and the next level fills the rest as four tiles.

A page can nest four levels deep, and the tempting move is a strip per level. Two strips already cost two units; three would cost three, 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.
  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.
  3. A strip exists only when its level has two or more items. The ladder shrinks to fit the page.
  4. Every switcher carries a label. ZONES, LAYERS, PLUGINS — in the label band that takes the top 30% of the strip’s single unit, so the strip is still exactly one row on the grid.
One strip · Output, darkOne strip · Output, light
One strip · OutputButton, toggle and dropdown side by side, all the same height. The fourth strip cell is left empty rather than stretching three across the width.
Uneven zones · Environment, darkUneven zones · Environment, light
Uneven zones · EnvironmentCamera carries no instances, so the second strip is simply absent. The strip stack is per zone, not per page.
Loose controls · Placement, darkLoose controls · Placement, light
Loose controls · PlacementTwo orphan buttons under a MAIN tab, then two collapsed sections. The emptiest page in the product, and the preview grows to six units to say so.
Two strips · Input, darkTwo strips · Input, light
Two strips · InputZones then plugins, both labelled. Four unipolar sliders in a column: reading their fill widths as one shape is what this style does that a boxed row cannot.
Centered sliders in context, darkCentered sliders in context, light
Centered sliders in contextX = 2.400 grows right out of zero. Y = 0.000 draws no fill at all, just the cursor on the tick. Z = −4.800 grows left. Three values, one glance.
Instances · Placement, darkInstances · Placement, light
Instances · PlacementSame components, same order, different words. Zones strip, placements chips, dividers. Every switcher carries its label.

Top-down or bottom-up

Top-down is what every board above shows: page in the bottom bar, then the strips at the top of the list. Bottom-up moves the strips below the list and reverses them, so reading upward from the bottom bar you get page, then layer, then section — each level sitting directly above the one it belongs to, and all of it within thumb reach.

It is a setting, not a second design. Nothing else about the page changes.

Bottom-up, darkBottom-up, light
Bottom-upThe same page with the hierarchy switch flipped. The strips move below the list and reverse, so reading upward from the bottom bar gives page, then layer, then section — each level directly above the one it belongs to.

What a hold opens

The list stays 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 above the scrim so the breadcrumb stays readable.

The sheet, on hold, darkThe sheet, on hold, light
The sheet, on holdA short drag moves the value; a long press opens this. The header says HELD so the gesture is discoverable the first time. Bar, then Min, Max and Reset, each cell one unit.
The dropdown sheet, darkThe dropdown sheet, light
The dropdown sheetSix options or fewer step in the row. Longer, or any list the engine builds at runtime, opens this with a filter. Every option row is one unit.

Styling settings

The grid divisor, the overlay, the theme, whether the bars invert, the hierarchy direction, whether labels show, the type scale. All of it lives one tap from anywhere, behind the gear at the top right.

Keeping it out of the app’s own Settings page is deliberate. Settings is content — what the engine does. This is chrome — how the surface presents it. They change on different timescales and by different people.

Styling settings, darkStyling settings, light
Styling settingsOpened from the gear at the top right, deliberately apart from the app’s own Settings page because these govern how the UI looks, not what it does. Every row is one unit; the segmented control inside is 0.6 of one.

Why this shape holds

Six rules, and they compose rather than compete: one unit sets every height, at most two strips, one full-bleed row per control, the fill is the value, the bars invert and always sit beyond the fill, and everything carries a label. A page is then fully described by which zone it has, whether it has instances, and what its dividers are called.

That matters because the pages are not authored here — they are generated from the DNA, and they change. A surface built out of six rules absorbs a new section or a renamed group without a redesign. A surface built out of per-page layouts does not.

Three things this design does not yet settle, stated plainly. At a divisor of 8 the buttons inside a labelled strip are about 34 px tall, under Apple’s 44 pt guidance — a divisor of 6 or 7 fixes it, which is exactly the reason the divisor is a setting. Render Mode still carries 43 flat controls with no level to split on. And the render parameter names in these mockups are invented, because the real ones arrive with whichever render mode is loaded at runtime.

Settled

  • Row unit = width ÷ divisor, default 8
  • Grid overlay as a switch
  • Six type levels plus value and state
  • Bars invert, and never merge with a fill
  • Every control one unit tall
  • Every switcher labelled
  • Logo cycles the preview; home returns to the page overview

Next

  • Pick the divisor on a real phone and a real iPad
  • Regroup Mapping’s eight sections into four in the DNA
  • Safe-area inset on the top bar
  • Icons for every page’s home tiles

Later

  • Split Render Mode in the DNA
  • Swipe to step a dropdown
  • Per-level font pickers in the styling panel

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 it.
unit
The row height, and the width of one grid column. UI width divided by the divisor; 48.75 px at the default of 8 on a 390 pt phone.
divisor
The number the UI width is divided by to get the unit. A setting, not a constant.
subgrid
Half a unit. Used for the gap above a section and for the label band inside a strip.
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 labelled full-width row of tabs or chips. 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 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.
home
A page’s starting state: the level under the page shown as four tiles. The bottom-left button always returns here.
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 · One number, every height

EVERY HEIGHT IN UNITSTOP BARPREVIEWZONESLAYERSSECTIONBOTTOM BAR1u3u · preview2u · strips8u · list1u390 ÷ 8 = 48.75 pxsquare grid, eight columnsWORD TABS — TWO COLUMNS EACH, NEVER MORE THAN FOURRENDERSTARTOBJECTTRANSFORMFour tabs land on the same ruler as eight chips.NUMBER CHIPS — ONE COLUMN EACH123456+A digit needs one column; a word needs two.THE ONLY NUMBER YOU CHANGE÷ 665.0 px÷ 755.7 px÷ 848.75 px÷ 943.3 px÷ 1039.0 pxSet it on any artboard and the whole screen re-flows.Nothing else has to be touched — no board carries ahard-coded height anywhere.SUBGRIDHalf a unit: the gap above a section, the label insidea strip, and the segmented controls in the settings panel.