Skip to content

Releases: Exoridus/ExoJS

v2.1.1

18 Apr 23:49

Choose a tag to compare

Patch release fixing a cluster of WebGPU and scene-graph bugs discovered after 2.1.0 shipped. No public API removals or renames; one backward-compatible addition on Container.addChild.

Fixed

  • WebGPU adapter ordering. WebGpuRenderManager now requests the GPU adapter before acquiring the canvas WebGPU context. A null adapter previously locked the canvas into WebGPU mode, preventing Application's automatic WebGL2 fallback from obtaining a context on the same element.
  • WebGL2 shader program binding. WebGl2ShaderRuntime.sync() now binds the program before writing uniforms. The previous draw pipeline never called bindShader(shader) with a non-null shader, so every uniform* write targeted the wrong or null program and drawElements reported "no valid shader program in use". Exposed by the WebGPU adapter fallback above.
  • WGSL multi-texture sprite shader uses textureSampleGrad with explicit screen-space derivatives. textureSample's uniformity requirement prevented the 8-slot dispatch from compiling on any sprite batch spanning more than one texture slot.
  • Sprite index buffer allocation and lifecycle. Buffer size was 4× larger than intended (indexData.byteLength * BYTES_PER_ELEMENT instead of indexData.byteLength), and _ensureBatchCapacity ran inside the draw loop and could destroy a buffer the render pass had already bound. Capacity is now grown once up front.
  • Sprite multi-batch rendering. When a flush contained multiple batches (blend-mode change, texture-slot overflow, or pipeline switch), each batch's queue.writeBuffer(vertexBuffer, offset: 0, ...) serialised before the single submit, leaving only the last batch's vertex data in the buffer. All batch vertex data is now packed into one CPU buffer at distinct sprite offsets and uploaded once; drawIndexed uses firstIndex to target each range.
  • Particle and primitive multi-drawcall rendering. Same multi-write-to-offset-0 pattern, plus mid-loop _ensureCapacity destroying buffers still referenced by the pass. Particle renderer now submits one command buffer per system. Primitive renderer was rewritten: CPU bakes view * globalTransform into vec4 clip-space positions per vertex, pipeline has no bind-group, one render pass per flush with packed vertex/index buffers.
  • Primitive combine order. _combinedTransform.copy(view).combine(global) produced global * view (Matrix.combine applies the argument on the left, confirmed by SceneNode.getGlobalTransform which chains local.combine(parent.global) to yield parent.global * local). Swapped to copy(global).combine(view) = view * global.
  • WebGPU mipmap generation. The full-screen downsample triangle's UVs are no longer Y-flipped relative to framebuffer orientation. Every odd mip level was being rendered upside-down, producing a visible sprite flip whenever the view zoomed far enough for the LOD selector to cross an odd/even boundary.

Added

  • Container.addChild accepts multiple children via rest args (addChild(...children)). The previous single-argument signature silently dropped the tail of addChild(a, b, c, d); callers only saw a in the scene graph. Single-child usage stays backward compatible.
  • Doc comment on ParticleOptions.position clarifying it is in the owning ParticleSystem's local coordinate space. The shader applies the system's global transform on top, so passing world coordinates double-translates the emitter.

v2.1.0

18 Apr 18:43

Choose a tag to compare

[2.1.0] - 2026-04-18

Product-readiness release. Additive across assets, game-feel, visuals, performance, optional physics, and WebGPU parity. No public contracts were removed or renamed since v2.0.0.

Highlights

  • Typed asset manifests and bundle loading workflow.
  • AnimatedSprite with named clips, loop control, and frame signals.
  • Scene stacking with participation policies, input routing, and fade transitions.
  • View/camera polish: follow with lerp, bounds clamp, zoom, shake.
  • Audio sprites and sound pooling.
  • Visual capability wave: filter pipeline, masking, render passes, cache-as-bitmap, multi-texture batching on the WebGPU backend.
  • Automatic off-screen culling with observable render stats.
  • Optional Rapier physics integration behind an optional peer dependency.
  • WebGPU parity improvements and clearer initialization failure semantics.
  • Docs and examples overhaul; release verification hardening.

Assets / workflow

  • defineAssetManifest, AssetEntry, and loadBundle with progress callbacks.
  • BundleLoadError surfaces per-entry failures with the responsible loader token.
  • Strict manifest validation runs at definition time.
  • CacheStore + IndexedDbStore remain the persistence path; strategy classes (CacheFirstStrategy, NetworkOnlyStrategy) are exposed for custom pipelines.

Game-feel

  • AnimatedSprite: defineClip, setClips, play, stop, loop override, onComplete and onFrame signals.
  • SceneManager is now a real stack: pushScene, popScene, setScene with resolved SceneParticipationPolicy covering stack mode and input mode.
  • SceneInputEvent routing honours stack participation so overlay/modal scenes can intercept input cleanly.
  • Fade transitions integrated into scene switching.
  • View camera: follow with lerp, setBoundsConstraint, zoom/setZoom, shake with decay and configurable frequency.
  • Sound: setPoolSize, playPooled, stopPooled, defineSprite, setSprites for audio-sprite playback.

Rendering / visuals

  • Filter pipeline: abstract Filter base with BlurFilter and ColorFilter implementations; per-node filter chains wired through the render runtime.
  • Masking support in both render managers and on RenderNode.
  • Render-pass composition: RenderTargetPass, CallbackRenderPass, RenderTarget, and the existing RenderTexture for off-screen work.
  • RenderNode.cacheAsBitmap flattens expensive subtrees to a cached texture with invalidation.
  • Container.sortableChildren + SceneNode.zIndex provide depth-sorted rendering with a stable fallback on insertion order.
  • Multi-texture batching on the WebGPU sprite renderer (textureSlots, maxBatchTextures). See caveat below.
  • WebGPU sprite, particle, and primitive renderers reached functional parity with the WebGL2 equivalents.
  • Context-loss handling preserved.

Performance

  • Automatic off-screen culling: Drawable checks inView(view) each frame and counts skipped nodes.
  • RenderStats exposes submittedNodes, culledNodes, drawCalls, batches, renderPasses, and frameTimeMs for observability.
  • Hot-path cleanup across the renderers.
  • npm run perf:benchmark runs the rendering benchmark harness under test/perf/.

Physics

  • Optional Rapier integration via createRapierPhysicsWorld({ gravityY }).
  • @dimforge/rapier2d-compat is declared as an optional peerDependency; apps that do not import the physics entry point incur zero runtime cost.
  • Collision groups/masks encoded into Rapier's 16/16 packed format; PhysicsCollisionFilter lets you declare membership and what each body collides with.
  • Triggers vs. solid colliders distinguished via trigger on the descriptor; onTriggerEnter / onTriggerExit signals on the body.
  • Transform sync helpers and a createDebugGraphics/updateDebugGraphics path for debug draw through the existing Graphics primitive.

WebGPU

  • Sprite, particle, and primitive renderers now cover the WebGL2 feature surface used by the scene runtime.
  • Explicit backend: { type: 'webgpu' } errors out if WebGPU is unavailable or initialization fails — failures are not silently swallowed.
  • backend: { type: 'auto' } prefers WebGPU when navigator.gpu is present and falls back to WebGL2 only when the WebGPU init path throws.
  • Initialization error paths are now observable through the thrown error rather than partially constructed state.

Docs / examples

  • README rewritten to match the shipped surface.
  • New docs hub under docs/ with sections for getting-started, core-concepts, assets, scenes, rendering, audio, physics, performance, and examples.
  • New class-focused API pages: Application, Renderer, Graphics, AnimatedSprite, AssetManifests, Audio, View, VisualEffects, PhysicsRapier, Performance, GameFeel.
  • examples/ folder contains focused source snippets (01-quickstart.ts08-physics-rapier.ts) that are typechecked against the public API via tsconfig.examples.json.

Tooling / release quality

  • npm run typecheck:examples typechecks the in-repo examples against src/ to prevent example drift.
  • npm run verify:exports validates the package entry graph (scripts/verify-exports.mjs).
  • npm run verify:package runs build → example typecheck → export verification → npm pack --dry-run.
  • npm run verify:release is the smallest release gate: typecheck → lint → tests → verify:package.
  • CI runs lint, typecheck, tests, bundle build, declaration build, example typecheck, export verification, and pack dry-run on every PR to master.

Behaviour changes worth knowing

These are minor-level behaviour changes, not source-breaks; flagged here for transparency:

  • Automatic culling: nodes whose inView(view) check is false are no longer submitted and are counted in RenderStats.culledNodes. Apps that were already relying on correct bounds see no observable change. If a custom drawable under-reports its bounds, it may now be skipped when it was previously drawn off-screen.
  • Scene input routing: with the new stack, input dispatch honours the resolved SceneInputMode of each stack entry. Apps that only use setScene(...) with no pushScene keep single-scene v2.0.0 behaviour.
  • Explicit WebGPU failures: backend: { type: 'webgpu' } now throws rather than silently picking WebGL2. Apps that want the old "try WebGPU, otherwise WebGL2" behaviour should use backend: { type: 'auto' }.

Known limitations / honest caveats

  • WebGL2 is still single-texture batched. Multi-texture batching is implemented only in the WebGPU sprite renderer. WebGL2 sprite-heavy scenes will still flush on texture changes.
  • WebGPU is improved, not "production WebGPU". Treat the WebGPU backend as functional parity with WebGL2 for the features this library ships, not as a general-purpose WebGPU renderer.
  • Rapier is optional. If you never import the physics entry point, Rapier is not installed or loaded. It is not bundled with the library.
  • Tilemaps are not in scope. There is no built-in tilemap renderer; engines targeting Tiled-centric games should continue to reach for dedicated tooling.
  • Bitmap fonts are not shipped. Text renders via Canvas with stroke support; BitmapText is not included.
  • No tween library. Animation curves and tween orchestration are left to consumer code or external libraries.
  • Audio remains Web Audio decoded/streaming with pooling and sprites. Spatial audio (PannerNode), effects (ConvolverNode, BiquadFilterNode, DynamicsCompressorNode), and fade helpers are not part of this release.
  • Particles are still CPU-simulated. The WebGPU particle renderer is a rendering path, not a GPU compute simulator.
  • Graphics: no gradients, patterns, caps/joins, or dashing. Basic fills and strokes only.
  • Input gaps unchanged from 2.0.0: no haptics/vibration, no rebinding capture, no gesture library, fixed gamepad dead zones.

Upgrading from 2.0.0

No code changes are required for typical applications. Review the behaviour-change notes above if your code:

  • requests backend: { type: 'webgpu' } explicitly and was relying on silent fallback,
  • implements a custom Drawable with inexact bounds,
  • pushed multiple scenes via manual orchestration outside SceneManager.

[2.0.0] - previous major

Baseline for the modernized architecture wave (renderer runtime, scene runtime, class-token loader v2, math and rendering contract renames).

v2.0.0

17 Apr 15:54

Choose a tag to compare

Highlights

  • Loader API v2 — class-token resource loading: loader.load(Texture, 'hero') replaces the string-indexed ResourceContainer, producing fully inferred results per resource class.
  • Resource pipeline overhaul + stabilization: first-class AssetFactory contract, pluggable CacheStore strategies (network-only / cache-first), IndexedDbStore backed by a named Database, plus built-in factories for binary blobs, WebAssembly modules, and WebVTT cues.
  • Single-bundle direction: consolidated publication surface with one esm/iife pair, minified variants, and a single exo.d.ts.
  • Rendering / backend reorganization: WebGL2 and WebGPU backends split into dedicated rendering/webgl2/ and rendering/webgpu/ folders; text/video/sprite promoted to their own subpackages; shared blend-state moved to one module.
  • Repo / build cleanup: TypeScript rollup.config.ts, TypeScript-native ESLint flat config, stricter lint rules, modernized dependencies, and per-domain types.ts/utils.ts replacing the legacy types/ and utils/ piles.

Breaking changes

  • Scene hooks now receive the loader: init(loader), unload(loader) (was init(resources) / unload()).
  • ResourceContainer and AbstractResourceFactory are removed; use the loader and AssetFactory / AbstractAssetFactory instead.
  • Legacy src/types and src/utils barrels are gone; import from the owning module (core/types, math/CircleLike, etc.).