sillage_composition
The verb of Sillage: working at the perfumer's organ, and striking the blotter.
sillage_composition
The verb of Sillage: working at the perfumer's organ, and striking the blotter.
What it provides
- The organ overlay (
sillage_organ_hotspots) — a hit target over every one of the 36 vials, projected fromCore.vialSlot(n)through the live camera, so the targets follow the camera wherever staging puts it. A vial already in the formula wears a red-ink frame and a dose wafer. Down the left edge, the three phases (Tête / Coeur / Fond) bracket the tiers they belong to — the organ read as the drydown curve stood upright. - The formula card (
sillage_formula) — a portrait sheet of aged rag paper pinned to the bottom of the 3:4 frame. Material names in a chancery italic, doses in a figures column with dotted leaders, a dose that changed struck out in red with the new figure written above it, marginal notes, a thumbnail of the accord's shape, and a total underlined twice. A tab lowers the sheet so the lowest tiers of the organ can be reached. - The bench — a rack of mouillettes, a pipette on its rest, an inkpot, a written fiche on a short stack of blanks, and the struck strip with its stain spreading through it while the drydown runs.
- The clock — the 24 hours of wear compressed into
Core.time.drydownSeconds.
API
Composition.addDrop(id, n) | add or take back drops of a material |
Composition.addDropBySlot(slot, n) | the same, by organ slot 1..36 |
Composition.clearFormula() | empty the fiche, returning stock |
Composition.strike() | dip the blotter and run the drydown |
Composition.show() / .hide() | show or hide both screens |
Events
Emits formula:changed, strip:struck, strip:tick, strip:done, and
asks staging for the bench station with station:request. Listens for
screen:changed, brief:issued and station:changed.
Writes Core.state.formula and Core.state.drydownT. Reads the curve from
game.sillage_sim, meters drops through game.sillage_economy, and
highlights vials through world.sillage_organ — each guarded, so the loop
still plays when one of them is not there.
Materials
game.comp_blotter_paper, game.comp_card_paper, game.comp_brass,
game.comp_walnut, game.comp_glass_clear, game.comp_stain.
Interface
What this asset declares: the schema it conforms to, what it exposes, and the rendered structured payload.
conforms to
zero/source-extract/v2strict
quiet(what: string, fn: any, ...: ?) → void
A best-effort call across a boundary this slice does not own: a neighbour's screen, a neighbour's module. It must never take the loop down with it, and it must never fail silently either.
| arg | type | description |
|---|---|---|
| what | string | |
| fn | any | |
| ... | ? |
sim( ) → any
NEIGHBOURS — resolved lazily so a module that fails to load is survivable
econ( ) → any
organ( ) → any
face(ref: string, fallback: string) → string
THE HAND — the faces this slice writes in Core.type names the faces as asset refs; the UI's `fontFamily` takes the registered family NAME. Strip the ref and check it against the live registry so a missing face falls back to a period-plausible one rather than to a sans-serif, which would break 1954 entirely.
| arg | type | description |
|---|---|---|
| ref | string | |
| fallback | string |
frenchUpper(s: string) → string
| arg | type | description |
|---|---|---|
| s | string |
quatRotate(q: any, vx: number, vy: number, vz: number) → void
PROJECTION — 36 world points into UI space, through the live camera
| arg | type | description |
|---|---|---|
| q | any | |
| vx | number | |
| vy | number | |
| vz | number |
gameCamera( ) → any
The camera a player is looking through. In play that is the per-player clone of the rig; in edit it is the authored prototype, which is also the pose `capture` renders from — so a capture and a click agree.
currentView( ) → View
project(v: View, px: number, py: number, pz: number) → void
World point -> { x, y, depth } in UI space. depth <= 0 is behind the lens.
| arg | type | description |
|---|---|---|
| v | View | |
| px | number | |
| py | number | |
| pz | number |
vialRect(v: View, slot: number) → void
The on-screen rect of one vial, from its own extents rather than a guess.
| arg | type | description |
|---|---|---|
| v | View | |
| slot | number |
sheetStyle( ) → void
PAPER — the surfaces this slice prints on
laid(w: number, h: number) → any
| arg | type | description |
|---|---|---|
| w | number | |
| h | number |
foxing(w: number, h: number) → any
| arg | type | description |
|---|---|---|
| w | number | |
| h | number |
rule(weight: number, colour: string?, width: number?) → any
| arg | type | description |
|---|---|---|
| weight | number | |
| colour | string? | |
| width | number? |
doubleRule(width: number?) → any
| arg | type | description |
|---|---|---|
| width | number? |
smallcap(text: string, colour: string?, size: number?, spacing: number?) → any
| arg | type | description |
|---|---|---|
| text | string | |
| colour | string? | |
| size | number? | |
| spacing | number? |
hand(text: string, size: number, colour: string?, extra: { [string]: any }?) → any
| arg | type | description |
|---|---|---|
| text | string | |
| size | number | |
| colour | string? | |
| extra | { [string]: any }? |
figures(text: string, size: number, colour: string?, extra: { [string]: any }?) → any
| arg | type | description |
|---|---|---|
| text | string | |
| size | number | |
| colour | string? | |
| extra | { [string]: any }? |
gap(h: number) → any
| arg | type | description |
|---|---|---|
| h | number |
totalDrops( ) → number
THE FORMULA — the state this slice owns
orderedFormula( ) → void
marginNote(id: string, drops: number) → string
What the perfumer writes in the margin beside a line. Read off the sim's own numbers when it has them, off the catalogue's structure when not.
| arg | type | description |
|---|---|---|
| id | string | |
| drops | number |
shapeNote( ) → string
The note across the head of the sheet: what the SHAPE is missing, never which material. This is the one idea of the game, said in the margin.
hotspotTree( ) → any
leader( ) → any
THE FORMULA CARD The dotted leader a written ledger runs between a name and its figure, so a narrow sheet does not read as two columns floating apart.
formulaLine(row: { id: string, drops: number, m: any }, index: number) → any
| arg | type | description |
|---|---|---|
| row | { id: string, drops: number, m: any } | |
| index | number |
formeSketch( ) → any
The perfumer's own thumbnail of the shape: three bars, one per phase, as long as that phase's share of the formula. It is not the graph — that is the drydown slice's page — it is the sketch you make while you work.
cardTree( ) → any
refreshCard( ) → void
REDRAW
refreshHotspots( ) → void
refresh( ) → void
wantsCompose( ) → boolean
show( ) → void
hide( ) → void
syncScreen( ) → void
metered( ) → boolean
ECONOMY — a drop costs something once there is something to spend
addDrop(id: string, n: number) → boolean
THE VERB
| arg | type | description |
|---|---|---|
| id | string | |
| n | number |
addDropBySlot(slot: number, n: number) → boolean
| arg | type | description |
|---|---|---|
| slot | number | |
| n | number |
clearFormula( ) → void
usableCurve(c: any) → boolean
THE CURVE — sim's when sim has one, provisional when it does not
| arg | type | description |
|---|---|---|
| c | any |
provisionalCurve(formula: { [string]: number }) → any
| arg | type | description |
|---|---|---|
| formula | { [string]: number } |
simulate(formula: { [string]: number }) → any
| arg | type | description |
|---|---|---|
| formula | { [string]: number } |
phaseAverage(curve: any, from: number, to: number) → number
| arg | type | description |
|---|---|---|
| curve | any | |
| from | number | |
| to | number |
provisionalVerdict(brief: any, curve: any) → any
| arg | type | description |
|---|---|---|
| brief | any | |
| curve | any |
judge(brief: any, curve: any) → any
| arg | type | description |
|---|---|---|
| brief | any | |
| curve | any |
spawnProp(name: string, model: string, material: string, pos: { number }, scale: { number }, euler: { number }?) → any
THE BENCH — the props this slice puts on the marble
| arg | type | description |
|---|---|---|
| name | string | |
| model | string | |
| material | string | |
| pos | { number } | |
| scale | { number } | |
| euler | { number }? |
prop(name: string) → any
Every prop is reached BY NAME at the moment it is used, never through a reference kept from build time. Any slice may call Core.rebuild(), which despawns and respawns this slice's whole subtree; a proxy held across that points at an entity that no longer exists, and writing a field on it neither raises nor lands — the blotter simply never appears.
| arg | type | description |
|---|---|---|
| name | string |
setVisible(name: string, on: boolean) → void
| arg | type | description |
|---|---|---|
| name | string | |
| on | boolean |
build(root: any) → void
| arg | type | description |
|---|---|---|
| root | any |
setStain(t: number) → void
| arg | type | description |
|---|---|---|
| t | number |
requestStation(name: string, secs: number) → void
THE CLOCK
| arg | type | description |
|---|---|---|
| name | string | |
| secs | number |
strike( ) → boolean
onCallback(id: string, value: any) → void
CALLBACKS
| arg | type | description |
|---|---|---|
| id | string | |
| value | any |
assertCallbackEnv( ) → void
LIFECYCLE Every click in this slice arrives through one broadcast environment. It is keyed, so re-registering it is idempotent — and it has to be re-asserted rather than registered once, because a registration made in edit does not survive the engine entering play, and a slice whose environment has quietly gone is a slice where every vial is dead and nothing says so.
start( ) → void
stop( ) → void
Sub-parts
Everything contained inside this part. Assets are composite children (clickable cards). Files are leaf payloads. Expand any row to view its source.
Problems
Everything affecting this asset right now: its own problems, anything wrong inside it, and problems on its direct dependencies.
agent_score is exposed.+ quality × 0.35
+ performance × 0.25
± compat factor
Usability ratings
Did the part work as advertised when consumers tried to drop it in. Separate from upvotes: those are taste; this is "did it function".
Scoped to this part · feeds back into the world's score.