S4) Backticks
When BPscript opens a window
Native code in the temporal flow
BPscript does one thing: order symbols in time. When real code is needed — to define a sound, calculate a value — it opens a window to another language, then closes it. This window is the backtick.
Where does this article fit in?
We saw in S3 that each symbol belongs to an actor, which designates a runtime (downstream execution environment). Here, we understand how BPscript transmits code to this runtime: backticks.
The principle: entrusting code without interpreting it
Backticks (` “) delimit code that BPscript does not parse, understand, or validate. It transmits it to the relevant runtime, which evaluates it.
Sa(vel:`rrand(40,127)`)
BPscript sees: “the symbol Sa has a vel parameter whose value is a fragment between backticks”. It entrusts rrand(40,127) to Sa‘s runtime, which evaluates it and returns a number. BPscript doesn’t know what rrand means — and doesn’t need to know. This clear boundary is what allows the language to remain small: it describes the when, the runtime decides the how.
[!type] Current status: today, the operational evaluator is JavaScript, with Web Audio output. The tags
sc:(SuperCollider),py:(Python), ortidal:(TidalCycles) that we’ll see in the examples illustrate the target architecture — the same backtick mechanism, multiple possible downstream runtimes — but these evaluators are not yet connected.
Three types of backticks
BPscript distinguishes three types based on the backtick’s position and how the target runtime is designated.
Inline — the runtime is implicit
Inside a symbol call, the runtime is that of the symbol: no need to specify it.
Sa(vel:`rrand(40,127)`) // Sa's runtime evaluates the fragment
The compiler knows which runtime to call — that of the carrying symbol’s actor.
Orphan — at the top of the scene, the tag is mandatory
When a backtick is not attached to any symbol and is at the top of the scene (outside of rules), you must explicitly indicate which runtime it is intended for:
`sc: SynthDef(\sitar, { |freq, vel=80| ... }).add` // → SuperCollider
`py: import dmx; d = dmx.open()` // → Python
Standalone — in the flow, the tag is mandatory
When an unattached backtick appears inside a rule, it executes at the time of the derivation where it is located. The tag remains mandatory:
melodie -> Sa(vel:`sc: i*20`) `sc: i=i+1` Re(vel:`sc: i*20`)
The tag (sc:, py:, tidal:, js:…) precedes the code, separated by a :. Without a tag, the compiler doesn’t know where to send the code — this is an error. Only the inline type dispenses with it, because the carrying symbol already provides the runtime.
Three uses
The three types correspond to three moments in a scene’s lifecycle.
1. Init — prepare the runtime, before derivation
Orphan backticks, at the top of the scene, are executed once upon loading, before any rule:
`js: synth = makeGrainSynth()`
This is where the groundwork is laid: defining a synthesizer, importing a library, initializing a variable.
2. Flow — at the moment of derivation
A standalone backtick, in the middle of a sequence, executes at the time the derivation reaches it:
melodie -> Sa(vel:`js: i*20`) `js: i=i+1` Re(vel:`js: i*20`)
Here, ` js: i=i+1 ` is a zero-duration event in the flow: it executes between two notes, at the precise moment calculated by BP3, and modifies the runtime’s state.
3. Resolution — evaluate and return a value
An inline backtick, in a call, calculates the value of a parameter:
Sa(vel:`rrand(40,127)`) // the runtime evaluates and returns an integer
Sa(vel:120) // no backtick → literal, no evaluation
Resolution is a round trip: BPscript sends the expression, the runtime evaluates it and returns the value, BPscript injects it into the parameter. This is the only case where a response is expected; init and flow are fire-and-forget sends.
Variables, scopes, and isolation
The model provides that each runtime maintains its own persistent session — like a REPL (Read-Eval-Print Loop, an interactive console) that remains open throughout the scene. A variable lives there within its runtime’s scope:
`js: var i = 0` // runtime scope: i = 0
Sa(vel:`js: i`) `js: i = i + 1` Re(vel:`js: i`)
// i persists from one backtick to the next, in the same session
In the target architecture, multiple runtimes coexist without shared state: a variable defined in one is not visible in another. Coordination between them does not happen through variables, but through BPscript’s own mechanisms:
- flags (managed by BP3 — see S6);
- triggers
!and<!(see S7).
This is intentional isolation: each runtime is a sandbox, without hidden coupling or side effects from one language to another.
A file without backticks remains portable
A .bps file without any backticks does not depend on any external language: its structure is pure.
@tempo:120
S -> { melodie, rythme }
melodie -> Sa Re Ga Pa
rythme -> -!dha - -!ti -!dha
Such a file works with any adapter (program that translates BPscript events into commands for a runtime) capable of receiving gates and triggers — Web Audio today, others tomorrow. As soon as a backtick is added, a part of the file is linked to a specific runtime: this is an assumed compromise between power and portability.
Key takeaways
- Backticks delimit code that BPscript does not interpret — it transmits it to the runtime
- Current status — the operational evaluator is JS / Web Audio;
sc:/py:/tidal:tags illustrate the target architecture, not yet connected - Three types — inline (in a call, implicit runtime), orphan (at the top of the scene, mandatory tag), standalone (in the flow, mandatory tag)
- Three uses — initialization, execution at the moment of derivation, parameter calculation
- Persistent sessions — each runtime maintains its state; variables live within their own scope
- No shared state between runtimes — coordination happens via flags and triggers
- Without backticks = portable — the structure does not depend on any external language
Glossary
- Backtick: The `
“ character delimiting native code intended for a runtime — BPscript transmits it without interpreting it - Inline backtick: A backtick within a symbol call — the runtime is inferred from the symbol, no tag needed
- Orphan backtick: A backtick at the top of the scene, not attached to a symbol, executed at init — requires a runtime tag
- Standalone backtick: A backtick in the flow of a rule, executed at the moment of derivation — requires a runtime tag
- Runtime: A downstream execution environment that evaluates code and produces sound
- REPL: Read-Eval-Print Loop — an interactive console that maintains state between commands
- Scope: The visibility area of a variable — each runtime has its own, isolated from others
- Adapter: A program that translates timestamped BPscript events into commands for a runtime
Links in the series
- S3 — Types, actors, and bindings — how types and runtimes are declared
- S6 — Flags and traversals — coordination via flags
- S7 — The shared instant — coordination via
!/<!triggers - S9 — The six-layer pitch system — routing to adapters
- S10 — Under the hood — how the downstream runtime receives backticks
Prerequisite: S3
Reading time: 10 min
Tags: #BPscript #backticks #runtime #live-coding
Next article: S5 (coming soon) — Structuring time: polymetry, silences, speed