A Panel is a movable, resizable window that groups other controls together.
Think of it like a rack unit on a hardware synthesizer — you drop controls inside and they
scroll as a unit if there are too many to fit. Panels can be minimized, dragged around the
canvas, and resized by the user at runtime. You build the contents by calling
pnl.add(control) for each control you want inside.
Create a Panel with x, y, width, height,
and a label. An empty panel is just a chrome frame — but you can already drag
it, minimize it with the − button, and resize it by dragging the bottom-right corner.
pnl.add()
Pass any ProControls control to pnl.add() and it becomes a child of the panel.
Child coordinates are relative to the panel's top-left content area, not the canvas.
You can mix any control types — dials, sliders, switches, and more.
onChange
callbacks — wire them up the same way you would on the canvas directly.
When the total content is taller than the panel, a scrollbar appears automatically and the user can scroll with the mouse wheel inside the panel. You don't need to do anything special — just add controls and let ProControls handle the overflow. Try scrolling in the preview below.
onRelease and .values
The Panel fires onRelease whenever the user finishes interacting with any child
control. You can read pnl.values at any time — it returns an object keyed by
each child control's label, with the current value. This is the easiest way to
snapshot all the controls in a panel at once.
pnl.values inside your draw() loop
without any callbacks if you prefer — it always reflects the current state of all child controls.
A ModalPanel is a promise-based dialog you can fill with any ProControls — sliders,
switches, selectors, and more. Call await modal.show() to open it; your code pauses
at that line until the user clicks Save or Cancel. While the
dialog is open, a semi-transparent backdrop covers the rest of the canvas and all other control
events are blocked. This makes it ideal for collecting a small set of values before proceeding
with an action.
Create a ModalPanel in setup() and call .add() for each
control you want inside it. The controls are not shown yet — they appear only when
show() is called. Give each control a name so you can read its value
from the result later.
mousePressed must be declared async so that
await modal.show() works. p5.js fully supports async event handlers — the
draw() loop continues running while the dialog is open.
When the user clicks Save, show() resolves with an object
containing each control's current value, keyed by its name. When the user
clicks Cancel, it resolves with null. Always check for
null before using the result.
show(opts)
Pass options to show() to override the constructor values for that particular
invocation. This is useful when the same modal serves multiple purposes — for example, an
"Edit" dialog that shows the name of the item being edited in the title bar. Options accepted
by show() are the same as the constructor: label, width,
x, and y.
The most common ModalPanel pattern is to open it when the user triggers an "Edit" or "Configure"
action, then apply the returned values to the sketch. Because draw() keeps running
while the modal is open, you can show live feedback behind the backdrop — for example, an
animated waveform that responds to the controls as the user adjusts them.
modal.isOpen to guard against opening the modal twice.
Since mousePressed fires on every click, a second click while the modal is already
showing would otherwise try to open it again. The guard if (modal.isOpen) return;
prevents this.
A Bevel is a decorative separator line — the visual equivalent of the engraved
dividing lines you find on hardware mixers and synthesizer panels. Place one on the canvas or
inside a Panel to visually group related controls. Provide y for a horizontal line
or x for a vertical line. Positions can be in pixels or as a percentage of the
containing area.
y
Set y to place a horizontal bevel line at that pixel position from the top of
the canvas (or parent Panel). The line spans the full width of its container.
The dials above and below the line are separate controls — the bevel just sits between them.
x
Set x instead of y to get a vertical dividing line. Inside a Panel
you can also use percentage strings like '50%' to position the bevel relative to
the panel's width or height — so it stays centred even if the panel is resized.
thin, deep, color
The style option changes how the line is drawn. thin (default)
is a subtle two-tone engraved line. deep adds a wider shadow for a more
pronounced groove. color draws the line using your theme's accent colour —
useful for highlighting a section boundary.
Try editing style in the example below.
The label option adds text centered on the bevel line, displayed in a rounded
pill badge so it sits cleanly on top of the line. Vertical bevels rotate the label text
automatically. Labels are great for naming sections of a panel — like "AUX", "FX", or
"INPUTS".
A MessageDialog is a modal alert box — like the "Are you sure?" dialogs you see in desktop applications. It displays a title, a message, and one or more buttons. While it is open, it blocks interaction with everything behind it. It is ideal for confirming destructive actions, displaying warnings, or presenting a brief status message.
Pass a label (the dialog title), a message (the body text), and a
buttons array. The dialog appears immediately. Clicking any button dismisses it.
You can use any number of button labels and any message text. Convention matches operating system dialogs: the last button is the primary action, the first is Cancel or the safer option. Keep messages short and specific — users scan dialogs quickly.
onButton
The onButton callback fires when any button is clicked, passing the button's
index and label. Use the index to decide what action to take — 0 is the first
button (Cancel), the last index is the primary action. Click the buttons below and watch the
console!
A common pattern is to create the dialog only when an action is triggered, rather than at
startup. For example, show a confirm dialog when the user clicks a "Clear" button. Because
ProControls dialogs appear immediately on construction, just call new MessageDialog()
inside your event handler and it will pop up right away.
An InputDialog is a modal prompt — it displays a title, message, a text input field, and buttons. Use it any time you need the user to type something: renaming a preset, entering a BPM value, or providing a filename. Like MessageDialog, it blocks the rest of the canvas while open.
Create with a label, message, and buttons. An optional
inputPlaceholder shows hint text in the empty field. The dialog appears
immediately — click in the field and type something!
inputValue
Set inputValue to pre-populate the text field with an existing value. This is
useful when the user is editing something that already has a name — they see the current
value and can change just what they need to.
onSubmit and onButton
onSubmit fires when the user presses Enter or clicks the last button, passing
the typed string. onButton fires for any button click and also passes the string.
Use onSubmit for the primary action and onButton if you need to
distinguish between buttons. Type in the field below and press Enter or click OK!
Just like MessageDialog, the best pattern is to create the InputDialog only when an action is triggered. The example below uses a switch to simulate a "rename" button. Clicking it opens the dialog, and submitting the form logs the new name. Try it!
<input>
element — keyboard shortcuts, copy/paste, and IME all work exactly as expected.