Events

Notes and events

An endpoint that carries a record at a frame rather than a number every sample.

Anyone whose patch should respond to a keyboard.

An endpoint carries either a number every sample (stream) or a record at a frame (event).

A stream is a value you can read on every frame, like a voltage on a wire. An event is a message: “this key was pressed,” “this key was released,” or “the pressure changed.” The message has named fields, and a handler chooses which of them to remember.

Use streams when continuous values fit your patch naturally. Use events when you need note identity, several related values together, or exact native event timing. Both approaches still produce audio in the same frame loop.

Declare an endpoint and its handler#

Playground
input event note noteIn;
float gate = 0.0f;
float frequency = 220.0f;

on noteIn (note n)
{
    gate = n.on ? 1.0f : 0.0f;
    if (n.on) { frequency = n.pitch; }
}

noteIn is the endpoint name and n is the local name for each payload. This fragment shows the receiving side; the next chapter uses the stored values in a complete playable voice.

Note payload#

FieldPole typeMeaning
pitchfloatFrequency in Hz. Preserve the last sounding pitch when releasing a voice.
velocityfloatNormalized note velocity, 0…1.
pressurefloatNormalized expression pressure, 0…1.
brightnessfloatNormalized timbre expression, 0…1.
channelintMIDI channel identity supplied by the host.
keyintMIDI key identity; distinct from frequency in Hz.
kindint1 = note-on, 2 = note-off, 3 = expression, 4 = voice steal.
onConditionWhether the note is active. Use in if, or convert with n.on ? 1.0f : 0.0f.
legatoConditionHost-supplied legato status.

on and legato are conditions, not numeric floats: if (n.on) works and n.on * 2.0f does not. kind distinguishes a new note from an expression update, so changing pressure need not restart the attack.

Controller and bend payloads#

PayloadFieldPole typeMeaning
ccchannelintMIDI channel identity.
cccontrollerintController number; for example, 1 for the mod wheel.
ccvaluefloatNormalized controller value, 0…1.
bendchannelintMIDI channel identity.
bendvaluefloatNormalized bend, -1…1. The host's chosen pitch range is a separate convention.

Declare these with input event cc controlIn; or input event bend bendIn;, and provide a matching handler. The field types come from the compiler and host's shared event schema.

Timing#

A handler runs between the two samples either side of its frame: every event stamped at frame i executes before sample i is rendered, and several at one frame run in the order they were submitted.

A note therefore lands where it was played, rather than on the next block boundary.

The batch is validated in full before anything renders, so a malformed late event cannot leave a processor half-advanced or a note-on with no note-off. A frame at or past the block's length is refused rather than clamped, because it belongs to the next block.

Note In the browser, events are applied at block boundaries rather than on the stamped frame, so a note lands within about 3 ms of where it was asked for. That is a limit of the browser runtime, not of the language.

Events reserve no words#

event, on, note, cc and bend are all recognised by position, so let on = gate > 0.5f; and let note = 60.0f; keep working. Both already appear in shipped code.

An event endpoint occupies no slot#

Declaring one between two streams does not move the second one's slot, so a block can gain an event input without changing anything about how its numbers are addressed.

Streams or events?#

Both work, and they are different tools:

  • Streams — the pitch/gate/velocity convention described under endpoints — need no handler and are what most patches on this site use.
  • Events give you the note as a record, with channel, key and MPE expression, at the frame it happened.