v2.23.0
RightNow-AI/openfangv2.23.0Oct 1, 2026by mvaligursky
Upgrading?
AI Summary
Major WebGPU rendering engine update focusing on performance, pipeline optimization, and material system improvements.
Key Highlights
- Significant WebGPU performance optimizations including persistent storage buffers and pipeline retention.
- Material bind group caching to reduce bind group updates.
- Comprehensive lighting and shadow system improvements with unified light slots.
Breaking Changes
- Removed obsolete immediate mesh rendering.
- Removed the shader dump and precompile workflow.
- Removed obsolete Mac Chrome blitFramebuffer workaround.
- Removed obsolete Safari texture unit workaround.
New Features
- Distance-based fog and tonemap support.
- Partial vertex and index buffer uploads.
- Per-attachment clear colors for multiple render targets.
- Stencil clears inside a render pass on WebGPU.
- Mesh instance light mask packing into 8 bits.
Full Release Notes
## Changes * Add distanceSq by @AlexAPPi in #9313 * Add useFog/useTonemap to ParticleSystemComponent, deprecate noFog by @mvaligursky in #9340 * Load audio assets regardless of the URL's file extension by @mvaligursky in #9365 * Poll gamepads every frame without allocating by @willeastcott in #9470 * Run the engine headless in Node.js without jsdom by @willeastcott in #9504 * Replace batch.origMeshInstances[0] with sourceMeshInstance variable by @AlexAPPi in #9454 ## Rendering * Store mesh instance matrices in a persistent storage buffer on WebGPU by @mvaligursky in #9614 * Add a bloom threshold to CameraFrame by @mvaligursky in #9455 * Add per-attachment clear colors to the camera for multiple render targets by @mvaligursky in #9333 * Support stencil clears inside a render pass on WebGPU by @mvaligursky in #9612 * Allow camera viewports extending past the render target on WebGPU by @mvaligursky in #9516 * Add partial vertex and index buffer uploads by @mvaligursky in #9401 * Add screen_size to forward view uniforms and deprecate uScreenSize by @mvaligursky in #9331 * Pack forward view uniforms to reduce alignment padding by @mvaligursky in #9332 * Move per-pass uniforms into the view uniform buffer by @mvaligursky in #9582 * Move textureBias into the view uniform buffer by @mvaligursky in #9484 * Supply per-pass textures through the WebGPU view bind group by @mvaligursky in #9521 * Publish the scene environment textures under their own uniforms by @mvaligursky in #9364 * Add an opt-in std140 packing order to UniformBufferFormat by @mvaligursky in #9400 * Stop creating typed array views for every dynamic uniform buffer update by @mvaligursky in #9418 * Retain WebGPU render pipelines between draws by @mvaligursky in #9380 * Look the WebGPU render pipeline up only when its state changes by @mvaligursky in #9492 * Write WebGPU buffer data directly from its storage by @mvaligursky in #9379 * Pass WebGPU bind group dynamic offsets as a typed array by @mvaligursky in #9487 * Skip WebGPU vertex and index buffer binds the render pass already has by @mvaligursky in #9496 * Keep the backing store of the device vertex buffer list across draws by @mvaligursky in #9498 * Assign bind group slots by index during the update by @mvaligursky in #9425 * Split the bind group update into a scope path and an owner path by @mvaligursky in #9429 * Leave a texture out of the mesh bind group when a supplied group has it by @mvaligursky in #9436 * Let the graphics device own the built-in textures by @mvaligursky in #9438 * Rebuild layer mesh arrays lazily after removals by @mvaligursky in #9378 * Pack the mesh instance light mask into 8 bits, for 24 shader define flags by @mvaligursky in #9617 * Rename supportsUniformBuffers to usesMeshBindGroups by @mvaligursky in #9383 * Split WebGPU device creation from device initialization by @mvaligursky in #9483 * Ignore MeshInstance#setIndirect where indirect draw is not supported by @mvaligursky in #9462 * Remove obsolete immediate mesh rendering by @mvaligursky in #9385 * Remove the shader dump and precompile workflow by @mvaligursky in #9477 * Remove obsolete Mac Chrome blitFramebuffer workaround by @mvaligursky in #9318 * Remove obsolete Safari texture unit workaround by @mvaligursky in #9319 ## Lighting & Shadows * Move light uniforms into the view uniform buffer, uploaded once per pass by @mvaligursky in #9482 * Give every light an absolute slot, shared by all light masks by @mvaligursky in #9475 * Dispatch the lights of a pass once, not per light mask by @mvaligursky in #9479 * Consolidate light slots into a LightList shared by renderer, shaders and lightmapper by @mvaligursky in #9481 * Make world-cluster allocation request/execute based by @mvaligursky in #9473 * Fade directional shadows at the far distance by @mvaligursky in #9417 * Skip shadow sampling beyond the far distance by @mvaligursky in #9419 * Request directional shadow culling from scheduled render passes by @mvaligursky in #9423 * Request local shadow culling from scheduled render passes by @mvaligursky in #9428 * Cull only requested directional shadow cascades by @mvaligursky in #9437 * Skip spot VSM blur when the shadow pass does not execute by @mvaligursky in #9440 * Set shadow caster material state once per material run by @mvaligursky in #9524 * Derive the normal matrix of shader materials in the shadow pass by @mvaligursky in #9585 * Give a mesh instance lightmap its own texture slot by @mvaligursky in #9415 * Bind only the lightmap slots a bake writes by @mvaligursky in #9420 * Fix shadow shader selection when changing cascade count by @mvaligursky in #9343 * Reset clustered shadow lights before preparing each frame by @mvaligursky in #9414 * Fix WebGPU shadow shader compile error for PCSS spot lights by @mvaligursky in #9545 * Pass an empty LightList, not 0, to the shadow pass shaders by @willeastcott in #9556 * Hidden mesh instances with culling disabled no longer cast shadows by @mvaligursky in #9606 ## Materials * Add a material bind group slot and cached shader processing keys by @mvaligursky in #9370 * Hold the textures of a material in its own bind group by @mvaligursky in #9457 * Bind the material bind group at a material switch instead of at every draw by @mvaligursky in #9413 * Store StandardMaterial diffuse in a material-owned uniform buffer by @mvaligursky in #9381 * Store emissive color and intensity in the material uniform buffer by @mvaligursky in #9392 * Store the remaining StandardMaterial colors and numbers in the material uniform buffer by @mvaligursky in #9398 * Store the derived StandardMaterial uniforms in the material uniform buffer by @mvaligursky in #9402 * Store the texture map transforms in the material uniform buffer by @mvaligursky in #9405 * Store the cube map projection box in the material uniform buffer by @mvaligursky in #9406 * Copy the cube map projection box into the material instead of keeping a reference by @mvaligursky in #9411 * Support mesh instance overrides of material uniform buffer values by @mvaligursky in #9386 * Let a mesh instance override a texture of the material bind group by @mvaligursky in #9445 * Restore global scope values after mesh instance parameter overrides by @mvaligursky in #9588 * Warn in debug builds when a mesh instance override array changes in place by @mvaligursky in #9395 * Warn about material and mesh instance parameters of view uniforms by @mvaligursky in #9619 * Add support for all eight UV sets in StandardMaterial by @mvaligursky in #9376 * Make alphaTest an accessor of Material and retire the generic float property helper by @mvaligursky in #9408 * Separate sprite color from material uniforms by @mvaligursky in #9337 * Separate UI element colors from material uniforms by @mvaligursky in #9338 * Pass the model scale to dynamic refraction from the vertex shader by @mvaligursky in #9616 * Fix LitMaterial ambient by making the ambient tint a lit argument by @mvaligursky in #9538 * Fix StandardMaterial shader invalidation for environment textures and 0/1 number changes by @mvaligursky in #9359 * Fix StandardMaterial shader invalidation for refraction thresholds by @mvaligursky in #9363 * Make StandardMaterial texture map iteration ignore prototype properties by @mvaligursky in #9368 * Stop placeholder textures from forcing shader recompiles by @mvaligursky in #9592 ## Gaussian Splatting * Make gsplat LOD distance-only, with target or limit splat budgets by @mvaligursky in #9584 * Only judge oversized gsplat LOD nodes by their middle by @mvaligursky in #9586 * Gsplat LOD: empty level for nodes without coarse data; distance default by @mvaligursky in #9326 * Load streamed gsplat LOD files nearest the camera first by @mvaligursky in #9520 * Update StreamedGSplat presets to use LOD falloff by @mvaligursky in #9369 * Stochastic alpha rendering for the GPU-sort gsplat renderer by @slimbuck in #9443 * Flat per-splat gsplat varyings and fix ComputeRadixSort reallocation on growth by @slimbuck in #9372 * Allow either provoking vertex for WGSL splat varyings by @mvaligursky in #9374 * Prefer first provoking vertex on WebGL by @mvaligursky in #9375 * Use a per-axis focal length for the gsplat footprint by @dfattal in #9486 * Skip compute work for cached Gaussian splat shadows by @mvaligursky in #9416 * Make Gaussian splatting GLB support tree-shakeable by @mvaligursky in #9327 * Make GSplat scene parameters tree-shakeable by @mvaligursky in #9328 * Avoid retaining GSplat work buffer shaders in container bundles by @mvaligursky in #9322 * Apply withCredentials to the gsplat loaders that stream with fetch by @mvaligursky in #9458 * Warn when gaussian splats render into a multisampled target by @mvaligursky in #9532 * Fix gsplat GPU sort flicker from sort key precision loss by @mvaligursky in #9493 * Fix mirrored gsplat footprints in flipped render targets by @mvaligursky in #9490 * Fix fisheye gsplats and sky in flipped render targets by @mvaligursky in #9495 * Correct gsplat spherical harmonics with orthographic cameras by @mvaligursky in #9531 * Fix NVIDIA GPU hangs during gsplat depth picking by @slimbuck in #9572 * Prevent gsplat crash after destroying a splat entity by @mvaligursky in #9522 * Prevent gsplat streaming crash after destroying a camera by @mvaligursky in #9533 * Render gsplats again after a camera re-adds their layer by @mvaligursky in #9535 * Keep gsplat placement changes made in frame:ready handlers by @mvaligursky in #9536 * Stop a gsplat from blanking for a frame when its asset is replaced by @willeastcott in #9610 ## Physics * Add RigidBodyComponent#gravityScale by @willeastcott in #9465 * Add a hitBackFaces raycast option to skip mesh collider back faces by @willeastcott in #9506 * Tighten the public surface of the physics API by @willeastcott in #9519 * Harden physics raycasts and forward the Application physicsWorld option by @willeastcott in #9517 * Ship an Ammo build whose heap starts at 16MB and grows on demand by @willeastcott in #9488 * Route the Ammo internal tick callback through one dispatcher per module by @willeastcott in #9463 * Apply gravity to the physics backend on install and when it changes by @willeastcott in #9439 * Refresh Ammo broadphase bounds when a body is teleported by @willeastcott in #9489 * Mirror mesh colliders with negative scale factors by @willeastcott in #9499 * Keep the orientation of mirrored dynamic bodies across physics steps by @willeastcott in #9500 * Fix mesh colliders sharing a mesh at different scales and rebuild them on scale changes by @willeastcott in #9311 * Sync compound children from their own transforms, not the root's dirty flag by @willeastcott in #9456 * Rebuild a collision component as a trigger when its rigid body is removed by @willeastcott in #9464 * Give both entities of a contact the normal of the other surface by @willeastcott in #9450 ## XR * Stop a double XR start from leaving a broken session by @willeastcott in #9599 * Stop the app freezing when an XR session ends by @willeastcott in #9600 * Stop XR culling from using the previous frame's head pose by @willeastcott in #9601 * Remove XR event listeners that outlive their owners by @willeastcott in #9623 * Apply camera clip plane changes to a running XR session by @willeastcott in #9624 * Correct XR input source rays and velocities by @willeastcott in #9625 * Start XrSession sessions facing the way the camera looks by @willeastcott in #9626 * Keep removed XR planes and meshes readable, and harden hit testing by @willeastcott in #9627 * Keep XrControllers models loaded while input sources share them by @willeastcott in #9631 * Hide XrControllers models while their pose is not tracked by @willeastcott in #9635 * Stop XrNavigation teleporting on system menu gestures by @willeastcott in #9636 * Document the XR select events of ElementComponent by @willeastcott in #9633 ## UI * Warn when image materials have no opaque render pass by @mvaligursky in #9346 * Keep an element's position when it is added to an entity under a screen by @willeastcott in #9525 * Let ElementDragHelper drag an element that is a direct child of a screen by @willeastcott in #9551 * Update a scrollbar's handle when its track is resized by @willeastcott in #9573 * Fix layout group subscriptions when components change by @mvaligursky in #9399 * Build text meshes without a pass over empty arrays by @willeastcott in #9562 * Skip drawing text with an empty render range by @willeastcott in #9567 * Draw CanvasFont glyph cells fully transparent by @willeastcott in #9568 * Fix a markup parser crash when a tag name shadows hasOwnProperty by @willeastcott in #9468 * Correct UI component docs, the Spanish locale fallback and button examples by @willeastcott in #9552 ## Device Recovery * Restore WebGPU rendering resources after device loss by @mvaligursky in #9355 * Restore WebGPU profiling after device loss by @mvaligursky in #9357 * Fix WebGPU recovery for compute, readback and generated data by @mvaligursky in #9358 * Cancel WebGPU recovery when the device is destroyed by @mvaligursky in #9397 * Fix WebGPU resource cleanup on device destruction by @mvaligursky in #9394 * Restore the WebGL backbuffer before recovery callbacks by @mvaligursky in #9347 * Recover SOG streaming across WebGL context loss by @mvaligursky in #9350 * Restore procedural sky lighting after device recovery by @mvaligursky in #9351 * Add cross-backend debug device loss testing by @mvaligursky in #9353 * Restore generated HDR environments in examples after device loss by @mvaligursky in #9345 * Redraw the on-demand splat example after device recovery by @mvaligursky in #9348 * Rebake example lightmaps after device recovery by @mvaligursky in #9352 ## Profiling & Debugging * Redesign MiniStats with compact and grouped profiling views by @mvaligursky in #9307 * Add collapsible MiniStats sections by @mvaligursky in #9361 * Add tracked resource counts to MiniStats by @mvaligursky in #9366 * Expose application performance statistics through AppStats by @mvaligursky in #9308 * Add user counters to AppStats by @mvaligursky in #9341 * Correct MiniStats timing and build availability documentation by @mvaligursky in #9310 * Add TextureRenderer for debug texture previews by @mvaligursky in #9382 * Add per-camera culling and display fixes to TextureRenderer by @mvaligursky in #9387 * Add a debug-engine draw limit for stepping through a render pass by @mvaligursky in #9501 * Add descriptive GPU profiling names for quad passes by @mvaligursky in #9329 * Track shaders on every graphics device by @mvaligursky in #9466 * Add a bare WebGL 2 device type for testing on constrained devices by @mvaligursky in #9471 * Announce apps to a devtools hook by @mvaligursky in #9618 * Add AppOptions#devtools to opt out of the devtools hook by @mvaligursky in #9637 ## Fixes * Add missing TEXTURE_2D_ARRAY entry to targetToSlot by @mvaligursky in #9321 * Fix sprite tint color space conversion by @mvaligursky in #9334 * Clear keyboard and mouse states on detach and focus loss by @mvaligursky in #9344 * Reset stale mouse movement deltas by @mvaligursky in #9349 * Fix WebGPU texture-formats-tier feature names and rename their flags by @mvaligursky in #9390 * Fix skydome depth of field with scene-pass depth by @mvaligursky in #9391 * Wait for container meshes before marking render assets ready by @mvaligursky in #9404 * Fix Picker readback rectangle in the top-left to bottom-left flip by @mvaligursky in #9441 * Reconstruct the picked world position at the pixel center by @mvaligursky in #9446 * Call super.frameUpdate() from RenderPassCompose by @mvaligursky in #9449 * Copy the render style onto a batched mesh instance by @mvaligursky in #9448 * Fix CameraComponent#setShaderPass() throwing when resetting the pass by @mvaligursky in #9451 * Resolve glb model resources against the directory of the glb by @mvaligursky in #9453 * Stop leaking a DrawCommands set per indirect draw per frame by @mvaligursky in #9459 * Fix bundle asset load retrying and error-response handling by @mvaligursky in #9460 * Fix the WebGL multi-draw fallback submitting no draws by @mvaligursky in #9461 * Treat every 2xx HTTP status as success by @willeastcott in #9507 * Accept status 0 responses from non-http schemes by @willeastcott in #9508 * Fix Curve.closest outside unit range by @humairaambreen in #9543 * Return the first key from Curve#closest for times before the curve by @willeastcott in #9544 * Accept mesh instances without a material in Layer#addMeshInstances by @mvaligursky in #9579 * Skip disabled entities in OutlineRenderer by @mvaligursky in #9452 * Fix OutlineRenderer shader errors when clustered lighting is disabled by @mvaligursky in #9590 * Fix OutlineRenderer material cleanup and frameUpdate listener leak by @mvaligursky in #9589 * Fix OutlineRenderer outlines missing with some CameraFrame settings by @mvaligursky in #9594 * Blur the TAA output in the camera frame's bloom and depth of field by @willeastcott in #9591 * Fix calculateNormals returning NaN for vertices with no triangle area by @mvaligursky in #9596 * Fix FloatPacking.float2Half corrupting values too large for a half float by @mvaligursky in #9595 * Clean up pole and tip triangles in sphere, capsule and cone geometry by @mvaligursky in #9597 * Keep screen-space particles square on any canvas aspect by @willeastcott in #9570 * Export skinned meshes to USDZ where their bones place them by @willeastcott in #9546 * Use the WGSL built-in saturate, fixing lightVertexColor on WebGPU by @mvaligursky in #9598 * Flip the vertex normal of back faces for two sided lighting by @willeastcott in #9559 * Initialize GraphicsDevice renderPassIndex to 0 by @mvaligursky in #9605 * Load cubemap faces given as URLs as sRGB color, not RGBP by @mvaligursky in #9607 * Fix Texture#lock on cubemap faces and mip levels by @mvaligursky in #9608 * Fix WebGPU stencil read mask, reuse pipelines across stencil refs by @mvaligursky in #9611 * Make createGraphicsDevice reject instead of hanging when no device can be created by @willeastcott in #9603 * Depth state fixes (depthTest, WebGPU depth bias, layer reset) by @mvaligursky in #9615 * Fix BlendState#setAttachment reviving overrides cleared by copy by @mvaligursky in #9620 ## Examples * Restyle the examples browser by @mvaligursky in #9629 * Slim the collapsed code pane and hide the code minimap by @mvaligursky in #9632 * Open the PlayCanvas Inspector in the examples browser by @mvaligursky in #9621 * Serve the inspector loader as is in the dev server by @mvaligursky in #9638 * Link a whole examples category by URL by @mvaligursky in #9303 * Add category sharing and expand-all controls by @mvaligursky in #9356 * Reveal current example when clearing search by @mvaligursky in #9354 * Rebuild the UI button, panel, masking and custom shader examples by @willeastcott in #9565 * Rebuild the UI text examples by @willeastcott in #9566 * Rebuild the UI layout, image and world examples by @willeastcott in #9569 * Add the UI input, common widgets and drag and drop examples by @willeastcott in #9571 * Add a hidden test example for UI element states by @mvaligursky in #9580 * Add rendering without entities example by @mvaligursky in #9377 * Add environment sources test example by @mvaligursky in #9360 * Give the render to image example a knight of its own by @willeastcott in #9583 * Refresh the MiniStats example scene by @mvaligursky in #9309 * Migrate examples to TextureRenderer by @mvaligursky in #9384 * Load the VAT character data as a binary asset in the vat-characters example by @mvaligursky in #9518 * Render gaussian splat crop and depth-of-field examples without MSAA by @mvaligursky in #9634 * Restore the depth prepass in the gaussian splat depth-of-field example by @mvaligursky in #9639 * Apply area-picker example material changes via update() by @mvaligursky in #9497 * Remove no-op bloom.enabled writes from examples by @mvaligursky in #9476 * Replace the Arial and Courier fonts of the examples and tests with Roboto by @willeastcott in #9577 * Ship the Roboto ExtraLight license with the XR examples' font by @willeastcott in #9576 ## Documentation & Types * Type Entity#addComponent options and result by component name by @willeastcott in #9320 * Type Asset#resource by asset type (Asset<K>, AssetMap, AssetType) by @willeastcott in #9403 * Type the script lookup APIs against Script and return the class you pass in by @willeastcott in #9472 * Type element input events with the browser objects they carry by @willeastcott in #9527 * Add alpha option to createGraphicsDevice typings by @willeastcott in #9317 * Fix JSDoc types that disagree with runtime values, salvaged from #5817 by @willeastcott in #9469 * Fix Debug.assert and Texture levels JSDoc types by @willeastcott in #9491 * Clear all API reference build warnings and treat warnings as errors by @willeastcott in #9412 * Add a Framework category and home the uncategorised public classes by @willeastcott in #9421 * Rewrite Asset-related class overviews and document every resource handler by @willeastcott in #9409 * Rewrite the Math category class overviews by @willeastcott in #9410 * Rewrite the scene graph and render pipeline class overviews by @willeastcott in #9422 * Rewrite the material class overviews by @willeastcott in #9424 * Make the modern input system the Input category and describe its classes by @willeastcott in #9427 * Describe the modern animation classes and mark the legacy ones by @willeastcott in #9430 * Describe the sound classes and template the thin component system pages by @willeastcott in #9432 * Describe the Gizmo and TransformGizmo base classes by @willeastcott in #9433 * Use American spelling in the Material and LitShaderOptions overviews by @willeastcott in #9431 * Document which asset types fire the progress event by @willeastcott in #9474 * Correct RigidBodyComponent group/mask defaults and raycastAll sort order by @willeastcott in #9305 * Fix physics docs for impulse points, kinematic velocity and contact results by @willeastcott in #9515 * Improve OutlineRenderer API documentation by @mvaligursky in #9587 * Correct typos and British spellings across the engine source by @willeastcott in #9547 * Correct typos and British spellings in examples, scripts and tests by @willeastcott in #9550 * Add a context7.json so Context7 indexes usage docs and agent rules by @willeastcott in #9478 * Point the Context7 rules at the llms.txt indexes by @willeastcott in #9503 ## Engine Dependencies * Update dependency sharp to v0.35.4 [SECURITY] in #9330 * Update all npm dependencies in #9434 * Update dependency mocha to v12 in #9435 ### Contributors * @AlexAPPi * @dfattal * @humairaambreen * @mvaligursky * @slimbuck * @willeastcott ## New Contributors * @dfattal made their first contribution in #9486 * @humairaambreen made their first contribution in #9543 **Full Changelog**: https://github.com/playcanvas/engine/compare/v2.22.6...v2.23.0
Related Repositories
A cross-platform desktop All-in-One assistant for Claude Code, Codex, OpenCode, OpenClaw, Grok Build & Hermes Agent. Only official website: ccswitch.io
An open-source remote desktop application designed for self-hosting, as an alternative to TeamViewer.
Build smaller, faster, and more secure desktop and mobile applications with a web frontend.
π RuView turns commodity WiFi signals into real-time spatial intelligence, vital sign monitoring, and presence detection — all without a single pixel of video.
Incredibly fast JavaScript runtime, bundler, test runner, and package manager – all in one