Player bindings reference
player.bindings.yaml is optional and sits beside lute.project.yaml. Whole-project lute check (lute check --project <DIR>, no document operands) reads it after compilation. It does not affect the project fingerprint or runtime execution. Relative paths are resolved under assetsBase, which defaults to assets/ beside the bindings file.
bindingsVersion: "0.53"canvas: { width: 1920, height: 1080 }assetsBase: assets/assets: { theme: audio/theme.ogg }backgrounds: foyer: { image: bg/foyer.png, times: { night: bg/foyer-night.png } }characters: guide: sprite: { default: char/guide.png, emotions: { happy: char/guide-happy.png } } costumes: { coat: { default: char/guide-coat.png } } nameColor: "#e0c080"anchors: { default: { x: 0.5 } }actions: { enter: { effect: fadeIn, duration: 0.4 } }transitions: { fade: { effect: fade, duration: 0.6 } }cameraMove: { shake: { effect: shake, duration: 0.5, strength: 12 } }framing: { closeUp: { zoom: 1.35 } }cgLayouts: { full: { fit: cover }, inset: { fit: contain, scale: 0.6 } }textStyles: { emphasis: { fontWeight: bold }, whisper: { alpha: 0.6 } }vfx: flash: { effect: flash, color: "#fff", duration: 0.3 } lanterns: { effect: custom } tideRise: { effect: custom, name: tide-rise, duration: 2 }music: { moods: { calm: audio/calm.ogg } }volumes: { silent: 0, quiet: 0.25, normal: 0.5, loud: 0.75, full: 1 }sfx: { door: audio/door.ogg }sequences: { doorOpens: { video: video/door-opens.mp4 } }voice: { pattern: "voice/{voiceKey}.ogg" }ui: font: Noto Sans KR title: { text: Brinewake, background: bg/title.png, music: theme } ended: by: run.ending screens: restore: { title: "The Bell Rings Again", text: "Brinewake keeps its light.", background: dawnHarbor } retire: { title: "Quiet Water", text: "You hand the bell rope to Ivo." } theme: { text: "#ffffff", accent: "#e0c080" }flow: newGame: { occasion: start } idle: { menu: [{ label: Foyer, occasion: visit, target: place.foyer }] }Top-level sections
Section titled “Top-level sections”bindingsVersionis the toolchain line,"MAJOR.MINOR"(seebindingsVersion).canvasrequires positivewidthandheight;assetsBaseis optional.assetsmaps compiledassetIdvalues to files.backgroundsmapsbg.locationvalues toimage;timesmapsbg.timevalues to alternate files.charactersis keyed by cast id. Each value hassprite.default, optionalsprite.emotions, optionalcostumes(each with the same sprite shape), and optionalnameColor.anchors,actions,transitions,framing,cameraMove,cgLayouts,textStyles, andsequencesare keyed by their declared staging domains. Anchors havexplus optionalyandscale; framing haszoomplus optionalxandy; CG layouts havefit: cover|containplus optionalscale,x, andy; text styles acceptfontWeight,fontStyle,color,alpha,scale, andletterSpacing.music.moods,sfx, andvfxare keyed by values used by compiled documents.sequencesvalues containvideo;voice.patternis a{voiceKey}path template.volumes(0.42) maps each member of the project’svolumedomain to a gain, a finite number from0to1. A music cue’svolumeis an absolute level: the player plays it at that member’s gain and never parses a number. A cue with novolumeplays at gain1; a member without a gain plays at0, never at full volume. An unknown key isE-BINDING-KEY, a member with no key isW-BINDING-MISSING, and a gain that is not a finite number in[0, 1]isE-BINDING-SHAPEat its value.uicontainsfont, optionaltitle, optionalended, and arbitrary theme colour names. Every UI asset reference first checksassets:: an asset key counts as a use of that key; any other value is a file belowassetsBaseand must exist.ui.title(0.47) has an optionaltext— the game’s name, drawn as the title screen’s heading — and an optionalbackgroundandmusic, resolved like every UI asset reference. When written, it binds at least one of the three, andtextis a non-empty string;ui.title: {}or an emptytextisE-BINDING-SHAPE. Withouttextthe title screen draws no heading; withoutbackgroundormusicit draws no title backdrop or plays no title music.ui.endedrequiresby, a declared state path with a closed enum/entity domain, andscreens. Each screen requirestitleandtextand may setbackground; unknown screen keys areE-BINDING-KEY, while members without screens fall back silently.- When the ended screen is shown, player-web reads the entry at
byin the runtime view,program.view(state).state[by]. That entry is a runtime value{ kind, value }, not a bare string: player-web uses itsvalueonly when it is a{ kind: "str" }entry andscreensmaps that member, which then supplies the screen title, text, and optional background. An absent entry, an entry of another kind, an unmapped member, a failing query, or noui.endeduses the stock ended screen.
bindingsVersion
Section titled “bindingsVersion”bindingsVersion follows the luteVersion rule. Its value is "MAJOR.MINOR"; the toolchain line is the MAJOR.MINOR of the toolchain version ("0.53" for Lute 0.53.x).
- Whole-project
lute checkreportsW-BINDINGS-VERSION-STALEat the value when it differs from the toolchain line. An older line says to update it to the toolchain line, whichlute fixdoes; a newer line says to upgrade the toolchain rather than downgrade the stamp; a value that is notMAJOR.MINOR(such as"0.47.0") is told to restamp it. - A section newer than the declared line is
E-BINDINGS-VERSIONat that section’s key, naming the line it needs, for example`ui.ended` needs bindingsVersion "0.46" or newer; this file declares "0.45". Sections are not checked when the value is notMAJOR.MINOR. lute fixraises an olderMAJOR.MINORvalue inplayer.bindings.yamlto the toolchain line, keeping the rest of the file byte for byte (quotes and comments included). A newer or malformed value (leading zeros such as"0.046"included) is left alone, and so is a file written as one flow mapping ({ bindingsVersion: …, … }); the warning nameslute fixonly when it raises the value.
| Section | Needs bindingsVersion |
|---|---|
vfx.<name> with effect: custom | "0.41" |
volumes | "0.42" |
ui.ended | "0.46" |
ui.title.text | "0.47" |
| every other section | "0.40" |
Effects
Section titled “Effects”Effect objects have an effect key and only the parameters listed here:
| Effect | Parameters |
|---|---|
fade, dissolve, wipeLeft, wipeRight, wipeUp, wipeDown, slideLeft, slideRight | duration? |
cut, none | none |
fadeIn, fadeOut, slideIn, slideOut, hop | duration? |
shake | duration?, strength? |
zoom | duration?, scale? |
pan | duration?, x?, y? |
flash | duration?, color? |
blur | duration?, strength? |
tint | duration?, color? |
custom (only under vfx) | name?, duration? |
A vfx entry MAY be { effect: custom, name?, duration? }: a presenter-defined effect outside the catalogue. name defaults to the binding key; the player passes the entry through as a Vfx cue whose effect is custom with that name. actions, transitions, and cameraMove keep the closed catalogue: effect: custom there is E-BINDING-EFFECT.
Unknown YAML keys are rejected. Unknown effects or parameters produce E-BINDING-EFFECT. Malformed YAML or an invalid shape produces E-BINDING-SHAPE with the YAML position. A referenced file missing under assetsBase produces E-BINDING-FILE.
Project checks and diagnostics
Section titled “Project checks and diagnostics”Domain-keyed binding keys must be members of the merged project domains. characters keys must be declared cast ids. Flow occasions must be declared. These violations produce E-BINDING-KEY, with a nearby-name suggestion when one exists.
A flow target (flow.newGame.target, flow.idle.raise.target, flow.idle.menu[].target) is the full occasion target raiseOccasion takes, written in the prefixed form <prefix>.<member> of the occasion’s target domain: place.square, never the bare square (lute 0.47.1). Whole-project lute check checks it with the same rule as beat targets and lute play step targets: an open: entity kind admits any <prefix>.<id>; a closed kind or a members: subset admits exactly its members under the prefix. Anything else is E-BINDING-KEY; the message starts flow target `…`: and, when a close name exists, suggests did you mean `place.square`?. A target on an occasion declared without a target (no target: or target: false) is E-BINDING-KEY, and so is a target on an occasion whose entity kind the project does not declare.
Flow and declared occasions
Section titled “Flow and declared occasions”flow.newGame and flow.idle (its raise and its menu entries) are host configuration: the reference player raises them itself, flow.newGame after the runtime begins and flow.idle when the runtime is idle. Since 0.51 a plugin occasion may be declared raisedBy: declared (manifests): the host then raises it only through a directive’s or reward kind’s declared raises. A flow.newGame or flow.idle that raises such an occasion breaks that declaration, because the checker has already treated the occasion as raised only by the declared sources: keep these bindings to occasions the host is meant to raise on its own.
The reference player performs the declared raises itself, from a pending queue it drains after each presentation has settled and before the idle flow runs, so a flow.idle raise never overtakes one (see Custom hosts). The player save that holds the queue carries format "0.51"; a "0.50" save is refused.
A declared domain member, on-stage cast member, or compiled usage without a binding produces W-BINDING-MISSING. A binding in an asset/background/mood/sfx/vfx usage section that no compiled document uses produces W-BINDING-UNUSED. Diagnostics are anchored in player.bindings.yaml and appear in lute check --project <DIR> --json.
Use lute check --project <DIR> after lute compile --project <DIR> -o <out>; a clean project with no bindings remains valid because the file is optional.
The normative repository reference is docs/player/bindings.md.