Dicey Table

SoundMaterial

A material is the physical surface a piece is made of. It is one half of the pair — material × action — that the engine resolves into an actual licensed clip, and it is the only way a mod or a non-admin editor user can name a first-party sound. Clip ids and file paths are never addressable.

8 values, declared as soundMaterialSchema in packages/shared/src/soundSets.ts. Seven of them make a noise; silent is a sentinel that makes none.

The values#

Value What it sounds like Kinds that default to it Where you can name it
wood Wooden tiles, meeples, board surfaces. board, card-holder TableObjectDefinition.material · SoundRef · playSound({ event }) · ModSoundSet.material
cardboard Punchboard counters, chits, thick tokens. the same four
metal Coins, metal miniatures, metal-cased dice. the same four
plastic Injection-moulded pieces and standard dice. die, token the same four
card Paper card stock — the deck and hand sounds. card, deck the same four
tile Ceramic or heavy resin tiles; also the rummage sounds. bag the same four
generic The catch-all. What playSound({ event }) uses when you omit material. custom the same four
silent A sentinel, not a sound. An entity set to silent emits nothing. the same four

Every kind has a default, so an entity with no material still sounds like something: cardcard, deckcard, dieplastic, tokenplastic, boardwood, bagtile, card-holderwood, customgeneric.

Where a material comes from, in order#

  1. A per-action override on the entity — soundSetOverrides[action], set by api.setObjectSound or authored in the editor.
  2. The entity's own material field.
  3. The per-kind default above.

api.playSound({ event: { material, action } }) bypasses all three: it names a material directly for one play and changes nothing about any entity.

What a mod can and cannot name#

By design. A SoundRef is either { kind: "builtin", material } — semantic, resolved internally — or { kind: "mod", modId, name }, pointing at a sound the mod declared in its own manifest. A first-party clip id, a SoundSetId and a file path are all unrepresentable in the type, and the host additionally refuses a { kind: "mod" } ref naming another mod or a name this mod never declared. That is a licensing boundary — the first-party clips are not exposed as an addressable library — and it is not expected to change. Declare what you need in manifest.soundSets and reference it by name.

See also#