Skip to content
Feeding a clanker? Grab this page as raw .md

MidiButton API#

MidiButton is a pad you can press on-screen or bind to a hardware MIDI pad. Where Knob and Fader learn a continuous control-change, MidiButton learns a MIDI note — the message a drum pad or launch button sends — and turns presses into an event or a latched state.

Pick mode="momentary" (the default; value is True only while held) or mode="toggle" (each press flips value). Every press also bumps press_timestamp, so an observe handler fires even on repeated presses, and velocity carries the note-on velocity (0-127; on-screen clicks send 127).

Put a glyph on the face with icon: a built-in name (play, pause, stop, record, skip-back, skip-forward, circle, square, triangle, heart, star, bell, zap, check, x, plus, minus, power, mic, music), or any emoji/text rendered literally. With an icon set, label becomes a caption above the pad; with no icon, label renders on the face.

MIDI is on by default: the pad shows a "MIDI learn" chip — click it, hit a pad on your hardware, and the next note-on binds to this button (Web MIDI, Chromium browsers). The binding is remembered in browser localStorage so it survives a restart. Pass midi=False for a plain on-screen pad; read the binding back via midi_note / midi_channel / midi_device.

See also: Knob and Fader for the continuous-CC members of the same MIDI-learn family.

Bases: AnyWidget

A button that you can press on-screen or bind to a hardware MIDI pad.

Unlike :class:Knob / :class:Fader (which learn a continuous control-change), a MidiButton learns a MIDI note — the message a drum pad or launch button sends — and turns presses into an event or a latched on/off state.

Two modes:

  • "momentary" (default): value is True only while the button is held (mouse down, or note-on until note-off). Every press also bumps press_timestamp so an observe handler fires even on repeated presses.
  • "toggle": each press flips value between True and False; releases are ignored.

The MIDI "learn" flow mirrors the Knob: click the small MIDI chip, then hit a pad on your hardware — the next note-on binds to this button. Bindings persist in browser localStorage (keyed by midi_key / label) and use the Web MIDI API (Chromium browsers, secure context).

Examples:

import marimo as mo
from wigglystuff import MidiButton

trigger = mo.ui.anywidget(MidiButton(label="Fire", midi=True))
trigger

Create a MidiButton.

Parameters:

Name Type Description Default
label str

Optional caption shown above the pad.

''
icon str

Glyph shown on the pad face. Either a built-in name ("play", "pause", "stop", "record", "skip-back", "skip-forward", "circle", "square", "triangle", "heart", "star", "bell", "zap", "check", "x", "plus", "minus", "power", "mic", "music") or any literal string/emoji (e.g. "🔴"). Empty falls back to label on the face.

''
mode str

"momentary" (value is True only while held) or "toggle" (each press flips value).

'momentary'
value bool

Initial state. For "toggle" this is the starting on/off; for "momentary" it is the resting (unpressed) state, usually False.

False
size int

Pad size in pixels (square).

64
color str

Optional CSS color for the pressed/active button. Empty string uses the theme default.

''
midi bool

Show a "MIDI learn" chip (default True). Click it, then hit a pad on your hardware; the next note-on message binds to this button. Uses the Web MIDI API (Chromium browsers, secure context). Read the binding back via midi_note / midi_channel / midi_device. Pass False for a plain on-screen button with no MIDI chip.

True
midi_note int

Bind a note number (0-127) up front instead of learning it. -1 (default) leaves it unbound.

-1
midi_channel int

MIDI channel (0-15) for the binding, or -1 for any.

-1
midi_key str

localStorage key for persisting the learned binding across restarts. Defaults to label. Empty (no label either) disables persistence.

''
midi_scope Optional[str]

Namespace for the persisted binding, so different notebooks don't collide. Empty (default) uses the browser's URL path automatically; pass an explicit string to pin it.

None
**kwargs Any

Forwarded to anywidget.AnyWidget.

{}
Source code in wigglystuff/midi_button.py
def __init__(
    self,
    label: str = "",
    icon: str = "",
    mode: str = "momentary",
    value: bool = False,
    size: int = 64,
    color: str = "",
    midi: bool = True,
    midi_note: int = -1,
    midi_channel: int = -1,
    midi_key: str = "",
    midi_scope: Optional[str] = None,
    **kwargs: Any,
) -> None:
    """Create a MidiButton.

    Args:
        label: Optional caption shown above the pad.
        icon: Glyph shown on the pad face. Either a built-in name
            (``"play"``, ``"pause"``, ``"stop"``, ``"record"``,
            ``"skip-back"``, ``"skip-forward"``, ``"circle"``, ``"square"``,
            ``"triangle"``, ``"heart"``, ``"star"``, ``"bell"``, ``"zap"``,
            ``"check"``, ``"x"``, ``"plus"``, ``"minus"``, ``"power"``,
            ``"mic"``, ``"music"``) or any literal string/emoji (e.g.
            ``"🔴"``). Empty falls back to ``label`` on the face.
        mode: ``"momentary"`` (``value`` is ``True`` only while held) or
            ``"toggle"`` (each press flips ``value``).
        value: Initial state. For ``"toggle"`` this is the starting on/off;
            for ``"momentary"`` it is the resting (unpressed) state, usually
            ``False``.
        size: Pad size in pixels (square).
        color: Optional CSS color for the pressed/active button. Empty
            string uses the theme default.
        midi: Show a "MIDI learn" chip (default ``True``). Click it, then hit
            a pad on your hardware; the next note-on message binds to this
            button. Uses the Web MIDI API (Chromium browsers, secure
            context). Read the binding back via ``midi_note`` /
            ``midi_channel`` / ``midi_device``. Pass ``False`` for a plain
            on-screen button with no MIDI chip.
        midi_note: Bind a note number (0-127) up front instead of learning
            it. ``-1`` (default) leaves it unbound.
        midi_channel: MIDI channel (0-15) for the binding, or ``-1`` for any.
        midi_key: localStorage key for persisting the learned binding across
            restarts. Defaults to ``label``. Empty (no label either)
            disables persistence.
        midi_scope: Namespace for the persisted binding, so different
            notebooks don't collide. Empty (default) uses the browser's URL
            path automatically; pass an explicit string to pin it.
        **kwargs: Forwarded to ``anywidget.AnyWidget``.
    """
    if mode not in _MODES:
        raise ValueError(f"mode must be one of {_MODES}, got {mode!r}.")
    if midi_scope is None:
        midi_scope = ""

    super().__init__(
        label=label,
        icon=icon,
        mode=mode,
        value=bool(value),
        size=size,
        color=color,
        midi=midi,
        midi_note=midi_note,
        midi_channel=midi_channel,
        midi_key=midi_key,
        midi_scope=midi_scope,
        **kwargs,
    )

Synced traitlets#

Traitlet Type Notes
value bool Pressed (momentary) or on/off (toggle) state.
press_timestamp float Bumped on every press so observe fires on repeats.
velocity int Note-on velocity (0-127) of the last press; 127 for a click.
label str Caption above the pad, or the face text when no icon.
icon str Built-in icon name, or any emoji/text for the face.
mode str "momentary" or "toggle".
size int Pad size in pixels (square).
color str CSS color for the active pad. Empty follows the theme.
midi bool Show the MIDI-learn chip and listen for notes.
midi_note int Bound note number (0-127), or -1 when unbound.
midi_channel int Bound MIDI channel (0-15), or -1 for any.
midi_device str Name of the bound MIDI input device.
midi_supported bool Whether the browser exposes Web MIDI (set from JS).
midi_learning bool Whether the pad is currently in learn mode.
midi_key str localStorage key for the persisted binding (defaults to label).
midi_scope str Namespace for the binding; empty uses the browser URL path.