Skip to content
Apexify.jsDocs
Apexify.js version v5.4.5

Advanced / Scene · guide

Scene — types & SceneBuilder

Current Apexify.js 6.0.0 documentation for Scene — types & SceneBuilder.

apexify.jsRuntime: nodeCURRENTSince 6.0.0

SceneRenderInput

Used by renderScene, renderSceneToGIF, and renderSceneToVideoFrames.

FieldTypeNotes
widthnumberPositive root width in pixels.
heightnumberPositive root height in pixels.
backgroundSceneBackground = Omit<CanvasConfig, "width" | "height">Paint configuration only; root dimensions come exclusively from width / height.
layersSceneLayer[]Deterministic bottom → top paint order.

SceneRenderResult is a PNG Buffer.


SceneRenderOptions

Passed to renderScene / SceneBuilder.render and through GIF/video helpers as sceneRender.

FieldBehavior
validateDeprecated compatibility property. Safety validation is mandatory; this value is ignored and cannot disable validation.
maxSurfaceDepthOptional stricter nesting cap. It may not exceed the configured runtime maxSceneDepth (default 32).
resolveAssetRefsrenderScene / scene→GIF / scene→video default true. SceneBuilder.render defaults false unless you opt in.

There is no trusted-input validation bypass. Call painter.validateSceneRenderInput(input, options?) when you want an explicit preflight before a larger workflow; the renderer validates again at its own boundary.

SOURCEtypescript
typescript
Studio
import { ApexPainter } from "apexify.js"; const painter = new ApexPainter();const input = { width: 800, height: 600, layers: [] }; painter.validateSceneRenderInput(input, { maxSurfaceDepth: 8 });await painter.renderScene(input);

Validation covers dimensions, aggregate scene pixel budget, total layers/surfaces/images/text/charts/remote assets, total text content, nested depth, image/text domain validation, finite transforms, and opacity ranges.


SceneBuilder

createScene() returns a mutable builder around scene descriptions with copy-on-ingress and snapshot isolation.

When you pass a background, layer object, nested array/object, or Buffer into the builder, the builder captures an isolated composition copy. Later mutation of the caller-owned value does not silently change the builder.

Background

MethodReturnsRole
setBackground(bg)thisReplace the root background with a copied value.
clearBackground()thisRemove the root background.

Stack editing

MethodReturnsRole
addLayer(layer)thisAppend one copied layer.
addLayers(layers)thisAppend copied layers in order.
insertLayer(index, layer)thisInsert at an exact valid index. index === layerCount appends.
insertLayers(index, layers)thisInsert several layers preserving order.
insertBefore(index, layer)thisInsert directly below the layer currently at index.
insertAfter(index, layer)thisInsert directly above the layer currently at index.
replaceLayer(index, layer)thisReplace exactly one layer.
replaceLayers(layers)thisReplace the whole stack.
removeLayer(index)thisRemove exactly one layer.
moveLayer(fromIndex, toIndex)thisReorder one layer.
clearLayers()thisEmpty the stack.

Invalid indices throw ApexifyInputError; indices are not silently clamped.

Snapshot & render

MethodReturnsRole
toRenderInput()SceneRenderInputDeep composition snapshot, including copied Buffers. Mutating it does not mutate the builder.
render(options?)Promise<Buffer>Renders a fresh isolated snapshot. $ asset resolution is opt-in on the builder.

Read-only builder properties: width, height, and layerCount.


createScene overloads

Config object

SOURCEtypescript
typescript
Studio
const builder = painter.createScene({  width: 1920,  height: 1080,  background: { colorBg: "#020617" },  layers: [    {      type: "text",      texts: { text: "Title", x: 80, y: 100, fontSize: 72, color: "#f8fafc" },    },  ],}); const png = await builder.render();

Width + height

SOURCEtypescript
typescript
Studio
const png = await painter  .createScene(800, 600)  .setBackground({ colorBg: "#f1f5f9" })  .addLayers([    { type: "text", texts: { text: "Step 1", x: 24, y: 40, fontSize: 28, color: "#0f172a" } },    { type: "text", texts: { text: "Step 2", x: 24, y: 90, fontSize: 20, color: "#334155" } },  ])  .render();

Passing only numeric width is invalid; numeric construction requires both width and height.


Editor-style ordering example

SOURCEtypescript
typescript
Studio
const card = painter.createScene(400, 300).setBackground({ colorBg: "#0f172a" }); card.addLayer({  type: "text",  texts: { text: "Back", x: 8, y: 20, fontSize: 12, color: "#94a3b8" },});card.insertBefore(0, {  type: "text",  texts: { text: "Watermark", x: 200, y: 260, fontSize: 11, color: "#64748b" },});card.insertAfter(0, {  type: "text",  texts: { text: "Middle", x: 80, y: 80, fontSize: 14, color: "#e2e8f0" },});card.moveLayer(2, 0);card.replaceLayer(1, {  type: "text",  texts: { text: "Replacement", x: 80, y: 120, fontSize: 14, color: "#fff" },}); await card.render();

Every render recomposites the current stack; stack methods edit descriptions/order rather than caching a partially painted raster.


toRenderInput() → other workflows

SOURCEtypescript
typescript
Studio
import type { SceneRenderInput } from "apexify.js"; const builder = painter  .createScene({ width: 1080, height: 1080, background: { colorBg: "#111827" } })  .addLayers([]); const input: SceneRenderInput = builder.toRenderInput(); const png = await painter.renderScene(input);// await painter.renderSceneToGIF(input, { options: { ... } });// await painter.renderSceneToVideoFrames(input, { options: { ... } });

Next steps