Dice View v3 API
Reference for the physics API, requests, options, timeline, and looks in version 3.0.0-alpha.0.
This article covers the v3 alpha preview, which uses physics only and is not yet published on npm. V2 ↗ Licenses ↗
This reference describes the ae00d9f v3 revision, identified as 3.0.0-alpha.0. The alpha is not on npm yet; see installing v3. The published 2.6.1 release has a separate reference.
View receives resolved values and presents them. It neither parses notation nor draws random results. In v3, DisplayMode is only 'physics'; cinematic mode has been removed.
Entrypoints
| Import | Contents |
|---|---|
@erpg/dice3dview |
DiceResultViewer, helpers, types, and looks; WebGL module with no runtime dependencies |
@erpg/dice3dview/external |
Alias for the same module, retained for v2 integrations |
@erpg/dice3dview/adapters |
System dice converters without loading the renderer |
@erpg/dice3dview/style.css |
Canvas styles |
Construction requires an element in the DOM. init() requires WebGL2 or WebGL1; construct only on the client.
DiceResultViewer
| Member | Contract | Purpose |
|---|---|---|
canvas |
HTMLCanvasElement |
Canvas created and attached by the constructor |
constructor(options?) |
ViewerOptions |
Validates options and mounts the canvas in container |
init() |
Promise<this> |
Initializes WebGL, themes, and resize observation; idempotent |
display(request) |
Promise<DisplayResult> |
Simulates and draws supplied faces; calls init() when needed |
displayTimeline(request) |
Promise<DisplayTimelineResult> |
Runs a semantic journal using physics |
clear() |
void |
Cancels the presentation and immediately clears the scene |
updateOptions(options) |
Promise<void> |
Merges options; timeline effects are merged individually |
applyLook(look) |
Promise<void> |
Validates and applies a complete versioned look |
playParticles(moment, options?) |
void |
Triggers particles on dice already on the table |
resize() |
void |
Recalculates the stage; ResizeObserver also calls it |
dispose() |
void |
Releases resources and removes the canvas; do not reuse the instance |
A new presentation or clear() cancels the previous Promise with DisplayCancelledError. Detect it with isDisplayCancelledError(error).
display(request)
type DiceSides = 2 | 4 | 6 | 8 | 10 | 12 | 20 | 100
type DisplayMode = 'physics'
interface ResolvedDie {
readonly id: string
readonly sides: DiceSides
readonly value: number
readonly discarded?: boolean
readonly theme?: string
readonly themeColor?: string
}
interface DisplayRequest {
readonly id: string
readonly dice: readonly ResolvedDie[]
readonly seed?: string
readonly mode?: DisplayMode | 'kinematic' // legacy; runs physics
}
interface DisplayResult {
readonly id: string
readonly dice: readonly ResolvedDie[]
readonly durationMs: number
}
id and dice must be nonempty. value must be an integer from 1 to sides. seed defaults to id and affects only choreography. Supported shapes are d2, d4, d6, d8, d10, d12, d20, and d100; each d100 uses two bodies against the maxDice limit. mode: 'kinematic' is accepted for compatibility, warns once in the console, and runs physics.
display() returns normalized, frozen dice. After input validation, a WebGL or asset failure is logged and the result is still returned. If visual presentation is essential to your workflow, call await viewer.init() explicitly and inspect the stage.
displayTimeline(request)
interface DisplayTimelineRequest {
readonly id: string
readonly dice: readonly TimelineDieDefinition[]
readonly events: readonly DiceTimelineEvent[]
readonly seed?: string
readonly mode?: 'physics' | 'kinematic' // legacy
}
interface DisplayTimelineResult extends DisplayResult {
readonly eventCount: number
readonly phaseCount: number
readonly degraded: boolean
}
Each TimelineDieDefinition identifies a die and its sides; roll and reroll events supply the faces. Explosion, reroll, discard, and classification events retain the v2 journal format. V3 spawns and relaunches dice physically. onTimelineProgress receives initial, phase, and complete snapshots for synchronizing the total in your UI. Unlike display(), displayTimeline() propagates rendering and asset failures as well as validation errors.
System adapters
createMixedDisplayRequest({ id, dice, seed?, unsupportedDice?, theme?, themeColor?, keptIds?, themeColors? }) accepts rollMixedDice().dice from Core, preserves order, and selects physicalValue ?? rawValue ?? value. Vampire V5, Assimilation, Fate, and Daggerheart profiles automatically apply their themes. Generic dice with no 3D shape are omitted by default; unsupportedDice: 'error' makes that an error. createSystemDisplayRequest() accepts dice already associated with a profileId and applies each system profile.
import { rollMixedDice } from '@erpg/dicecore'
import { createMixedDisplayRequest } from '@erpg/dice3dview/adapters'
const mixed = rollMixedDice('2d20+5; v5(7,3,4); fate(4)', { seed: 'table-42' })
await viewer.display(createMixedDisplayRequest({
id: 'table-42',
seed: 'table-42',
dice: mixed.dice
}))
ViewerOptions
Core and scene
| Option | Default | Use |
|---|---|---|
container |
null |
Selector or element; required in practice |
assetPath |
/assets/dice-box/ |
Public root for themes/ |
origin |
page origin | Origin for internal assets |
theme / themeColor |
default / #2e8555 |
Default theme and color |
preloadThemes / externalThemes |
[] / {} |
Preloading and external theme URL map |
maxDice |
120 |
Body limit; d100 counts as two |
enableShadows / shadowTransparency |
true / 0.8 |
Soft contact shadows |
lightIntensity / antialias / scale |
1 / true / 5 |
Lighting, WebGL, and scale |
delay / wallPadding |
10 ms / 0.25 |
Sequential release and stage boundary |
spawnSpacing / spawnHeightStep / spawnOverscan |
1.72 / 0 / 0.15 |
Throw distribution |
reducedMotion |
auto |
auto, always, or never; auto follows the system preference |
mode defaults to physics and is optional. shadowResolution and duration remain accepted to compile v2 clients but are ignored.
Physics
| Option | Default | Use |
|---|---|---|
gravity |
1.3 |
Gravity multiplier |
mass |
1.08 |
Base mass |
startingHeight |
7.6 |
Starting height |
spinForce |
5.8 |
Initial spin |
throwForce |
6.4 |
Throw energy |
aggressiveThrowChance |
0.12 |
Seeded chance of a higher-energy throw |
colliderScale |
1.02 |
Polyhedron collider scale |
friction / restitution |
0.54 / 0.29 |
Friction and bounce |
linearDamping / angularDamping |
0.10 / 0.08 |
Damping |
settleTimeout |
4200 ms |
Settling window; never decides the result |
wallBounceChance remains a deprecated alias for aggressiveThrowChance. physicsWasmUrl is ignored: the v3 engine is JavaScript and uses no Havok.
Timeline and callbacks
timeline.enabled defaults to true, maxEvents to 500, maxDurationMs to 12000, and phaseGapMs to 180. Effects include explode, reroll, unique, compound, penetrate, keep, drop, success, failure, neutral, and criticals. explode.origin is source or edge; reroll.style and unique.style are hop, edge, or spin. When the budget is exceeded, the result marks degraded: true and presents the final state as a flat roll.
Callbacks: onCollision, onThemeConfigLoaded, onThemeLoaded, and onTimelineProgress. onCollision receives optional IDs for the two bodies and force; onTimelineProgress receives revealed dice and completed event sequences at each stage.
Skins, particles, and glow
const viewer = new DiceResultViewer({
container: '#dice-stage',
skin: {
texture: '/skins/marble.webp',
scale: 1,
blend: 'multiply',
opacity: 0.8,
labels: 'auto'
},
particles: { preset: 'sparkle', intensity: 1 },
glow: { color: '#ff4f93', intensity: 0.7, light: true }
})
skin projects an image over color materials; numbers and symbols remain above it. blend accepts normal, multiply, screen, and overlay. Particles can use one of 15 presets or a custom effect with emitters for trail, ground, impact, collision, settle, aura, explode, and critical. glow controls color, intensity, light on the table, and pulsing. All three are optional (null by default) and can be changed with updateOptions().
A DiceLook combines color, skin, particles, and glow in a versioned object that can be saved and applied:
import { createDiceLook } from '@erpg/dice3dview'
const look = createDiceLook({
name: 'Pink',
themeColor: '#e60049',
particles: { preset: 'sparkle' },
glow: { intensity: 0.7 }
})
await viewer.applyLook(look)
viewer.playParticles('critical')
The format uses format: 'dice3dview-look' and version: 1. applyLook() validates before changing the viewer; omitted parts are turned off. playParticles() accepts impact, collision, settle, aura, explode, and critical, optionally with { dice: ['id'] }.
Assets, lifecycle, and cancellation
Publish dist/assets/dice-box/ at /assets/dice-box/; v3 needs themes, models, and textures but no havok/. For external themes, configure externalThemes and the asset server’s CORS policy. Changing container, id, antialias, or the root of already-loaded assets requires a new instance.
import { isDisplayCancelledError } from '@erpg/dice3dview'
try {
await viewer.display(request)
} catch (error) {
if (isDisplayCancelledError(error)) return
throw error
}
Technical sources: official v3 API and public types.