Web Player API
The Cavalry Web Player and API is currently in beta and so subject to change.
Intro
The Cavalry Web Player can be used for playing and interacting with Cavalry Scenes in web browsers. This document provides a complete integration guide and API reference.
The Web Player consists of two main parts.
- WebAssembly Runtime
- JavaScript Module
The JavaScript module is used to intantiate a Web Player. The Web Player consists of 4 aspects.
- Module — Instance of the imported
CavalryWasmmodule - Player — The Cavalry player that controls playback, asset management, etc (similar to Cavalry's Scripting API)
- Canvas — The
canvaselement that functions as the viewport - Surface — The WebGL surface that renders the scene to the
canvas
Basic Example
// Import the Cavalry module
const wasm = await import("../wasm-lib/CavalryWasm.js")
// Initialise the module
const Module = await wasm.default({
// `locateFile` is required for the module to load the wasm files
locateFile: (path) => `../wasm-lib/${path}`,
print: (text) => console.log(text),
printErr: (text) => console.error(text),
})
// Load a scene file
const response = await fetch("scene.cv")
const sceneData = await response.arrayBuffer()
Module.FS.writeFile("scene.cv", new Uint8Array(sceneData))
// Create a player instance
const player = Module.Cavalry.MakeWithPath("scene.cv")
// Read the scene resolution
const scene = player.getSceneResolution()
const canvas = document.getElementById("canvas")
canvas.width = scene.width
canvas.height = scene.height
// Set up a rendering surface
const surface = Module.makeWebGLSurfaceFromElement(
canvas,
scene.width,
scene.height,
)
// Render the current frame
player.render(surface)
// Use `requestAnimationFrame` for efficient playback
let animationFrameId = 0
const runPlaybackLoop = () => {
const tick = (timestamp) => {
// `tick` advances the frame based on the elapsed time
const status = player.tick(surface, timestamp);
// Optionally update the UI when a frame changes
if (status.frameChanged) {
console.log("Frame", status.currentFrame);
}
animationFrameId = requestAnimationFrame(tick)
}
animationFrameId = requestAnimationFrame(tick)
}
runPlaybackLoop()
player.play()
// To stop playback
// player.stop();
// cancelAnimationFrame(animationFrameId);
Module
Import the Module using:
// Import the Cavalry module
const wasm = await import("../wasm-lib/CavalryWasm.js")
// Initialise the module
const Module = await wasm.default({
// `locateFile` is required for the module to load the wasm files
locateFile: (path) => `../wasm-lib/${path}`,
// Logs diagnostic data to the console
print: (text) => console.log(text),
// Logs errors to the console
printErr: (text) => console.error(text),
})
Methods
makeWebGLSurface(canvasId:string, width:number, height:number) → SkSurface
Initialises a WebGL2 context and Skia surface using the specified canvas element.
Parameters:
canvasId: ID of the canvas HTML element.width: Width of the rendering surface.height: Height of the rendering surface.
Returns: Skia SkSurface bound to the WebGL context.
const surface = Module.makeWebGLSurface("canvas", 800, 600);
makeWebGLSurfaceFromElement(canvas:emscripten::val, width:number, height:number) → SkSurface
Initialises a WebGL2 context and Skia surface using the specified canvas element.
Parameters:
canvas: The canvas HTML element.width: Width of the rendering surface.height: Height of the rendering surface.
Returns: Skia SkSurface bound to the WebGL context.
const surface = Module.makeWebGLSurfaceFromElement(canvas, canvas.width, canvas.height);
loadFont(path:string, alias?:string) → string
Loads a font from the virtual file system into the runtime font registry.
Parameters:
path: Path to the font file (e.g.,.ttf,.otf,.woff,.woff2) in the virtual file system.alias(optional): Custom name to register the font under. If provided, the font is registered with this alias instead of its internal family name. This is useful for matching external naming systems (e.g., CSS font-family names).
Returns: The registered family name (either the alias if provided, or the font's internal family name).
// Load font using its internal family name
Module.FS.writeFile("Montserrat.ttf", fontBytes);
const familyName = Module.loadFont("Montserrat.ttf", "");
// familyName = "Montserrat" (from font's internal name)
// Load font with a custom alias
Module.FS.writeFile("AC-Compacta.woff", fontBytes);
const familyName = Module.loadFont("AC-Compacta.woff", "my-custom-id");
// familyName = "my-custom-id"
// Use the alias with other font APIs
const styles = player.getFontStyles("my-custom-id");
player.setAttributeFont("textShape#1", "font", { font: "my-custom-id", style: "Regular" });
getFontMetadata(familyName:string) → FontMetadata
Gets detailed metadata for a loaded font, including all available styles with CSS-compatible values.
Parameters:
familyName: The registered family name (fromloadFont()orqueryFonts()).
Returns: A FontMetadata object containing font name variants and style information.
// Load a font
const familyName = Module.loadFont("Inter.ttf", "");
// Get metadata
const metadata = Module.getFontMetadata(familyName);
console.log("Family:", metadata.registeredFamily);
console.log("Styles:", metadata.styles);
// Use style info for CSS mapping
metadata.styles.forEach(style => {
console.log(`${style.name}: weight=${style.weight}, slant=${style.slant}`);
});
// Output: "Regular: weight=400, slant=normal"
// Output: "Bold: weight=700, slant=normal"
// Output: "Italic: weight=400, slant=italic"
loadFontData(url:string)
Loads font data from a given URL and loads it into the runtime font registry.
Parameters
url(string): The URL from which the font data is fetched.
// Load a font from a URL
Module.loadFontData("/url/to/font.ttf");
Cavalry
Create an instance using:
const player = Module.Cavalry.Make("scene.cv");
Attributes
pendingAssets
When a scene is loaded via MakeWithPath, its assets will be in the pendingAssets array. The assets can then be loaded by looping over the array.
See Loading Assets for an implementation example.
const player = Module.Cavalry.MakeWithPath("scene.cv");
console.log(Module.pendingAssets)
specialHTMLTargets
Adding a canvas element to this object allows the CavalryWasm module to find the canvas when it exists in the shadow DOM.
See Shadow DOM.
const id = "my-canvas"
const canvas = document.createElement("canvas")
canvas.id = id
Module.specialHTMLTargets[id] = canvas
Methods
Make(scenePath?:string) → Cavalry
Constructs and returns a new instance of the Cavalry class.
Parameters:
scenePath(optional): Path to the scene file in the virtual filesystem. Defaults to"scene.cv"for backward compatibility.
// Load default scene.cv
const player = Module.Cavalry.Make();
// Load a specific scene file
const player = Module.Cavalry.Make("myAnimation.cv");
MakeWithPath(scenePath:string) → Cavalry
Constructs and returns a new instance of the Cavalry class.
Alternative to Make.
Parameters:
scenePath: Path to the scene file in the virtual filesystem.
// Load a specific scene file
const player = Module.Cavalry.MakeWithPath("scene.cv");
render(surface:SkSurface)
Renders the current frame to the specified WebGL Skia surface.
player.render(surface);
getSceneResolution() → Resolution
Retrieves the resolution of the current composition.
Returns: An object of type Resolution.
const resolution = player.getSceneResolution();
console.log(resolution.width, resolution.height);
replaceImageAsset(filePath:string, assetId:string)
Replaces the image asset at assetId with the image from the given path.
Module.FS.writeFile("image.png", imageBytes);
player.replaceImageAsset("image.png", "asset#2");
player.render(surface);
replaceImageAssetData(assetId:string, image:string)
Replaces the image asset at assetId with the image from the virtual filesystem.
player.replaceImageAssetData("asset#2", "test.png");
player.render(surface);
replaceFontAsset(filePath:string, assetId:string)
Replaces the font asset at assetId with the font from the virtual filesystem.
player.replaceFontAsset("font.ttf", "asset#2");
player.render(surface);
replaceCSVAsset(filePath:string, assetId:string)
Replaces the CSV asset at assetId with the CSV from the given path.
The light and fast Web CSV Parser is rudimentary compared to the large and full-featured parser that ships with Cavalry itself. Custom delimiters and quotes are unsupported at this time. Please let us know if you"re having issues with a CSV file.
Module.FS.writeFile("test.csv", csvBytes);
player.replaceCSVAsset("text.csv", "asset#2");
player.render(surface);
replaceCSVAssetData(assetId:string, csvData:string)
Replaces the CSV asset at assetId with the CSV Data.
Note - the light and fast Web CSV Parser is rudimentary compared to the large and full-featured parser that ships with Cavalry itself. Custom delimiters and quotes are unsupported at this time. Please let us know if you're having issues with a CSV file.
player.replaceCSVAssetData("asset#2", "test.csv");
player.render(surface);
replaceExcelAsset(filePath:string, assetId:string)
Replaces the Excel asset at assetId with the Excel asset from the virtual filesystem.
player.replaceExcelAsset("sheet.xlsx", "asset#2");
player.render(surface);
replaceExcelAssetData(assetId:string, csvData:string)
Replaces the CSV asset at assetId with the CSV Data.
player.replaceExcelAssetData("asset#2", "test.csv");
player.render(surface);
replaceSVGAsset(filePath:string, assetId:string)
Replaces the SVG asset at assetId with the SVG asset from the virtual filesystem.
player.replaceExcelAsset("image.svg", "asset#2");
player.render(surface);
replaceSVGAssetData(assetId:string, svgData:string)
Replaces the SVG asset at assetId with the SVG Data.
player.replaceExcelAssetData("asset#2", "image.svg");
player.render(surface);
replaceGoogleSheet(sheetUrl:string, assetId:string)
Google Sheets are loaded automatically. Use replaceGoogleSheet to replace a Google Sheet asset at runtime.
Set the Google Sheet's sharing permissions to "Anyone with the link" to avoid errors.
const sheetId = "UNIQUE_ID_OF_GOOGLE_SHEET"
const url = `https://docs.google.com/spreadsheets/d/${sheetId}`
player.replaceGoogleSheet(url, assetId);
getActiveComp() → string
Returns the ID of the currently active composition.
const compId = player.getActiveComp();
getInConnection(layerId:string, attrId:string) → string
Returns the input connection to an Attribute. An empty string is returned if there's no input on the Attribute in question.
const path = player.getInConnection("basicShape#2", "scale");
disconnect(fromLayerId:string, fromAttrId:string, toLayerId:string, toAttrId:string)
Remove connections between attributes. This returns an empty string when the connection is removed.
player.disconnect("basicShape#2", "position", "basicShape#2", "scale");
const path = player.getInConnection("basicShape#2", "scale");
console.log("Connection for Scale Attribute after disconnect is", path);
setCompTimeMultiplier(multiplier:number)
Sets the time multiplier for a Composition. Setting this to 2.0 will mean a Composition plays back twice as quickly.
player.setCompTimeMultiplier(0.5);
setActiveComp(compId:string)
Sets the active composition by ID. If the composition is not found, a warning is logged.
setAttributeViaStringify(layerId:string, jsonString:string)
Sets one or more attributes on a layer by passing a JSON string.
Parameters:
layerId: ID of the layer to modify.jsonString: JSON string mapping attribute names to values.
const color = JSON.stringify({ backgroundColor: { r: 255, g: 0, b: 128 } });
player.setAttributeViaStringify(player.getActiveComp(), color);
player.render(surface);
const playbackStep = JSON.stringify({ playbackStep: 2 });
player.setAttributeViaStringify(player.getActiveComp(), playbackStep);
setAttribute(layerId:string, attrId:string, value:any)
Sets an attribute on a layer. The value type is automatically detected and converted to the appropriate C++ type.
Parameters:
layerId: ID of the layer to modify.attrId: Attribute to modify.value: The value to set. Supported types:- Numbers: Automatically detected as
double,int, orenumbased on attribute type - Strings: For text and richText attributes
- Booleans: For boolean attributes
- Arrays:
[x, y]for 2D vectors,[x, y, z]for 3D vectors,[r, g, b]or[r, g, b, a]for colours - Objects:
{x, y}or{x, y, z}for vectors,{r, g, b}or{r, g, b, a}for colours
- Numbers: Automatically detected as
// Numbers (auto-detected as double, int, or enum based on attribute type)
player.setAttribute("textShape#1", "fontSize", 24.5);
player.setAttribute("duplicator#1", "count", 10);
player.setAttribute("dropdown#1", "alignment", 2);
// Strings
player.setAttribute("textShape#1", "text", "Hello World!");
// Booleans
player.setAttribute("shape#1", "visible", true);
// 2D vectors (array or object syntax)
player.setAttribute("shape#1", "position", [100, 200]);
player.setAttribute("shape#1", "scale", {x: 1.5, y: 2.0});
// 3D vectors (array or object syntax)
player.setAttribute("camera#1", "position", [10, 20, 30]);
player.setAttribute("transform#1", "rotation", {x: 0, y: 45, z: 0});
// Colours (array or object syntax, values 0-255)
player.setAttribute("shape#1", "fillColor", [255, 0, 0, 255]);
player.setAttribute("shape#1", "material.materialColor", {r: 128, g: 64, b: 255, a: 255});
// Composition background colour
player.setAttribute(player.getActiveComp(), "backgroundColor", {r: 30, g: 30, b: 30});
getAttribute(layerId:string, attrId:string) → any
Gets an attribute value from a layer. The return type depends on the attribute type.
Parameters:
layerId: ID of the layer to query.attrId: Attribute to retrieve.
Returns: The attribute value. Return types:
- double/int/enum:
number - bool:
boolean - string/richText:
string - double2/int2:
[x, y]array - double3/int3:
[x, y, z]array - color:
{r, g, b, a}object with values 0-255
// Numbers
const fontSize = player.getAttribute("textShape#1", "fontSize");
const count = player.getAttribute("duplicator#1", "count");
// Strings
const text = player.getAttribute("textShape#1", "text");
// Booleans
const isVisible = player.getAttribute("shape#1", "visible");
// 2D vectors (returns array)
const position = player.getAttribute("shape#1", "position");
console.log(`Position: ${position[0]}, ${position[1]}`);
// 3D vectors (returns array)
const rotation = player.getAttribute("transform#1", "rotation");
console.log(`Rotation: ${rotation[0]}, ${rotation[1]}, ${rotation[2]}`);
// Colours (returns object)
const color = player.getAttribute("shape#1", "material.materialColor");
console.log(`Color: R=${color.r}, G=${color.g}, B=${color.b}, A=${color.a}`);
// Convert colour to CSS hex
const hex = "#" + [color.r, color.g, color.b]
.map(x => x.toString(16).padStart(2, "0"))
.join("");
getAttributeViaStringify(layerId:string, attrId:string) → string
Gets any attribute value as a JSON string. This is a fallback method for complex attribute types that don't have specific getters.
Parameters:
layerId: ID of the layer to query.attrId: Attribute to retrieve.
Returns: JSON string representation of the attribute value.
const jsonString = player.getAttributeViaStringify("basicShape#1", "customData");
const data = JSON.parse(jsonString);
getAttributeName(layerId:string, attrId:string) → string
Gets the name of an Attribute. Attribute names can be set in Cavalry by right-clicking on an Attribute in the Attribute Editor and selecting "Rename...".
Parameters:
layerId: ID of the layer to query.attrId: Attribute to retrieve.
Returns: The name of the attribute or the attribute id.
const name = player.getAttributeName("basicShape#1", "position");
getAttributeDefinition(layerId:string, attrId:string) → AttributeDefinition
Gets detailed metadata about an attribute including its type, constraints, default values, and structure. This is particularly useful for building dynamic user interfaces that need to understand attribute characteristics.
Parameters:
layerId: ID of the layer to query.attrId: Attribute to inspect.
Returns: An AttributeDefinition object containing comprehensive attribute metadata.
// Get definition for a numeric attribute with constraints
const sizeDef = player.getAttributeDefinition("pixelateFilter#1", "size");
console.log(`Type: ${sizeDef.type}`);
console.log(`Default: ${sizeDef.defaultValue}`);
console.log(`Min: ${sizeDef.numericInfo.hardMin}, Max: ${sizeDef.numericInfo.hardMax}`);
// Use definition to create appropriate UI
if (sizeDef.type === "int" && sizeDef.numericInfo.hasHardMin && sizeDef.numericInfo.hasHardMax) {
// Create slider with proper min/max values
const slider = document.createElement("input");
slider.type = "range";
slider.min = sizeDef.numericInfo.hardMin;
slider.max = sizeDef.numericInfo.hardMax;
slider.value = JSON.parse(sizeDef.defaultValue);
}
AttributeDefinition Properties:
interface AttributeDefinition {
attrId: string; // Attribute id
type: string; // Data type (int, double, bool, string, Color, etc.)
prefix: string; // UI prefix text (e.g X, or R)
placeholder: string; // UI placeholder text for string attributes
defaultValue: string; // JSON-serialized default value
isArray: boolean; // Whether this is an array attribute
isCompound: boolean; // Whether this has child attributes
isDynamic: boolean; // Whether this is dynamically created
isAttrReadOnly: boolean; // Whether this is read-only
allowsConstrainProportions: boolean; // Whether proportions can be constrained
multiline: boolean; // Whether text should be multiline
numericInfo: NumericInfo; // Numeric constraint information
enumValues: number[]; // Enum option values
children: AttributeDefinition[]; // Child attribute definitions
}
interface NumericInfo {
hardMin: number; // Hard minimum constraint
hardMax: number; // Hard maximum constraint
softMin: number; // Soft minimum constraint
softMax: number; // Soft maximum constraint
step: number; // Step increment
hasHardMin: boolean; // Whether hardMin is set
hasHardMax: boolean; // Whether hardMax is set
hasSoftMin: boolean; // Whether softMin is set
hasSoftMax: boolean; // Whether softMax is set
hasStep: boolean; // Whether step is set
}
getControlCentreAttributes(compId:string) → [string]
Gets a list of all Control Centre Attribute paths for a given Composition.
Parameters:
compId: ID of the composition to query (typically obtained fromgetActiveComp()).
Returns: Array of strings representing attribute paths in the format "layerId.attributeId".
// Get active composition and its control centre attributes
const activeComp = player.getActiveComp();
const attributes = player.getControlCentreAttributes(activeComp);
console.log("Control Centre Attributes:", attributes);
// Output example: [ "pixelateFilter#1.size", "pixelateFilter#1.outline", "pixelateFilter#1.borderColor" ]
// Generate dynamic UI for each Attribute
attributes.forEach((attrPath) => {
const [layerId, attrId] = attrPath.split(".");
// Get attribute definition to determine UI type
const definition = player.getAttributeDefinition(layerId, attrId);
// Create appropriate control based on type
switch (definition.type) {
case "int":
// Create integer input or slider
break;
case "bool":
// Create checkbox
break;
case "color":
// Create color picker
break;
// ... handle other types
}
});
getFontAxisInfo(fontName:string, fontStyle:string) → FontAxisInfo
Retrieves variable font axis information for a given font. Variable fonts contain adjustable axes (such as weight, width, slant) that allow fine-grained control over the font's appearance. This method returns metadata about each available axis including its name, tag, range, and current value.
Parameters:
fontName: The name of the font family (e.g., "Roboto", "Inter").fontStyle: The style of the font (e.g., "Regular", "Bold").
Returns: An array of FontAxisInfo objects, one for each variable font axis. Returns an empty array if the font is not a variable font or has no axes.
// Query available fonts and their styles
const fonts = player.queryFonts();
const styles = player.getFontStyles(fonts[0]);
// Get axis information for a variable font
const axisInfo = player.getFontAxisInfo("Roboto Flex", "Regular");
if (!axesInfo.length) {
console.warn("This font has no variable axes (not a variable font)");
} else {
console.log(`Font has ${axisInfo.length} variable axes`);
console.log(axisInfo)
// Create sliders for each axis
axisInfo.forEach(axis => {
const slider = document.createElement("input");
slider.type = "range";
slider.min = axis.minValue;
slider.max = axis.maxValue;
slider.value = axis.currentValue;
slider.addEventListener("input", (e) => {
// Update font axis value when slider changes
console.log(`${axis.axisName} set to ${e.target.value}`);
})
})
}
fetchAndProcess(zipUrl:string) → Promise<void>
Fetches and processes a ZIP file, extracting its contents and creating an image sequence asset in the player.
Parameters
zipUrl(string): The URL of the ZIP file to fetch and process.
await player.fetchAndProcess("/url/to/ImageSequence.zip");
Playback
The Web Player provides a native playback manager that handles frame timing and rendering.
Playback Modes
Two playback modes are available:
- Accurate (default): Wall-clock based timing that skips frames to maintain correct timing. Best for audio synchronisation and real-time playback.
- Smooth: Shows every frame in sequence, but may run slower than real-time on complex scenes. Best for frame-accurate preview.
Basic Playback Pattern
// Start playback
player.play();
// Run the playback loop
function runPlaybackLoop() {
const tick = (timestamp) => {
if (!player.isPlaying()) {
return;
}
const status = player.tick(surface, timestamp);
// Update UI if needed
if (status.frameChanged) {
console.log(status)
}
requestAnimationFrame(tick);
};
requestAnimationFrame(tick);
}
runPlaybackLoop();
// Stop playback
player.stop();
play()
Starts playback from the current frame position. Call this before starting the requestAnimationFrame loop.
player.play();
runPlaybackLoop();
stop()
Stops playback. The current frame position is preserved, so calling play() again will resume from where playback stopped.
player.stop();
cancelAnimationFrame(animationFrameId);
toggle()
Toggles playback state. If playing, stops playback. If stopped, starts playback.
player.toggle();
console.log(player.isPlaying() ? "Stop" : "Play");
isPlaying() → boolean
Returns whether playback is currently active.
console.log("Playback is running?", player.isPlaying());
tick(surface:SkSurface, timestampMs:number) → PlaybackStatus
Advances playback based on elapsed time and renders the current frame. This should be called from the requestAnimationFrame callback.
Parameters:
surface: The Skia surface to render to.timestampMs: The timestamp fromrequestAnimationFrame(DOMHighResTimeStamp).
Returns: A PlaybackStatus object containing the current playback state.
const tick = (timestamp) => {
if (!player.isPlaying()) {
return;
}
const status = player.tick(surface, timestamp);
if (status.frameChanged) {
console.log("Frame", status.currentFrame);
}
requestAnimationFrame(tick);
}
setFrame(frame:number)
Sets the current frame. The frame number is clamped to the valid playback range (between getStartFrame() and getEndFrame()).
Note: If playback is running, stop the playback before setting the frame, then optionally restart it.
// Seek to a specific frame
const wasPlaying = player.isPlaying();
player.stop();
player.setFrame(50);
player.render(surface);
if (wasPlaying) {
player.play();
runPlaybackLoop();
}
getFrame() → number
Returns the current frame number.
const currentFrame = player.getFrame();
console.log("Current frame", currentFrame);
getStartFrame() → number
Returns the starting frame of the current composition's playback range.
const startFrame = player.getStartFrame();
console.log(startFrame);
getEndFrame() → number
Returns the ending frame of the current composition's playback range.
const endFrame = player.getEndFrame();
console.log(endFrame);
getFPS() → number
Returns the frames per second (FPS) of the current composition.
const fps = player.getFPS();
console.log(`Scene runs at ${fps} FPS`);
getActualFPS() → number
Returns the measured playback FPS during active playback. This is calculated over a 1-second window and can be used to monitor playback performance.
const actualFps = player.getActualFPS();
console.log(`${actualFps.toFixed(1)} FPS`);
setPlaybackMode(mode:number)
Sets the playback mode.
Parameters:
mode:0for Accurate mode (default),1for Smooth mode.
// Use Smooth mode for frame-accurate preview
player.setPlaybackMode(1);
// Use Accurate mode for real-time playback
player.setPlaybackMode(0);
getPlaybackMode() → number
Returns the current playback mode. 0 = Accurate, 1 = Smooth.
const mode = player.getPlaybackMode();
console.log(mode === 0 ? "Accurate" : "Smooth");
setLoop(loop:boolean)
Sets whether playback should loop when reaching the end frame.
Parameters:
loop:trueto enable looping (default),falseto stop at the end.
player.setLoop(false);
isLooping() → boolean
Returns whether playback looping is enabled.
console.log("Playback will loop?", player.isLooping());
incrementFrame()
Manually advances to the next frame. This is a low-level method for manual frame control - for most use cases, use tick() instead which handles timing automatically.
player.incrementFrame();
player.render(surface);
Data Types
Resolution
interface Resolution {
width: number;
height: number;
}
Represents scene or canvas resolution.
ColorRGBA
interface ColorRGBA {
r: number; // Red component (0-255)
g: number; // Green component (0-255)
b: number; // Blue component (0-255)
a: number; // Alpha component (0-255)
}
Represents an RGBA colour value with components in the 0-255 range. Returned by getAttribute() for colour attributes.
const color = player.getAttribute("shape#1", "fillColor");
// Convert to CSS hex colour
const hex = "#" + [color.r, color.g, color.b]
.map(x => x.toString(16).padStart(2, "0"))
.join("");
// Set a colour using an object
player.setAttribute("shape#1", "fillColor", {r: 255, g: 128, b: 0, a: 255});
Double2
interface Double2 {
x: number; // X component
y: number; // Y component
}
Represents a 2D vector with double precision values. Note that getAttribute() returns 2D vectors as arrays [x, y] for convenience.
// getAttribute returns an array [x, y]
const position = player.getAttribute("shape#1", "position");
console.log(`Position: (${position[0]}, ${position[1]})`);
// Set a new position using array syntax
player.setAttribute("shape#1", "position", [position[0] + 10, position[1] + 20]);
// Or use object syntax
player.setAttribute("shape#1", "position", {x: 100, y: 200});
Double3
interface Double3 {
x: number; // X component
y: number; // Y component
z: number; // Z component
}
Represents a 3D vector with double precision values. Note that getAttribute() returns 3D vectors as arrays [x, y, z] for convenience.
// getAttribute returns an array [x, y, z]
const cameraPos = player.getAttribute("camera#1", "position");
console.log(`Camera at: (${cameraPos[0]}, ${cameraPos[1]}, ${cameraPos[2]})`);
// Set camera position using array syntax
player.setAttribute("camera#1", "position", [
cameraPos[0],
cameraPos[1],
cameraPos[2] + 100
]);
// Or use object syntax
player.setAttribute("camera#1", "position", { x: 10, y: 20, z: 30 });
FontMetadata
interface FontMetadata {
registeredFamily: string; // The name used for registration (use with getFontStyles)
registeredStyle: string; // Default style name
fullName: string; // Full font name from the font file
preferredFamily: string; // Preferred family name (may differ from registeredFamily)
preferredSubfamily: string; // Preferred subfamily name
postscriptName: string; // PostScript name
wwsFamilyName: string; // WWS (Weight Width Slope) family name
styles: FontStyleInfo[]; // All available styles with CSS-compatible values
}
Contains font metadata including name variants from the font's name table and all available styles. Returned by getFontMetadata().
const metadata = Module.getFontMetadata("Inter");
console.log(metadata.registeredFamily); // "Inter"
console.log(metadata.styles); // Array of FontStyleInfo objects
FontStyleInfo
interface FontStyleInfo {
name: string; // Style name (e.g., "Regular", "Bold Italic")
weight: number; // CSS font-weight: 100-900 (400=normal, 700=bold)
slant: string; // CSS font-style: "normal", "italic", or "oblique"
width: number; // CSS font-stretch: 1-9 (5=normal, 3=condensed, 7=expanded)
}
Represents a font style with CSS-compatible values. Use these values to map Cavalry font styles to CSS properties.
const metadata = Module.getFontMetadata("Roboto");
metadata.styles.forEach(style => {
// Create CSS font-face rules or match styles
console.log(`@font-face { font-family: "${style.name}"; font-weight: ${style.weight}; font-style: ${style.slant}; }`);
});
FontAxisInfo
interface FontAxisInfo {
axisName: string; // Human-readable name (e.g., "Weight", "Width")
axisTag: string; // Four-character axis tag (e.g., "wght", "wdth")
minValue: number; // Minimum allowed value for this axis
maxValue: number; // Maximum allowed value for this axis
defaultValue: number; // Default value for this axis
currentValue: number; // Current value for this axis
}
Represents information about a variable font axis. Variable fonts can have multiple axes that allow fine-grained control over the font's appearance. Common axes include weight (wght), width (wdth), slant (slnt), and optical size (opsz).
// Get variable font axis information
const axisInfo = player.getFontAxisInfo("Inter", "Regular");
console.log(axisInfo)
PlaybackStatus
interface PlaybackStatus {
isPlaying: boolean; // Whether playback is currently active
currentFrame: number; // The current frame number
frameChanged: boolean; // Whether the frame changed since the last tick
actualFPS: number; // Measured playback FPS
}
Returned by tick() to provide information about the current playback state. Use frameChanged to determine when to update UI elements.
const status = player.tick(surface, timestamp);
if (status.frameChanged) {
console.log(status.currentFrame);
console.log("Frame", status.currentFrame);
}
if (status.actualFPS > 0) {
console.log(`${status.actualFPS.toFixed(1)} FPS`)
}
PlaybackMode
enum PlaybackMode {
Accurate = 0, // Wall-clock based, skips frames to maintain timing
Smooth = 1 // Shows every frame, may run slow
}
Defines how the playback manager advances frames:
- Accurate (0): Calculates the correct frame based on elapsed wall-clock time. If rendering is slow, frames will be skipped to maintain correct timing. Best for audio synchronisation.
- Smooth (1): Advances exactly one frame at a time when sufficient time has elapsed. Never skips frames, but playback may run slower than real-time on complex scenes.
// Check current mode
console.log(player.getPlaybackMode());
// Set to Smooth mode for frame-by-frame preview
player.setPlaybackMode(1);
// Set to Accurate mode for real-time playback
player.setPlaybackMode(0);
Events
The Cavalry Web Player automatically dispatches events for certain asset-related operations. These events allow automatic asset loading and other integration scenarios.
cavalryAutoLoadAsset
Event Type: CustomEvent
Automatically dispatched when the Cavalry Web Player detects image or font assets in a scene file that need to be loaded from the web server into the virtual filesystem.
Event Detail:
interface CavalryAutoLoadAssetDetail {
type: "image" | "font" | "csv" | "svg" | "excel" | "googlesheet";
assetId: string; // Asset ID from the scene file (e.g., "asset#2")
filename: string; // Extracted filename (e.g., "3.jpg" or "font.ttf")
}
// Listen for automatic asset loading events
window.addEventListener("cavalryAutoLoadAsset", async (event) => {
console.log("Auto-loading asset:", event.detail);
const path = "/path/to/assets/"
if (event.detail.type === "image") {
const { assetId, filename } = event.detail;
const response = await fetch(path + filename);
if (!response.ok) {
throw new Error(`Could not find image ${filename}`);
}
const imageData = await response.arrayBuffer();
// Write to virtual filesystem
Module.FS.writeFile(filename, new Uint8Array(imageData));
// Load the asset
player.replaceImageAsset(filename, assetId);
// Re-render to show the image
player.render(surface);
console.log(`Auto-loaded image: ${filename} for ${assetId}`);
}
if (event.detail.type === "font") {
const { assetId, filename } = event.detail;
const response = await fetch(path + filename);
if (!response.ok) {
throw new Error(`Could not find font ${filename}`);
}
const fontData = await response.arrayBuffer();
// Write to virtual filesystem
Module.FS.writeFile(filename, new Uint8Array(fontData));
// Load the font into the font registry
Module.loadFont(filename);
// Re-render to show font changes
player.render(surface);
console.log(`Auto-loaded font: ${filename} for ${assetId}`);
}
});
How it Works:
- When a Cavalry scene (.cv file) is loaded, the Web Player scans for asset definitions
- File assets with
"type": "file"and supported extensions trigger this event:- Image files: png, jpg, jpeg, exr, webp
- Font files: ttf, otf, woff, woff2
- The event provides the asset ID and extracted filename for the application to handle
- The event handler should fetch the file from the web server and write it to the virtual filesystem
- Then call the appropriate API to load the asset:
- Images:
replaceImageAsset(filename, assetId) - Fonts:
Module.loadFont(filename)
- Images:
Automatic Loading:
- Google Sheets assets are handled automatically and don't require this event
- Image and font assets require JavaScript handling because they need to be fetched from the web server
File Path Resolution: The Web Player extracts filenames from Cavalry's internal paths, e.g:
"@assets/image.jpg"→"image.jpg""@assets/Changa_One/ChangaOne-Regular.ttf"→"ChangaOne-Regular.ttf""subfolder/font.ttf"→"font.ttf"
The event handler should try common locations like root directory (./filename) and Assets folder (./Assets/filename).
Example Implementations:
- Image loading: See the Image Asset demo in
src/wasm/cavalry-web-player/image-asset/ - Font loading: See the Custom Font demo in
src/wasm/cavalry-web-player/custom-font/