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

Architecture / Migration · migration

Changelog

Current Apexify.js 6.0.0 documentation for Changelog.

apexify.jsRuntime: nodeCURRENTSince 6.0.0

All notable changes to Apexify.js will be documented in this file.

The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.

Version history is mirrored here for the docs site. For the canonical file (including exact diffs, contributor tables, and full history), see the CHANGELOG in the Apexify.js repository.

<!-- changelog-versions -->

[6.0.0] - Unreleased

⚠️ Breaking / runtime contract

  • Node.js 22.x, 24.x, and 26.x are the supported runtime matrix for Apexify.js 6.
  • Scene validation is mandatory before raster allocation. The legacy SceneRenderOptions.validate field is retained only for source compatibility and no longer disables validation; use maxSurfaceDepth only to make the configured scene-depth ceiling stricter for a render.
  • await painter.use(plugin) is the supported plugin lifecycle. Installation may be asynchronous and is not considered complete until the returned promise resolves.

🧩 Phase 6 — composition architecture

  • Scenes / SceneBuilder — deterministic bottom-to-top ordering, insertion/move/removal/replacement, copied ingress and isolated render snapshots, nested surfaces, aggregate scene budgets, and repeated-render determinism. Nested surfaces remain canvases until direct parent compositing rather than performing an unnecessary PNG encode/decode round trip.
  • Named assets — explicit duplicate rejection and replace* APIs; images/fonts/palettes plus JSON-like loadValue; dotted object/array lookup; whole-field Buffer/structured references; scalar embedded references; $$ escaping; cycle and unsafe-key rejection; failed replacement does not mutate the registry.
  • Templates — immutable definition/data snapshots, dotted placeholders, nullish-only defaults, visibility before placeholder resolution, deep id-based overrides, deterministic insertions, $asset resolution, flex/grid expansion, and normal scene validation on the resolved output. Explicit 0, false, and "" are preserved as values; they do not silently trigger defaults.
  • Components — badge, progressBar, avatar, card, and watermark all emit renderable SceneLayer[] with runtime validation for sizing/geometry and invalid inputs.
  • Plugins — duplicate/pending-name rejection, serialized asynchronous installs, transactional PluginHost registration rollback on throw/rejection, retry after failed install, installed-state inspection, and preservation of unrelated application registry writes during an async plugin failure.
  • Composition safety — shared cloning/resolution rejects cyclic graphs and prototype-pollution keys while preserving opaque runtime objects only where the generic composition pipeline intentionally treats them as opaque.

✅ Verification

  • Added a dedicated Phase 6 self-challenge, runtime/regression suites, golden rendering checks, and composition benchmarks.
  • Phase 6 is part of the mandatory package npm test gate alongside prior security, Phase 3, Phase 4, and Phase 5 regressions.
  • Package/release CI verifies Node 22/24/26, native ESM + CommonJS output, dual declaration modes, clean tarball consumer installs, clean prepack rebuilds, package contents, and production dependency audit.
  • Documentation build CI runs on Node 22/24/26 and the composition/scene guides were synchronized with the hardened 6.0 behavior.

[5.4.5] - 2026-05-13

✨ Added

Procedural audio — painter.createAudio
  • painter.createAudio on ApexPainter — server-side SFX synthesis (16-bit PCM WAV Buffer, no external sound files required).
  • preset(name, overrides?) — 39 built-in presets (laser, explosion, coin, jump, gameOver, whoosh, engine, …); overrides: volume, transpose, full layer overrides.
  • synth(options) / custom() — multi-layer sounds: waveforms (sine, square, sawtooth, triangle, noise, pink), ADSR, frequency sweeps, vibrato / tremolo, filters, partials, pan, per-layer delay/gain.
  • sequence({ events }) — timeline of presets or custom sounds at at (seconds) with per-event gain.
  • compose({ clips }) — one WAV from overlapping clips: at, duration, sourceStart, preset / sound / wav, pitch/volume/pan/fades, noise, filter, quality; optional postHighpassHz, noiseGateThreshold on the master mix.
  • mix(inputs) — simultaneous mix at t = 0, or timeline mix when clips include compose fields.
  • save(wav, path), listPresets(), presetNames.
  • Types: SynthPresetName, SynthSoundOptions, SynthSequenceOptions, SynthComposeOptions, … — apexify.js/types.
Video pipeline — videoPipeline()
  • painter.videoPipeline(source?) / painter.video.videoPipeline() — declarative edit stack for editor workflows; fewer redundant encodes than chaining many createVideo calls.
  • Layer types: source, trim, splice, text (full TextProperties), audio (files + createAudio presets / synth / sequence / WAV).
  • Upsert by id — same id replaces trim/splice/source; text / audio with the same id merge unless { replace: true }.
  • pipeline.render({ outputPath }) — compiles layers; returns passes count.
Video text overlays — createVideo({ addTextOverlay })
  • addTextOverlay — timed captions using the same TextProperties model as createText (fonts, gradients, glow, shadow, stroke, wrap, curved text, decorations, placement, …). Each overlays[] entry is a VideoTextOverlayClip: style + startTime, endTime, transitionIn / transitionOut, overlayOpacity.
  • Multiple overlays — overlapping time ranges at different x / y; later array items stack on top.
  • Transitions — fade, slideLeft / Right / Up / Down, zoomIn / zoomOut, bounce, aliases (fadeIn, slideIn, …); VideoTextTransition.custom for FFmpeg expression overrides.
  • Canvas render → transparent PNG per overlay → FFmpeg overlay chain.

🔧 Changed

  • AudioCreate / TemplateCreate facades on ApexPainter (same pattern as VideoCreate, GifCreate, SceneCreate).
  • createVideo({ addText }) and createVideo({ addAnimatedText }) — still available (FFmpeg drawtext) but deprecated; prefer addTextOverlay or videoPipeline().text().
  • Procedural SFX entry is painter.createAudio (aligned with createVideo, createCanvas, createGIF).
  • createVideo({ mixAudio }) when keepOriginalAudio: false: no silent anullsrc bed under overlays (fixes quiet/muddy SFX). Overlay-only mixes use amix normalize=0 plus light alimiter.
  • mixAudio buffers: temp files use correct extension (.wav / .mp4 / .mp3) from buffer magic bytes.

🐛 Fixed

  • mixAudio with WAV Buffer overlays previously written as .mp4, causing missing or silent audio tracks.
  • Space shooter demo: removed continuous ambience drone, tuned SFX levels, reliable mux from .wav, post-mux audio stream check on output MP4.

📚 Documentation


[5.4.4] - 2026-05-13

🔧 Changed

  • painter.image.resize (ResizeOptions.imagePath): string | Buffer → sharpFromResolvableInput — Buffer raster bytes; http(s): URLs (fetched); data:image/...;base64,... (**parameters allowed before ;base64,, e.g. charset); filesystem paths (**absolute / relative to process.cwd()).
  • painter.image.imgConverter: source string | Buffer — same resolution rules as resize (paths / URLs / data URLs / buffers).

📚 Documentation


[5.4.03] - 2026-05-13

Higher-level composition: templates (placeholders + named assets + flex layout), painter.assets, painter.components (preset SceneLayer[] snippets), painter.plugins, opt-in $ resolution on imperative ApexPainter APIs and batch/chain, prepareForRender, and SceneBuilder.render aligned with renderScene.

✨ Added (user-facing)

  • createTemplate(definition, options?) → TemplateHandle with render, toRenderInput — {{key}}, {{key | default}}, $ refs (optional custom resolveAssetRef); flex layout; visible; layer id; runtime overrides
  • painter.assets — loadImage, loadFont, loadPalette, resolve, clears / unregister helpers
  • painter.components — badge, progressBar, avatar, card, watermark (toLayers → SceneLayer[])
  • painter.plugins — use, get, has, remove; painter.use(plugin) (ApexifyPlugin, synchronous install)
  • prepareForRender(payload) — deep $ walk for JSON-like configs (same semantics as renderScene)
  • Optional trailing { resolveAssetRefs: true } on createCanvas, createImage, createText, measureText, createChart / comparison / combo, createGIF, animate, createVideo (off by default on those helpers; renderScene / scene steps in renderSceneToGIF / renderSceneToVideoFrames resolve by default)
  • createScene / SceneBuilder receives painter.assets’ resolver; SceneBuilder.render({ resolveAssetRefs: true }) when layers carry $…
  • batch(operations, opts?) / chain — resolveAssetRefs and resolve; resolve is required when resolveAssetRefs is true (assets.resolve is the usual default)

🔧 Changed / 🐛 Fixed

  • ApexPainter.use — synchronous install only (removed async catch that could hide failures)
  • SceneRenderOptions typing — resolveAssetRefs next to validate / maxSurfaceDepth

📚 Documentation

  • README — named assets ($name, $palette.key): scenes vs templates vs SceneBuilder, prepareForRender, batch / chain.
  • Composition (advanced) — Composition hub, templates, assets, components, plugins, imperative $ resolution.

[5.4.02] - 2026-05-13

Scene composition is easier to discover and safer by default: SceneBuilder is its own façade, fluent APIs can append or reorder layers in bulk, and renderScene / SceneCreator.render optionally validate before allocating the root canvas.

✨ Added

Scene — SceneBuilder
  • addLayers(layers) — append many layers in paint order; addLayer(layer) ↔ addLayers([layer])
  • Stack editing — insertLayer, insertLayers, removeLayer, moveLayer, replaceLayers, clearLayers, layerCount
  • toRenderInput() — plain SceneRenderInput with a copied layers array (renderScene, renderSceneToGIF, renderSceneToVideoFrames, tests)
Scene — validation
  • SceneRenderOptions (SceneCreator.render, ApexPainter.renderScene, SceneBuilder.render) — validate (default true) and maxSurfaceDepth (default 64); { validate: false } only when inputs are trusted
  • validateSceneRenderInput on apexify.js; ApexPainter.validateSceneRenderInput delegates to the same implementation
Scene — GIF / video helpers
  • renderSceneToGIF — gif.sceneRender (same options bucket as renderScene during rasterization)
  • renderSceneToVideoFrames — video.sceneRender forwarded for the raster step before FFmpeg

🔧 Changed

  • SceneBuilder separated from SceneCreator (builder façade vs compositor runtime)

📚 Documentation

  • Scene (advanced) — Scene overview: addLayers, stack editing, toRenderInput, validation, resolveAssetRefs, sceneRender on GIF/video.

[5.4.0] - 2026-05-13

Structural packaging pivot on 5.x: apexify.js publishes the modern façade (pre-5.4 used a broader legacy layout). Typical ApexPainter workflows stay familiar; where helpers hang on the instance changed.

✨ Highlights (user-facing)

  • apexify.js/types — import type { … } from "apexify.js/types"
  • painter.image — stitch, collage, compress, palette / validHex, resize, blend, Sharp helpers
  • painter.video — FFmpeg-backed VideoStack, createVideo, probe / extract helpers (flat delegates)
  • painter.path2d — create, draw, custom (**replaces createPath2D, drawPath, **createCustom****)
  • painter.detect — path, region, anyRegion, distance (replaces flat hit-test helpers)
  • Scene entry points unchanged in product terms — createScene, renderScene, renderSceneToGIF, renderSceneToVideoFrames

⚠️ Breaking changes (summary)

  • Published apexify.js barrel is ApexPainter + types; importing deep implementation modules from npm is unsupported unless you own exports. Chart/internals forks should patch package.json locally.
  • chain favors dotted step identifiers ("image.resize", "image.stitchImages", …).

Migration

SOURCEtypescript
typescript
Studio
import { ApexPainter } from "apexify.js";import type { SceneRenderInput, CanvasConfig } from "apexify.js";// or: import type { … } from "apexify.js/types"; const painter = new ApexPainter({ type: "buffer" });await painter.createVideo({  source: "./in.mov",  convert: { outputPath: "./out.mp4", quality: "high" },});await painter.image.stitchImages([a, b], { direction: "horizontal" });const path = painter.path2d.create(commands);await painter.path2d.draw(canvasBuffer, path);const hit = await painter.detect.path(path, x, y);

Older versions (5.3.x and earlier): see the complete CHANGELOG on GitHub.


Next steps