Architecture / Migration · migration
Changelog
Current Apexify.js 6.0.0 documentation for Changelog.
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.validatefield is retained only for source compatibility and no longer disables validation; usemaxSurfaceDepthonly 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-likeloadValue; dotted object/array lookup; whole-fieldBuffer/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,
$assetresolution, flex/grid expansion, and normal scene validation on the resolved output. Explicit0,false, and""are preserved as values; they do not silently trigger defaults. - Components —
badge,progressBar,avatar,card, andwatermarkall emit renderableSceneLayer[]with runtime validation for sizing/geometry and invalid inputs. - Plugins — duplicate/pending-name rejection, serialized asynchronous installs, transactional
PluginHostregistration 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 testgate 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.createAudioonApexPainter— server-side SFX synthesis (16-bit PCM WAVBuffer, 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 atat(seconds) with per-eventgain.compose({ clips })— one WAV from overlapping clips:at,duration,sourceStart,preset/sound/wav, pitch/volume/pan/fades,noise,filter,quality; optionalpostHighpassHz,noiseGateThresholdon 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 manycreateVideocalls.- Layer types:
source,trim,splice,text(fullTextProperties),audio(files +createAudiopresets / synth / sequence / WAV). - Upsert by
id— sameidreplaces trim/splice/source;text/audiowith the sameidmerge unless{ replace: true }. pipeline.render({ outputPath })— compiles layers; returnspassescount.
Video text overlays — createVideo({ addTextOverlay })
addTextOverlay— timed captions using the sameTextPropertiesmodel ascreateText(fonts, gradients, glow, shadow, stroke, wrap, curved text, decorations, placement, …). Eachoverlays[]entry is aVideoTextOverlayClip: 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.customfor FFmpeg expression overrides. - Canvas render → transparent PNG per overlay → FFmpeg
overlaychain.
🔧 Changed
AudioCreate/TemplateCreatefacades onApexPainter(same pattern asVideoCreate,GifCreate,SceneCreate).createVideo({ addText })andcreateVideo({ addAnimatedText })— still available (FFmpegdrawtext) but deprecated; preferaddTextOverlayorvideoPipeline().text().- Procedural SFX entry is
painter.createAudio(aligned withcreateVideo,createCanvas,createGIF). createVideo({ mixAudio })whenkeepOriginalAudio: false: no silentanullsrcbed under overlays (fixes quiet/muddy SFX). Overlay-only mixes useamixnormalize=0plus lightalimiter.mixAudiobuffers: temp files use correct extension (.wav / .mp4 / .mp3) from buffer magic bytes.
🐛 Fixed
mixAudiowith WAVBufferoverlays 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
- README —
videoPipeline,addTextOverlay,createAudio, API table, media-pipeline use case; package.json keywords/description. - Video (feature guide) — Video overview, Text overlays, Video audio (FFmpeg).
- Video (advanced) — Video pipeline, Video (advanced) hub.
- Audio (advanced) —
createAudio, Audio (advanced) hub.
[5.4.4] - 2026-05-13
🔧 Changed
painter.image.resize(ResizeOptions.imagePath):string|Buffer→sharpFromResolvableInput—Bufferraster bytes;http(s):URLs (fetched);data:image/...;base64,...(**parameters allowed before;base64,, e.g.charset); filesystem paths (**absolute/relative toprocess.cwd()).painter.image.imgConverter:sourcestring|Buffer— same resolution rules asresize(paths/URLs/data URLs/buffers).
📚 Documentation
- Image resize & convert —
resize/imgConverterunifiedBuffer/ URL / data URL / path inputs.
[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?)→TemplateHandlewithrender,toRenderInput—{{key}},{{key | default}},$refs (optional customresolveAssetRef); flexlayout;visible; layerid; runtimeoverridespainter.assets—loadImage,loadFont,loadPalette,resolve, clears / unregister helperspainter.components—badge,progressBar,avatar,card,watermark(toLayers→SceneLayer[])painter.plugins—use,get,has,remove;painter.use(plugin)(ApexifyPlugin, synchronousinstall)prepareForRender(payload)— deep$walk for JSON-like configs (same semantics asrenderScene)- Optional trailing
{ resolveAssetRefs: true }oncreateCanvas,createImage,createText,measureText,createChart/ comparison / combo,createGIF,animate,createVideo(off by default on those helpers;renderScene/ scene steps inrenderSceneToGIF/renderSceneToVideoFramesresolve by default) createScene/SceneBuilderreceivespainter.assets’ resolver;SceneBuilder.render({ resolveAssetRefs: true })when layers carry$…batch(operations, opts?)/chain—resolveAssetRefsandresolve;resolveis required whenresolveAssetRefsis true (assets.resolveis the usual default)
🔧 Changed / 🐛 Fixed
ApexPainter.use— synchronousinstallonly (removed asynccatchthat could hide failures)SceneRenderOptionstyping —resolveAssetRefsnext tovalidate/maxSurfaceDepth
📚 Documentation
- README — named assets (
$name,$palette.key): scenes vs templates vsSceneBuilder,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()— plainSceneRenderInputwith a copiedlayersarray (renderScene,renderSceneToGIF,renderSceneToVideoFrames, tests)
Scene — validation
SceneRenderOptions(SceneCreator.render,ApexPainter.renderScene,SceneBuilder.render) —validate(defaulttrue) andmaxSurfaceDepth(default 64);{ validate: false }only when inputs are trustedvalidateSceneRenderInputonapexify.js;ApexPainter.validateSceneRenderInputdelegates to the same implementation
Scene — GIF / video helpers
renderSceneToGIF—gif.sceneRender(same options bucket asrenderSceneduring rasterization)renderSceneToVideoFrames—video.sceneRenderforwarded for the raster step before FFmpeg
🔧 Changed
SceneBuilderseparated fromSceneCreator(builder façade vs compositor runtime)
📚 Documentation
- Scene (advanced) — Scene overview:
addLayers, stack editing,toRenderInput, validation,resolveAssetRefs,sceneRenderon 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 helperspainter.video— FFmpeg-backedVideoStack,createVideo, probe / extract helpers (flat delegates)painter.path2d—create,draw,custom(**replacescreatePath2D,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.jsbarrel isApexPainter+ types; importing deep implementation modules from npm is unsupported unless you ownexports. Chart/internals forks should patchpackage.jsonlocally. chainfavors dotted step identifiers ("image.resize","image.stitchImages", …).
Migration
Older versions (5.3.x and earlier): see the complete CHANGELOG on GitHub.