VOLTAreference

Reference / Work in Studio

Skin control faces

Work in Studio

Open in the workbench →
On this page · 5 sections

Draw your own knob, fader, display and meter faces in a skin. The faces change how a control looks, never how it behaves.

Faces and scopes#

faces defines faces by ID; controls binds a face to a scope. Both are schema 2 and may also appear in modes.<mode>. Both inherit through extends from parent user skins, face ID by face ID and scope by scope: the nearest declaration wins, and an inherited face's image layers use its declaring skin's assets.

"faces": {
  "calm": { "kind": "knob", "layers": [
    { "arc": { "r": 0.38, "from": "centre", "to": "value" }, "stroke": [0.11, "accent"] },
    { "circle": [0.5, 0.5, 0.27], "fill": { "select": "hover", "on": "control-hover", "off": "control" } },
    { "path": [[0.5, 0.5], [0.5, 0.24]], "stroke": [0.07, "ink"],
      "rotate": { "value": [-135, 135] }, "about": [0.5, 0.5] }
  ] }
},
"controls": { "knob": "calm",
              "mixer.knob": { "face": "calm", "required": true },
              "mixer.fader": { "face": "console", "fallback": "slot" } }

A scope is <kind> (knob, fader, display, meter) or <pane>.<kind>, where the pane is a workspace role: mixer, macros, rack, source, timeline, sounds, scopes, commands. The mixer's pan pots and faders use mixer; the workspace's Macros pane uses macros (the song outline's macro rows are bars, not faces). Precedence runs from the built-in recipe, through skin and mode, to the pane scope; the face's own state mappings come last.

display and meter are the Rack case's live module faces (Patch Bay and Rack § Live faces), drawn in the rack pane:

  • A display is a module's small window: the envelope, LFO, filter or oscillator screen. The face is its backdrop. Its value is the live mark's position across the window (the voice's point on its envelope, the LFO's phase, the cutoff). active is on while the engine feeds the window, and disabled while it does not.
  • A meter is a module's level meter: a VCA, the out panel or a track's strip. Its value is the level on the meter's scale (−60 to +6 dB).

Neither kind has named recipes, so its only fallback is "built-in": the screen backdrop, or the theme's meter recipe.

A binding is optional unless "required": true. A missing, unsupported or invalid optional face paints its fallback with a warning: "built-in" (the default, which is the theme's recipe) or a recipe name (arc, dial, dot, notch; cap, slot, bar). A required face that cannot be drawn refuses the skin. button is reserved for a later build: it falls back, or refuses the skin when required.

Paint IR#

Coordinates are normalized to the face box. For a knob, that is the centred square inside the host's 4.5 pt rim. For a fader, x spans the column beyond a 4 pt witness band, and y spans exactly the value's travel. Lengths scale with the box's shorter side.

PrimitiveForm
rect[x0, y0, x1, y1], radius makes it a rounded rect
circle[cx, cy, r]
ellipse[cx, cy, rx, ry]
arc{ c, r, from, to } along the 270° sweep (0 at 7:30, 1 at 4:30); stroke only
ticks{ c, r: [inner, outer], count, from, to }, evenly spaced marks; stroke only
path[[x, y], …], closed; filled only when closed and convex
imagea skin asset ID, at: [x0, y0, x1, y1], crop: [x, y, w, h] in texels
group[layers], with an optional rect clip

A fill is a colour, { "linear": { from, to, stops } }, { "radial": { c, r, stops } } with 2–4 stops, or { "material": "<name>", "fallback": <colour> }: the skin's named material or a built-in (material::lookup), its baked tile mapped one texel per point from the face box; an unknown name paints the fallback. A stroke is [width, colour]. Colours are #rgb, #rrggbb or #rrggbbaa, or accent (the control's own colour, such as a track colour), or a theme role: ink, mid, faint, pri, sec, acc, alert, operational, brt, screen, screen-lo, bezel, rule, track, control, control-hover, readout or transparent. Roles resolve against the solved theme once.

Any layer may carry opacity, rotate (degrees, about about), translate, scale and when ("hover", "!disabled", …).

Bounds, checked at compile time: 16 faces per skin, 96 drawn primitives per face (each tick counts), 32 points per path, groups 4 deep, 4 stops and 48 ticks.

State#

A face reads only this state: value (0–1), centre (0.5 for a bipolar control, else 0), hover, pressed, active, disabled and focus. A number may be literal, an input ("value"), or a clamped linear map: { "value": [-135, 135] } or { "map": "value", "in": [0.5, 1], "out": [0, 1] }. A colour may be { "select": "hover", "on": …, "off": … } or { "mix": "value", "from": …, "to": … }. Faces have no loops, expressions, shaders, event handlers or engine calls.

What the host keeps#

Over every face the host paints the value witness: a rim arc for a knob, or a leading-edge bar for a fader, with detents, the ridden band and the live modulated value. Over a display it paints the module's trace, cursor and live mark. Over a meter it paints a level bar on the leading edge. It also paints the focus ring (a ring on a knob, an outline on a fader, under every recipe and face), a veil when the control is disabled, and an alert ring. So the face's art is never the only sign of a control's value.

Labels, numeric readouts and units stay the control's own widgets. Hit testing, drag (valuefeel::control), wheel, arrow keys, typed entry, double-click reset and Undo belong to the control adapter, so they are identical under every face. A face adds no accessibility node.

Right-click on a mixer pan pot opens a typed entry. Type L30, R100, C or a signed percent, then press Enter. The entry works under any face.

Packs#

A skin that uses faces declares the faces capability in its format-2 manifest. Face images carry the role face. Export writes inherited faces and scopes into the child's modes and bundles their images under stable <parent>--<id> IDs, so the child installs without its parent's faces. Export v1 drops faces and controls and lists what it dropped. Decoration and face images share one GPU cache and one residency budget.

To review a face, Display ▸ Appearance inspector shows each bound scope at rest, hover, pressed, focus and disabled. The MCP preview_skin tool returns the same strip as an image (Designing a workbench pack).