Getting Started
Load the script file after p5.js, set ControlStyle, create controls in setup(), and call .draw() in draw(). Mouse, wheel, and touch events are wired automatically — no event handlers needed.
HTML setup
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined&display=block" rel="stylesheet">
<script src="p5.min.js"></script>
<script src="https://cdn.jsdelivr.net/gh/dstein-art/proControls4p5js@master/ProControls.js"></script>
<script src="sketch.js"></script>
Minimal sketch
ControlStyle = 'black'; // set before creating controls
let vol, pan, pwr;
function setup() {
createCanvas(600, 300);
vol = new AnalogSlider({ x: 40, y: 50, label: 'VOL' });
pan = new Dial({ x: 120, y: 60, label: 'PAN' });
pwr = new AnalogSwitch({ x: 220, y: 80, label: 'PWR', states: ['OFF', 'ON'] });
}
function draw() {
proControlBackground(); // style-matched background
// Controls render themselves — no draw loop needed.
}
// No event handlers. No draw loop. Controls self-register and self-draw.
Rebuilding controls at runtime
When recreating all controls (e.g. on a style switch), call proControlReset() first to clear stale registrations. Read current values before rebuilding to preserve them.
function buildControls() {
proControlReset(); // clear old registrations
ControlStyle = currentStyle;
mySlider = new AnalogSlider({ value: mySlider?.value ?? 0.5 });
}
Removing a single control
mySlider.remove(); // deregisters from events; stop calling .draw() to remove visually
Common options (all controls)
Every control inherits these from the ProControl base class.
| Option | Type | Default | Description |
| x | number | 20 | Left edge of the control panel in canvas pixels. |
| y | number | 20 | Top edge of the control panel in canvas pixels. |
| min | number | 0 | Minimum value of the range. |
| max | number | 1 | Maximum value of the range. |
| value | number | min | Initial value. Writable at any time via ctrl.value = v. |
| label | string | '' | Text label drawn on the control panel. |
| scale | string | 'linear' | 'linear' or 'log'. Log requires min > 0. Affects dragging, scrolling, and tick labels. |
| disabled | boolean | false | Draws a translucent overlay and blocks all interaction. |
| onChange | function | null | Called with the new value whenever it changes during interaction. |
| onRelease | function | null | Called once with the final value on mouse/touch release. |
| theme | object | {} | Partial theme override merged on top of the active style. See Themes. |
AnalogSlider Input
A vertical fader with a draggable cap, optional scale ticks, and a live readout. Drag the cap or scroll the mouse wheel to change value. Double-click the cap to reset to the initial value.
new AnalogSlider(opts)
Options
| Option | Type | Default | Description |
| horizontal | boolean | false | When true, the fader runs left-to-right instead of bottom-to-top. Default width becomes 180 and default height becomes 44. showScale defaults to false in horizontal mode. |
| width | number | auto / 180 | Panel width. Vertical: auto-sized to fit tick labels when showScale is true. Horizontal: defaults to 180. |
| height | number | 180 / 44 | Panel height. Vertical default 180, horizontal default 44. |
| readout | string | 'raw' | 'raw' — numeric value · 'percent' — 0–100% · 'db' — dB relative to max (shows -∞ at min). |
| decimals | number | 2 | Decimal places for the 'raw' readout. |
| showScale | boolean | true / false | Draw tick marks and numeric labels along the track. Defaults true vertical, false horizontal. |
| style | string | 'knob' | Visual style — 'knob' (default), 'wheel' (cylinder with scrolling ribs), 'button' (minimal circular cap). Works for both vertical and horizontal orientations. See slider styles below. |
| showFader | boolean | true | Show or hide the draggable cap. false = display-only meter. |
| springBack | boolean | false | When true, the fader animates back to its initial value after mouse/touch release. |
| springDuration | number | 1.0 | Seconds for the spring animation to complete. |
| springDefault | number | value | Value to restore on double-click reset (and spring-back target). Defaults to the initial value. Override when the control is rebuilt dynamically to pin the reset point. |
Example
editable — changes update the preview
Log scale: Set scale: 'log' with a small positive min (e.g. 0.001) for a perceptually even frequency fader.
Slider styles
Set style on any vertical AnalogSlider to change its visual. Interaction is identical across all styles.
wheel — a cylinder visible through a slot. Ribs scroll with the value position; center notches mark the midpoint. Vertical: horizontal ribs, width defaults to 50px. Horizontal: vertical ribs, full slot is draggable. Scale defaults to off.
button — a minimal thin-groove track with a spherical circular cap. Works in both orientations. Width defaults to 40px and scale defaults to off.
MultiSlider Input
Several AnalogSlider faders arranged side-by-side in a single unit — useful for equalizers, mixer channel strips, or any bank of related faders. A single onChange/onRelease fires whenever any child slider changes, receiving a name → value object covering all sliders at once.
new MultiSlider(opts)
Options
| Option | Type | Default | Description |
| sliders | object | {} | Each key is a slider name/label. The value is either a number (initial value) or an object with per-slider overrides (value, min, max, readout, …). |
| x | number | 20 | Left edge of the first slider. |
| y | number | 20 | Top edge of all sliders. |
| height | number | 180 | Slider height in pixels (shared by all children unless overridden per-slider). |
| min | number | 0 | Shared minimum (overridable per-slider). |
| max | number | 1 | Shared maximum (overridable per-slider). |
| readout | string | 'raw' | Shared readout format — 'raw', 'db', or 'pct' (overridable per-slider). |
| decimals | number | 2 | Shared decimal places (overridable per-slider). |
| scale | string | 'linear' | Shared scale — 'linear' or 'log' (overridable per-slider). |
| horizontal | boolean | false | When true, each child is a horizontal slider and the group stacks them vertically. Default width becomes 180 and default height becomes 44 per slider. |
| gap | number | 4 | Pixel gap between adjacent sliders. |
| label | string | '' | Group label (not drawn — available for identification). |
| onChange | function | null | Called with a { key: value, … } object when any slider changes. Keys are slider labels with spaces and punctuation removed. |
| onRelease | function | null | Called with a { key: value, … } object on mouse/touch release. |
| style | string | 'knob' | Shared slider style — 'knob', 'wheel', or 'button'. Can be overridden per-slider. |
| springBack | boolean | false | When true, all sliders animate back to their initial values after release. Can be overridden per-slider. |
| springDuration | number | 1.0 | Seconds for the spring animation to complete. Can be overridden per-slider. |
| theme | object | {} | Shared theme override applied to all children. See Themes. |
Properties & methods
| Property / method | Description |
.values | Getter — returns { key: value, … } for all sliders (keys are labels with spaces/punctuation stripped). Setter accepts the same format to update any subset. |
.slider(name) | Returns the named child AnalogSlider for direct access (e.g. .value, .disabled). |
.disabled | Getter/setter — propagates to all child sliders. |
Per-slider overrides
Pass an object instead of a plain number for any slider to override shared options for that specific channel.
const eq = new MultiSlider({
x: 20, y: 40, height: 180,
min: -12, max: 12, readout: 'raw', decimals: 1,
sliders: {
'60Hz': 0, // plain number → uses shared min/max
'250Hz': 0,
'2kHz': { value: 3, theme: { capIndicator: '#ff6600' } }, // object → per-slider overrides
'8kHz': 0,
},
onChange: v => console.log('eq changed', v),
onRelease: v => console.log('eq released', v),
});
Example — vertical (default)
editable — changes update the preview
Example — horizontal with a spring-back override on one slider
editable — changes update the preview
RangeSlider Input
A fader with two draggable caps that define a low/high range. Drag either cap independently; caps constrain each other so they never cross. The region between the caps is filled with the accent colour. Scroll-wheel adjusts the cap nearest the cursor. Double-click a cap to reset it to its initial value.
new RangeSlider(opts)
Options
| Option | Type | Default | Description |
| x, y | number | 20, 20 | Top-left position. |
| width | number | 50 (v) / 180 (h) | Panel width in pixels. |
| height | number | 180 (v) / 44 (h) | Panel height in pixels. |
| min | number | 0 | Minimum value. |
| max | number | 1 | Maximum value. |
| valueLow | number | min | Initial lower handle value. |
| valueHigh | number | max | Initial upper handle value. |
| horizontal | boolean | false | When true, renders as a horizontal slider. Width/height defaults swap accordingly. |
| readout | string | 'raw' | Value format — 'raw', 'percent', or 'db'. |
| decimals | number | 2 | Decimal places for 'raw' readout. |
| showScale | boolean | true (v) / false (h) | Draw tick marks and value labels alongside the track. |
| showFader | boolean | true | Draw the cap handles. Set to false to show only the range fill (display-only mode). |
| label | string | '' | Caption drawn at the top of the panel. |
| onChange | function | null | Called with (valueLow, valueHigh) whenever either handle moves. |
| onRelease | function | null | Called with (valueLow, valueHigh) on mouse/touch release. |
| disabled | boolean | false | Disables interaction and renders a translucent overlay. |
| theme | object | {} | Partial theme override. See Themes. |
Properties
| Property | Description |
.valueLow | Current lower value (read/write). Setting it programmatically will not fire onChange. |
.valueHigh | Current upper value (read/write). |
Example — vertical
editable — changes update the preview
Example — horizontal
editable — changes update the preview
Dial Input
A rotary knob with a 270° arc sweep. Drag up/down or scroll the mouse wheel to turn it. A tooltip shows the live value while dragging. Double-click to reset to the initial value.
new Dial(opts)
Options
| Option | Type | Default | Description |
| size | number | 70 | Square panel size in pixels. Knob, arc, and scale all scale proportionally. |
| readout | string | 'raw' | Same as AnalogSlider: 'raw', 'percent', 'db'. |
| decimals | number | 2 | Decimal places for the 'raw' readout. |
| showScale | boolean | false | Draw tick marks and labels around the arc. Increase size if labels clip. |
| showKnob | boolean | true | Show or hide the knob body. false = arc-only display (used by VUDial). |
| style | string | 'classic' | Knob visual style — 'classic' · 'rubber' · 'grooved' · 'pointer'. See dial styles below. |
| springBack | boolean | false | When true, the knob animates back to its initial value after mouse/touch release. |
| springDuration | number | 1.0 | Seconds for the spring animation to complete. |
Example
editable — changes update the preview
Dial styles
Set style to choose a knob visual. All styles share the same interaction and arc track — only the knob body rendering changes.
MultiDial Input
Several Dial knobs arranged side-by-side in a single unit. A single onChange/onRelease fires whenever any dial changes, receiving a name → value object. A raised bevel panel frames the group. All dials share options by default; individual dials can override with per-dial objects.
new MultiDial(opts)
Options
| Option | Type | Default | Description |
| dials | object | {} | Each key is a dial name/label. Value is a number (initial value) or an object with per-dial overrides (value, min, max, style, …). |
| x | number | 20 | Left edge of the first dial. |
| y | number | 20 | Top edge of all dials. |
| size | number | 70 | Shared dial panel size in pixels (overridable per-dial). |
| min | number | 0 | Shared minimum (overridable per-dial). |
| max | number | 1 | Shared maximum (overridable per-dial). |
| readout | string | 'raw' | Shared readout format (overridable per-dial). |
| decimals | number | 2 | Shared decimal places (overridable per-dial). |
| horizontal | boolean | true | When true (default), dials are arranged in a horizontal row. When false, dials are stacked in a vertical column. |
| style | string | 'classic' | Shared knob style — 'classic', 'rubber', 'grooved', 'pointer' (overridable per-dial). |
| gap | number | 4 | Pixel gap between adjacent dials. |
| onChange | function | null | Called with { key: value, … } when any dial changes. Keys are dial labels with spaces and punctuation removed. |
| onRelease | function | null | Called with { key: value, … } on mouse/touch release. |
| springBack | boolean | false | When true, all dials animate back to their initial values after release. Can be overridden per-dial. |
| springDuration | number | 1.0 | Seconds for the spring animation to complete. Can be overridden per-dial. |
| theme | object | {} | Shared theme override. See Themes. |
Properties & methods
| Property / method | Description |
.values | Getter — returns { key: value, … } (keys are labels with spaces/punctuation stripped). Setter accepts same format. |
.dial(name) | Returns the named child Dial for direct access. |
.disabled | Getter/setter — propagates to all child dials. |
Example — horizontal row (default)
editable — changes update the preview
Example — vertical column with an individual dial override
editable — changes update the preview
Pads Input
XYPad Input
A two-axis drag surface. Click or drag to set X and Y values independently. Fires separate callbacks per axis. Y increases upward to match musical convention. Double-click to reset both axes to their initial values.
new XYPad(opts)
Options
| Option | Type | Default | Description |
| width | number | 160 | Panel width in pixels. |
| height | number | 160 | Panel height in pixels. |
| minX / maxX | number | 0 / 1 | Value range for the horizontal axis. |
| minY / maxY | number | 0 / 1 | Value range for the vertical axis. |
| valueX / valueY | number | center | Current X and Y position within the pad's range. Read or set at any time to get or move the crosshair programmatically. |
| scaleX | string | 'linear' | 'linear' or 'log'. Log requires minX > 0. |
| scaleY | string | 'linear' | 'linear' or 'log'. Log requires minY > 0. |
| crosshairColor | string | theme accent | Color of the crosshair dot and lines. |
| onChangeX | function | null | Called with the new X value when it changes. |
| onChangeY | function | null | Called with the new Y value when it changes. |
| onChange | function | null | Called with (valueX, valueY) when either axis changes. |
| onRelease | function | null | Called with (valueX, valueY) on release. |
| springBack | boolean | false | When true, both axes animate back to their initial values after mouse/touch release. |
| springDuration | number | 1.0 | Seconds for the spring animation to complete. |
Example
editable — changes update the preview
Example — spring back to center
editable — changes update the preview
GridPad Input
A 2-D grid of cells with four modes: toggle flips each cell 0 ↔ 1 on click; percent raises or lowers each cell's 0–1 value while the mouse is held; button treats each cell as a momentary key with press/release callbacks and no stored value; colorselect cycles each cell through a list of colours on each click, with each colour mapped to a semantic value. Optional horizontal and vertical grouping draws a bevel separator every N cells. Double-click the label to reset all cells to their initial values.
new GridPad(opts)
Options
| Option | Type | Default | Description |
| x, y | number | 0 | Top-left position. |
| rows | number | 4 | Number of rows. |
| cols | number | 4 | Number of columns. |
| mode | string | 'toggle' | 'toggle' — click to flip 0/1 · 'percent' — hold to raise/lower · 'button' — momentary press · 'colorselect' — click to cycle through colours. |
| options | array | [] | colorselect mode only. Each entry is either a colour string ('red', '#ff0000') or an object { color, value } where value is the semantic value returned by onChange. When value is omitted it defaults to the colour string. |
| cellSize | number | 20 | Pixel size of each square cell. |
| cellGap | number | 2 | Gap in pixels between cells. |
| hGroup | number | 0 | Draw a bevel separator every N columns. 0 disables. |
| vGroup | number | 0 | Draw a bevel separator every N rows. 0 disables. |
| groupGap | number | 4 | Extra pixels added at each group boundary. |
| values | array | all 0 | Initial 2-D array [[row][col]]. For colorselect, pass indices or semantic values — both are accepted. Ignored in button mode. |
| rate | number | 0.02 | Percent change per frame while mouse is held (percent mode only). |
| label | string | '' | Label drawn below the grid. |
| theme | object | {} | Partial theme override. See Themes. |
| onChange(values) | function | null | Called with a 2-D copy of values when any cell changes. In colorselect mode the grid contains the semantic value for each cell. |
| onRelease(…) | function | null | Toggle/percent: called with values on release. Button mode: called with (row, col) on release. |
| onPress(row, col) | function | null | Button mode only — called with the row and column of the pressed cell. |
Interaction — toggle mode
| Action | Effect |
| Click cell | Flip value between 0 and 1. |
Interaction — percent mode
| Action | Effect |
| Hold left click | Raise value toward 1 at rate per frame. |
| Hold right click | Lower value toward 0 at rate per frame. |
| Double-click at 0 | Snap to 1. |
| Double-click at 1 | Snap to 0. |
| Double-click mid-range | Snap to whichever boundary is closer. |
Interaction — button mode
| Action | Effect |
| Press cell | Cell highlights; onPress(row, col) fires. |
| Release | Cell returns to default; onRelease(row, col) fires. |
Interaction — colorselect mode
| Action | Effect |
| Left click | Advance to the next colour in options, wrapping around. |
| Right click | Step back to the previous colour. |
| Double-click label | Reset all cells to their initial colour (index 0 by default). |
Public API
| Member | Description |
values (getter) | Returns a deep copy of the 2-D values array. In colorselect mode returns semantic values, not indices. |
getValue(r, c) | Returns the value at row r, column c. In colorselect mode returns the semantic value of the current colour. |
setValue(r, c, v) | Sets the value at (r, c). In toggle mode snapped to 0/1; percent clamped 0–1; colorselect accepts an index integer. |
Example — toggle mode (step sequencer)
editable — changes update the preview
Example — percent mode (velocity matrix)
editable — changes update the preview
Example — colorselect mode
editable — changes update the preview
Selectors Input
Switch Input
An illuminated discrete selector. Two states renders as a rocker toggle — click to flip. Three or more states renders as a vertical button stack — click any slot to select it. The active slot glows with the style's accent color. Double-click any slot or the label to reset to the initial state.
new Switch(opts)
Options
| Option | Type | Default | Description |
| states | string[] | ['OFF','ON'] | Array of state labels. 2 entries = rocker. 3+ entries = selector stack. |
| state | number | 0 | Index of the initially active state. |
| springDefault | number | state | State index to restore on double-click reset. Defaults to state. Useful when the control is rebuilt dynamically and you want reset to always target a fixed index. |
| width | number | 48 | Panel width in pixels. |
| height | number | auto | Defaults to 70 for 2-state; states.length × 26 + 20 for N-state. |
| onChange | function | null | Called with (stateIndex, stateLabel) on every click. |
Reading state
sw.state // active index (readable/writable)
sw.states[sw.state] // active label string
Two-state rocker
editable — changes update the preview
N-state selector
editable — changes update the preview
A square push button that renders a Material Symbols icon at its centre. Pass any icon name from the Material Symbols Outlined set as the icon option — the font is already included via the page's <link> tag. Supports momentary (default) and toggle modes. An optional text label is drawn inside the button below the icon.
new IconButton(opts)
Material Symbols font required. Add this to your <head>:
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined&display=block" rel="stylesheet">
Options
| Option | Type | Default | Description |
| icon | string | 'star' | Material Symbols icon name, e.g. 'play_arrow', 'volume_up', 'settings'. Full list at fonts.google.com/icons. |
| size | number | 44 | Width and height of the button in pixels. Both dimensions are equal. |
| toggle | boolean | false | When true the button latches on each click, toggling .state between true and false. When false (default) the button fires once per click and does not latch. |
| state | boolean | false | Initial toggle state. Only meaningful when toggle: true. |
| iconSize | number | auto | Icon font size in pixels. Defaults to round(size × 0.58). |
| x, y | number | 0, 0 | Canvas position of the top-left corner. |
| label | string | '' | Text caption drawn inside the button below the icon. |
| onClick | function | null | Called on release. For toggle buttons receives the new state (true/false); for momentary buttons called with no arguments. |
| disabled | boolean | false | Suppresses all interaction and draws a disabled overlay. |
| theme | object | {} | Partial theme override. See Themes. |
Properties
| Property | Description |
.state | Read/write. Current toggle state (true/false). Setting programmatically does not fire onClick. |
.icon | Read/write. Change the displayed icon at any time. |
.disabled | Read/write. Disables or re-enables the button. |
Example
editable — changes update the preview
Selector Input
A multi-style option selector. Set style to choose the visual style. Scroll wheel and touch work for all styles. Double-click the selection display or the label to reset to the initial option (double-clicking the arrow buttons or gear wheel advances state as normal).
new Selector(opts)
Options
| Option | Type | Default | Description |
| options | string[] | ['A','B','C'] | Array of option labels. |
| state | number | 0 | Initial selected index into options. |
| style | string | 'rotary' | Visual style — 'rotary' or 'arrow'. See style descriptions below. |
| width | number | 140 | Total panel width in pixels. |
| height | number | 40 | Panel height in pixels. |
| onChange | function | null | Called with (index, label) whenever the selection changes. |
| onRelease | function | null | Called with (index, label) when the mouse/touch is released. |
| springBack | boolean | false | When true, the selection snaps back to its initial state after springDuration seconds. |
| springDuration | number | 1.0 | Seconds before snapping back when springBack is enabled. |
| label | string | '' | Caption drawn below the panel. |
| disabled | boolean | false | Disables interaction and renders a translucent overlay. |
| theme | object | {} | Partial theme override. See Themes. |
Properties
| Property | Description |
.state | Current selected index (read/write). |
.options | Array of option labels (read/write). |
Style — rotary (default)
A mechanical drum-style selector inspired by vintage rotary counters. A display window shows the selected option; the drum's serrated edge is exposed on the right — horizontal ridges scroll as the drum rotates, giving a tactile feel. Adjacent options are visible while scrolling. Drag up/down, scroll wheel, or touch.
Teeth column sizing: width is max(14, round(height × 0.28)) px. The display window occupies the remaining panel width to the left.
editable — changes update the preview
Style — arrow
A compact left/right arrow selector. Pressing either arrow button steps to the adjacent option; the new selection slides into the display window from the direction of travel. Scroll wheel also works.
editable — changes update the preview
A multi-select chip panel. Each string in words is displayed as a rounded pill inside a box. Click any pill to toggle its selection; multiple pills can be selected simultaneously. Double-click the label to reset to the initial selection.
new TagSelector(opts)
Options
| Option | Type | Default | Description |
| words | string[] | [] | Array of strings to display as selectable chips. Each string may contain spaces (multi-word phrases are treated as one chip). |
| selected | string[] | [] | Initially selected words. Must be members of words. |
| width | number | 200 | Panel width in pixels. Chips wrap to new rows when they exceed this width. |
| height | number | auto | Panel height. Omit to auto-size to fit all chips. Provide a fixed value to enable vertical scrolling when chips overflow — a thin scrollbar appears automatically and mouse-wheel or drag scrolls the content. |
| label | string | '' | Caption shown at the bottom of the panel. Double-click it to reset selection. |
| onChange | function | null | Called with (selectedArray, addedWord, removedWord) on each toggle. Exactly one of addedWord / removedWord is non-null per click; both are null on a label double-click reset. |
| disabled | boolean | false | Disables interaction and renders a translucent overlay. |
| theme | object | {} | Partial theme override. See Themes. |
Properties
| Property | Description |
.selected | Getter — returns current selection as string[]. Setter accepts a string[] to programmatically set selection. |
.words | Array of all chip strings (read/write). Reassigning marks layout dirty so chips reflow on next draw. |
Example
editable — changes update the preview
SliderSelector Input
A vertical fader that snaps to discrete string options. It looks and behaves like an AnalogSlider — same panel, track, and brushed-metal cap — but instead of a continuous numeric range the slider clicks into one of the provided option strings. Tick marks at each position are drawn to the right of the track, and the active option's tick and label are highlighted in the accent colour. Scroll-wheel steps through options when hovering.
new SliderSelector(opts)
Options
| Option | Type | Default | Description |
| options | string[] | [] | The ordered list of selectable strings. Index 0 appears at the top of the track, the last index at the bottom. |
| state | number | 0 | Initial selected index. |
| style | string | 'knob' | 'knob' — brushed-metal rectangular fader cap. 'button' — circular cap with a radial gradient, matching the AnalogSlider button style. |
| x, y | number | 20, 20 | Canvas position. |
| width | number | auto | Panel width. Defaults to a size that fits the longest option label with padding. |
| height | number | auto | Panel height. Defaults to enough vertical space for all options at 22 px spacing (min 80 px). |
| label | string | '' | Caption rendered at the top of the panel. |
| onChange | function | null | Called with (index, string) whenever the selection changes during drag or scroll. |
| onRelease | function | null | Called with (index, string) on mouse release. |
| disabled | boolean | false | Disables interaction and renders a translucent overlay. |
| theme | object | {} | Partial theme override. See Themes. |
Properties
| Property | Description |
.state | Current selected index (read/write). |
.value | Getter — returns options[state] as a string. Setter accepts a string and updates state to its index (no-op if not found). |
.options | The options array (read/write). Reassign to change the available choices; update .state if needed. |
Interaction
| Gesture | Effect |
| Drag cap | Moves the cap; snaps to the nearest option tick as you drag. |
| Scroll wheel | Steps one option up or down when hovering over the control. |
Example
editable — changes update the preview
Panel Container
A clipping container that groups ProControls into a positioned, optionally scrollable surface. Controls added with .add() are positioned relative to the panel's top-left corner — a slider at (10, 10) appears 10px from the panel edge, not the canvas edge. When child controls extend beyond the panel bounds, thin scrollbars appear automatically; the scroll wheel pans the view when no child control is hovered.
Constructor options
| Option | Type | Default | Description |
| x, y | number | 20, 20 | Panel top-left position on the canvas. |
| width | number | 300 | Panel width in pixels. |
| height | number | 200 | Panel height in pixels. |
| visible | boolean | true | Initial visibility. Set panel.visible = false to hide the panel and suppress all child events. |
| label | string | '' | Title shown in a bar at the top of the panel. When set, the scrollable content area starts below the bar and a minimize/maximize button appears on the right side of the bar. No title bar is shown when empty. |
| minimized | boolean | false | Initial minimized state. When true the panel starts collapsed to just the title bar. |
| minimizable | boolean | true | When false the −/+ toggle button is hidden and the panel cannot be collapsed. |
| movable | boolean | true | When false dragging the title bar does not reposition the panel. |
| resizable | boolean | false | When true a resize grip appears in the bottom-right corner; drag it to change the panel's width and height at runtime. |
| onChange | function | null | Called with a { fieldName: value, … } snapshot object whenever any child control's value changes. See onChange / values below. |
| onRelease | function | null | Called with the same snapshot object when the user releases a child control. |
| theme | object | {} | Partial theme override. See Themes. |
Methods & properties
| Name | Description |
.add(control) | Attach a ProControl to the panel. The control is removed from the global registry and managed by the panel from that point on. Returns this for chaining. |
.onChange | Callback fired with the full { fieldName: value, … } snapshot whenever any child changes. Can be set at construction or assigned later. |
.onRelease | Callback fired with the same snapshot when the user releases any child control. Can be set at construction or assigned later. |
.values | Read-only getter. Returns a fresh { fieldName: value, … } snapshot of every child control's current value — same object shape as the onChange and onRelease arguments. Useful for polling the panel state without setting up a callback. |
.visible | Get/set panel visibility. When set to false the panel and all children stop drawing and receiving events. |
.minimized | Get/set minimized state. When true the panel collapses to only the title bar; the small −/+ button in the title bar also toggles this. |
.minimizable | Read/write. Hides the toggle button and disables collapse when false. |
.movable | Read/write. Prevents title-bar dragging when false. |
.resizable | Read/write. When true, a grip appears in the bottom-right corner allowing the user to drag-resize the panel. |
.scrollX / .scrollY | Current scroll offset in pixels. Read or write to programmatically scroll the panel. |
onChange / onRelease / values
When onChange or onRelease are set, the panel listens to every child control and fires the respective callback — with a single snapshot object — on change or release. The same object is always available via panel.values.
Field names are derived from each control's label option with spaces and punctuation stripped (e.g. "Low Cut" → LowCut). Controls without a label are named by class and a per-class counter: the first unlabelled AnalogSlider becomes AnalogSlider1, the second AnalogSlider2, and so on. MultiSlider and MultiDial follow the same rule — the slider/dial keys within their own values object also have spaces and punctuation stripped from the label.
Values per control type:
| Control | Value in snapshot |
| AnalogSlider, Dial | number |
| Switch | Current state label string (e.g. "On") |
| Selector, SliderSelector | Selected option string |
| MultiSlider, MultiDial | { name: value, … } object |
| GridPad | 2-D array of cell values |
| TagSelector | Array of selected tag strings |
| RangeSlider | { low, high } |
| XYPad | { x, y } |
| IconButton | boolean state |
| Markup, Menu, VUMeter, LEDMeter, ADSRDisplay | omitted |
Note: set any per-control onChange callbacks before calling panel.add(). Callbacks assigned after add() will work for the individual control but the panel notification will wrap the value that was set at add-time.
Scrolling
Scrollbars appear automatically when children extend beyond the panel boundaries. The scroll wheel scrolls the panel when the pointer is over the panel but not over a focused child control. If a child control (e.g. a Dial) is hovered, the scroll wheel is forwarded to that child instead. You can also drag the scrollbar thumbs directly.
Example — onChange
Example — grouped controls
Example — scrollable content
MessageDialog Container
A self-sizing, movable dialog that displays a formatted text message and optional action buttons. The message string is word-wrapped to fit the dialog width. Buttons are arranged in a single row at the bottom; if multiple buttons are provided the dialog widens automatically to fit them all on one line. Drag the title bar to reposition.
new MessageDialog(opts)
Options
| Option | Type | Default | Description |
| message | string | '' | The text to display. Use \n for explicit line breaks. Long lines are wrapped automatically to fit the dialog width. |
| buttons | string[] | [] | Button labels shown in a row at the bottom of the dialog. Omit or pass an empty array for a message-only dialog. |
| x, y | number | 20, 20 | Canvas position. |
| width | number | auto | Dialog width. Defaults to at least 220 px or wide enough to fit all buttons on one row, whichever is larger. |
| label | string | '' | Title bar text. When set a styled title bar is shown at the top and the dialog can be repositioned by dragging it. |
| movable | boolean | true | When false the dialog cannot be dragged. |
| onButton | function | null | Called with (index, label) when a button is clicked. |
| disabled | boolean | false | Disables interaction and renders a translucent overlay. |
| theme | object | {} | Partial theme override. See Themes. |
Properties
| Property | Description |
.message | Read/write. Reassigning triggers a layout rebuild on the next draw so the dialog resizes to fit the new text. |
.buttons | Read/write. Reassigning the array rebuilds the button row on the next draw. |
.movable | Read/write. Toggle whether the dialog can be dragged at runtime. |
Example
editable — changes update the preview
Displays
Read-only meters and display panels. Assign .value (or individual properties) each frame to drive them — no event wiring required.
VUMeter Display
Extends AnalogSlider. Overlays a segmented LED column (green → yellow → red) with peak hold on top of the fader track. Drive it each frame by assigning .value from audio analysis.
new VUMeter(opts)
Inherits all AnalogSlider options. Defaults overridden: readout: 'db', showScale: false, min: 0, max: 1.
Additional options
| Option | Type | Default | Description |
| segCount | number | 20 | Number of LED segments. |
| meterWidth | number | 8 | Pixel width of the LED column. |
| colorLow | string | '#00cc44' | Segment color for the lower 60%. |
| colorMid | string | '#cccc00' | Segment color for 60–85%. |
| colorHigh | string | '#cc2200' | Segment color for the top 15%. |
| showFader | boolean | true | false = hide the fader cap entirely for a pure display meter. |
Example
editable — changes update the preview
VUDial Display
Extends Dial. Replaces the filled arc with a ring of segmented LEDs with peak hold. Use showKnob: false for a pure meter ring, or true to show both the set position and the live level.
new VUDial(opts)
Inherits all Dial options. Defaults overridden: readout: 'db', showScale: false, min: 0, max: 1.
Additional options
| Option | Type | Default | Description |
| segCount | number | 24 | Number of LED arc segments. |
| colorLow | string | '#00cc44' | Color for the lower 60%. |
| colorMid | string | '#cccc00' | Color for 60–85%. |
| colorHigh | string | '#cc2200' | Color for the top 15%. |
| showKnob | boolean | true | false = pure LED ring with no knob body. |
Example
editable — changes update the preview
LEDMeter Display
A 7-segment numeric display. Segments are drawn as beveled hexagons with a canvas glow. Values are right-aligned; if the number is too wide for the digit slots, all segments show -----. Assign .value each frame.
new LEDMeter(opts)
Options
| Option | Type | Default | Description |
| digits | number | 5 | Number of digit positions (including sign). |
| readout | string | 'raw' | 'raw', 'percent', or 'db'. dB clamps at −99 at min. |
| decimals | number | 2 | Decimal places. The dot is drawn in the gap between digits — no extra slot needed. |
| digitW | number | 14 | Width of each digit character in pixels. |
| digitH | number | 26 | Height of each digit. Segment thickness scales with this. |
| digitGap | number | 4 | Gap between digits. Decimal point is centered here. |
| ledColor | string | null | LED color. null uses theme.capIndicator. |
Example
editable — changes update the preview
Panel width = padding×2 + digits×digitW + (digits−1)×digitGap. With defaults a 5-digit meter is 106 px wide.
ADSRDisplay Display
A display-only ADSR envelope graph inside a beveled panel. The envelope shape updates live whenever any property changes — pair it with dials or sliders to visualise synth parameters in real time.
new ADSRDisplay(opts)
Options
| Option | Type | Default | Description |
| x, y | number | 0 | Top-left position of the panel. |
| width | number | 220 | Panel width in pixels. |
| height | number | 120 | Panel height in pixels. |
| attack | number | 0.1 | Attack time — any consistent unit (seconds, ms, etc.). Relative to decay and release. |
| decay | number | 0.2 | Decay time. |
| sustain | number | 0.7 | Sustain amplitude level, 0–1. |
| release | number | 0.3 | Release time. |
| envelopeColor | string | null | Line and fill tint. null uses theme.capIndicator. |
| label | string | '' | Label drawn below the panel. |
| theme | object | {} | Partial theme override. See Themes. |
Visual layout
Attack, Decay, and Release are displayed proportionally across 70% of the plot width. The sustain hold occupies the remaining 30% as a fixed visual segment. A, D, S, R labels appear below each segment; dashed dividers mark the phase boundaries.
Example
editable — changes update the preview
Markup Display
A scrollable text panel that renders a subset of Wiki Markup syntax — headings, bold, italic, bullet and numbered lists, indented blocks, horizontal rules, and links. Set text at any time to update the content; the panel re-parses and scrolls to the top automatically.
new Markup(opts)
Options
| Option | Type | Default | Description |
| x, y | number | 0 | Top-left position of the panel. |
| width | number | 280 | Panel width in pixels. |
| height | number | 200 | Panel height (visible viewport) in pixels. |
| text | string | '' | Wiki Markup source. Can be set at any time. |
| fontSize | number | 12 | Base font size in pixels. Headings scale relative to this. |
| padding | number | 10 | Inner padding on all sides. |
| lineSpacing | number | 1.5 | Line-height multiplier applied to fontSize. |
| onClick | function | null | Called on any click inside the panel. Receives the link's URL string when a link is clicked, or null when clicking non-link text. |
| theme | object | {} | Partial theme override. See Themes. |
Properties
| Property | Type | Description |
| text | string (get/set) | Current markup source. Setting resets scroll to top. |
| scrollY | number (get/set) | Current vertical scroll offset in pixels. Clamped to valid range automatically. |
Wiki Markup syntax
| Syntax | Renders as |
= Heading 1 = | Large heading with underline rule |
== Heading 2 == | Medium heading |
=== Heading 3 === | Small heading |
'''bold''' | Bold text |
''italic'' | Italic text |
'''''bold italic''''' | Bold italic text |
[[label]] or [[url|label]] | Link text (rendered in accent color) |
* item | Bullet list item |
** item | Sub-bullet list item |
# item | Numbered list item |
## item | Sub-numbered list item |
: text | Indented block with left bar |
---- | Horizontal rule |
| (blank line) | Paragraph gap |
Scrolling
When content is taller than the panel, a thin scrollbar appears on the right edge. Scroll with the mouse wheel while hovering the panel. Set scrollY programmatically to jump to any position.
Example
editable — changes update the preview
ConsolePanel Display
A floating, scrollable log panel that intercepts console.log, console.warn, console.error, and console.info calls as well as uncaught runtime errors and unhandled promise rejections. Messages are color-coded by type, timestamped, and always auto-scroll so the most recently added line is visible at the bottom. Consecutive identical messages (same type and text) are collapsed into a single entry showing the repeat count and the most recent timestamp — e.g. [12:04:31 ×5] redraw tick. A CLR button clears the log; a −/+ toggle minimizes the panel to just its title bar. The panel is movable, resizable, and minimizable by default.
new ConsolePanel(opts)
Options
| Option | Type | Default | Description |
| x, y | number | 20, 20 | Canvas position of the panel's top-left corner. |
| width | number | 400 | Panel width in pixels. |
| height | number | 200 | Panel height in pixels. |
| label | string | 'Console' | Text shown in the title bar. |
| movable | boolean | true | When false the panel cannot be repositioned by dragging. |
| resizable | boolean | true | A resize grip appears in the bottom-right corner; drag it to resize the panel at runtime. |
| minimizable | boolean | true | A −/+ toggle button appears in the title bar; clicking it collapses the panel to just the title bar. |
| minimized | boolean | false | Initial minimized state. |
| maxMessages | number | 100 | Maximum number of messages retained. Oldest messages are dropped when the buffer is full. |
| timestamps | boolean | true | Prefix each message with a [HH:MM:SS] timestamp. Set to false to suppress. |
| intercept | string[] | ['log','warn','error','info'] | The console methods to intercept. Pass a subset to limit capture, e.g. ['error','warn']. |
| theme | object | {} | Partial theme override. See Themes. |
Properties & methods
| Name | Description |
.clear() | Clears all messages and resets scroll. Same as clicking the CLR button. |
.messages | Read-only getter. Returns a shallow copy of the message array. Each entry is { type, text, time, count }; count is ≥ 1 when consecutive identical messages have been collapsed. |
.remove() | Removes the panel and restores all intercepted console methods to their originals. |
.minimized | Get/set. When true the panel collapses to just the title bar. |
.label | Read/write. Text shown in the title bar. |
.resizable | Read/write. Enables or disables the resize grip at runtime. |
.minimizable | Read/write. Shows or hides the minimize toggle button. |
.movable | Read/write. Enables or disables title-bar dragging at runtime. |
Message colors
| Source | Color |
console.log | Theme label color (default text) |
console.info | Theme accent / indicator color |
console.warn | Amber #cc8800 |
console.error / uncaught errors | Red #dd3333 |
Interaction
| Gesture | Effect |
| Drag title bar | Repositions the panel (when movable is true). |
| Click −/+ button | Minimizes or restores the panel (when minimizable is true). |
| Scroll wheel | Scrolls the message log. The next incoming message will always snap back to the bottom. |
| Drag scrollbar | Scrolls by dragging the thin scrollbar on the right edge (visible when content overflows). |
| Click CLR button | Clears all messages. |
| Drag resize grip | Resizes the panel from the bottom-right corner (when resizable is true). |
Console passthrough: intercepted methods still call the original browser console functions, so DevTools output is unaffected. Call .remove() to fully restore the originals.
Example
editable — changes update the preview
TimeGraphPanel Display
A scrolling time-series line graph panel. Push data points each frame to plot them against time — the oldest point scrolls off the left edge as new ones arrive on the right. Pass a plain number to plot a single variable, or an object ({key: value, …}) to plot multiple named variables simultaneously, each drawn in its own color with a legend. Y-axis range is auto-scaled to the data with 8% padding, or fixed by setting min and max. The panel is movable, resizable, and minimizable by default.
new TimeGraphPanel(opts)
Options
| Option | Type | Default | Description |
| x, y | number | 20, 20 | Canvas position of the panel's top-left corner. |
| width | number | 360 | Panel width in pixels. |
| height | number | 200 | Panel height in pixels. |
| label | string | 'Time Graph' | Text shown in the title bar. |
| maxSamples | number | 200 | Maximum number of data points retained. Oldest points are discarded when the buffer is full. |
| autoAdjustY | boolean | true | Automatically fit the Y axis to the min/max of the data currently in the buffer. Set to false to use fixed min/max bounds instead. |
| min | number | 0 | Fixed Y-axis minimum. Only used when autoAdjustY is false. |
| max | number | 1 | Fixed Y-axis maximum. Only used when autoAdjustY is false. |
| lineWidth | number | 1.5 | Stroke weight for data lines in pixels. |
| grid | boolean | true | Show horizontal grid lines at 25%, 50%, and 75% of the Y range. |
| legend | boolean | true | Show a color legend in the top-right corner of the plot (only visible when graphing multiple variables). |
| movable | boolean | true | Allow repositioning by dragging the title bar. |
| resizable | boolean | true | Show a resize grip in the bottom-right corner. |
| minimizable | boolean | true | Show a −/+ toggle button to collapse the panel to just the title bar. |
| minimized | boolean | false | Initial minimized state. |
| theme | object | {} | Partial theme override. See Themes. |
Properties & methods
| Name | Description |
.push(value) | Add a data point. Pass a number for single-variable mode, or a plain object ({key: value, …}) for multi-variable mode. Switching between modes resets the data buffer. |
.clear() | Remove all data points and reset variable tracking. |
.autoAdjustY | Get or set whether the Y axis auto-fits to the data range (true) or uses fixed bounds (false). |
.data | Read-only getter. Returns a shallow copy of the current data array. |
.minimized | Get/set. Collapses the panel to just the title bar when true. |
.visible | Get/set. Hides the panel and suppresses events when false. |
Example — single variable
Example — multiple variables
Themes & Styles
Set ControlStyle before creating controls. The style is captured at construction time, so controls built before a change keep their original look.
ControlStyle = 'brushed'; // before any new controls
Built-in styles
stainless
navy accent · polished chrome texture
brushed
orange accent · sine-wave grain
dimpled
blue accent · dimpled circle texture
yellow
red accent · dark text
proControlBackground()
Use instead of p5's background(). Fills the canvas with the style color. 'brushed' and 'stainless' also draw per-row texture overlays via the native Canvas 2D API.
| Style | Texture |
| black | Flat fill — #1a1a1a. |
| stainless | Gaussian highlight profile: sharp specular band near the top third, soft secondary reflection below center, edge darkening, and a thin pure-white specular line at the peak. |
| white | Flat fill — #e8e8e8. |
| brushed | Three layered sine-wave frequencies (fine grain, visible stroke bands, slow tonal drift) plus a directional sheen band — simulates brushed aluminum. |
| dimpled | Brushed aluminum base with a grid of small dimpled circles (12 px spacing) — each dot has a dark outer ring and a lighter inner highlight. Blue accent. |
| red | Flat fill — #cc0000. Primary red background, darker red controls, yellow accent. |
| blue | Flat fill — #0000cc. Primary blue background, darker blue controls, yellow accent. |
| yellow | Flat fill — #e0d000. Warm yellow background, golden controls, red accent · dark text. |
Runtime style switching
Rebuild controls with proControlReset() when switching styles. The Live Demo uses an AnalogSwitch to cycle all four styles.
const STYLES = ['black', 'stainless', 'white', 'brushed', 'dimpled', 'red', 'blue', 'yellow'];
const LABELS = ['BLACK', 'STEEL', 'WHITE', 'BRUSH', 'DIMPL', 'RED', 'BLUE', 'YLW'];
let currentStyle = 'black';
function buildControls() {
proControlReset();
ControlStyle = currentStyle;
mySlider = new AnalogSlider({ value: mySlider?.value ?? 0.5 });
styleSwitch = new AnalogSwitch({
states: LABELS,
state: STYLES.indexOf(currentStyle),
label: 'STYLE',
onChange: i => { currentStyle = STYLES[i]; buildControls(); },
});
}
Per-control theme overrides
Pass a partial theme object to override individual color keys while keeping the rest of the active style.
| Key | Affects |
| panel | Panel background fill. |
| panelStroke | Panel border and knob outline. |
| track | Slider groove / dial arc background. |
| capHighlight | Top gradient color of the fader cap or knob. |
| capShadow | Bottom gradient color of the fader cap or knob. |
| capIndicator | Accent line / filled arc color / LED glow color. |
| scaleText | Scale label text color. |
| scaleTick | Scale tick mark color. |
| label | Control label text color. |
| readout | Readout value text color. |
| readoutBg | Readout pill background. |
| hoverGlow | Hover highlight fill (include alpha). |
| disabledOverlay | Overlay when disabled: true. |
| font | p5 font object from loadFont(). null = canvas default. |
new Dial({ label: 'MASTER', theme: { capIndicator: '#ffcc00' } });
Touch & Events
All mouse, wheel, touch events, and rendering are handled automatically by the library. No event functions and no draw loop are needed in your sketch. touch-action: none is applied to the canvas automatically to prevent iOS scroll conflicts.
Nothing to wire up. Just create controls — they start receiving events and drawing themselves immediately. Call .remove() when done.
How it works
Each control self-registers on construction into a shared array. Two p5.prototype.registerMethod hooks handle everything: a 'pre' hook fires at the start of every frame to dispatch mouse/wheel/touch events; a 'post' hook fires at the end of every frame and draws any control that wasn't already drawn explicitly that frame. This means you can still call c.draw() manually for z-order control — the post hook simply skips controls already rendered.
Coexisting with your own handlers
Registered library methods and sketch-level event functions both fire independently — they don't conflict. You can still define mousePressed() in your sketch for other logic.
Multi-touch: p5.js maps only the first touch to mouseX/mouseY, so only one control can be manipulated at a time. Two-finger gestures are unaffected.