v2.19.0
playcanvas/enginev2.19.0May 28, 2026by mvaligursky
AI Summary
A major update introducing WebXR support, GSplat hybrid rendering, and architectural refactoring.
Key Highlights
- Adds WebXR support for Apple Vision Pro and WebGPU
- Introduces GSplat hybrid raster renderer
- Migrates engine builds to esbuild
- Refactors components to use class fields
Breaking Changes
- Remove HTMLAudioElement fallback from sound system
- Deprecate GSplatComponent#unified and default it to true
New Features
- WebGPU XR rendering support
- GSplat hybrid raster renderer
- WebGPU radix sort implementation
- esbuild migration for engine builds
- Class field migration for components
Full Release Notes
## Breaking changes * [BREAKING] Remove HTMLAudioElement fallback from sound system by @willeastcott in #8636 * [BREAKING] Deprecate GSplatComponent#unified and default it to true by @mvaligursky in #8802 ## Changes * JSDoc whitespace cleanup by @willeastcott in #8616 * Standardize JSDoc class overviews across components by @willeastcott in #8617 * Unlink SHADER_ constants in Material.update by @willeastcott in #8621 * Unlink renderPasses note in CameraComponent by @willeastcott in #8622 * GSplat local rasterize early-out via precomputed power cutoff by @mvaligursky in #8623 * Simplify self-referential JSDoc @link tags by @willeastcott in #8624 * Remove redundant JSDoc @type tags from inferable fields by @willeastcott in #8625 * Clean up 'Other' category on API reference index by @willeastcott in #8627 * Read SOG gsplat data directly from source textures by @mvaligursky in #8628 * Calculate camera aspect ratio in CameraComponent onEnable by @lucaheft in #8632 * Document both halves of accessor pairs in public API by @willeastcott in #8634 * Hoist constructor-assigned properties to class fields in src/core and src/platform/input by @willeastcott in #8635 * Make camera aspect ratio auto-refresh in Camera class by @mvaligursky in #8633 * Fold gsplat debug AABB flags into debug enum by @mvaligursky in #8637 * Drop vendor-prefixed Page Visibility API support by @willeastcott in #8638 * Include GPU vendor/architecture in WebGPU feature log by @mvaligursky in #8642 * Inline AppBase tick as a class-field arrow by @willeastcott in #8639 * Move per-frame stats collection from AppBase to ApplicationStats by @willeastcott in #8643 * Remove dead `this.controller` references from AppBase and hide Controller class by @willeastcott in #8644 * Expose device.minSubgroupSize and device.maxSubgroupSize on GraphicsDevice by @mvaligursky in #8645 * Add WebGPU compute radix sort (4-bit portable + OneSweep NVIDIA) by @mvaligursky in #8620 * Add WGSL linear_indexing and CAPS_STORAGE_TEXTURE_READ by @mvaligursky in #8648 * Radix sort facade, indirect mode, and WebGPU subgroup sizing by @mvaligursky in #8647 * Standardize Layer on class fields with grouped ordering by @willeastcott in #8649 * Defer WebGPU buffer destruction until submit by @slimbuck in #8652 * Use texImage2D for gsplat splat-order uploads by @slimbuck in #8656 * Hybrid raster renderer with compute projector by @mvaligursky in #8658 * Clarify spotlight cone angles are half-angles by @willeastcott in #8665 * Align LightComponent with CameraComponent architecture by @willeastcott in #8666 * Tighten LightComponent JSDoc by @willeastcott in #8667 * Migrate ComponentData classes to class fields by @willeastcott in #8668 * Remove redundant constructor assignments in ButtonComponent by @willeastcott in #8669 * Remove redundant get data() overrides by @willeastcott in #8670 * Migrate ZoneComponent and ScreenComponent to class fields by @willeastcott in #8671 * Migrate SpriteComponent state to class fields by @willeastcott in #8672 * Remove unused _batchGroup field from batched components by @willeastcott in #8673 * Clean up vestigial Model plumbing on SpriteComponent by @willeastcott in #8674 * Remove unused JSDoc @import lines by @willeastcott in #8677 * SpriteAnimationClip class fields, layer typings, and docs by @willeastcott in #8679 * SpriteAnimationClip name, fps, loop property JSDoc by @willeastcott in #8680 * Remove unused WebGPU compute perf benchmark by @mvaligursky in #8681 * Speed up GSplat octree evaluateNodeLods by @mvaligursky in #8683 * Fuse GSplat budget bucket indices into LOD evaluation by @mvaligursky in #8684 * Remove raster GPU sort path and sort-key compute by @mvaligursky in #8685 * Rename GSPLAT_RENDERER_RASTER_HYBRID to RASTER_GPU_SORT by @mvaligursky in #8686 * Respect gsplat alphaCull on CPU raster and compute paths by @mvaligursky in #8687 * Remove unused gsplat-material module by @mvaligursky in #8688 * Exclude StorageBuffer from device.buffers tracking by @slimbuck in #8690 * Add Scene#gsplatCentersEnabled opt-out for gsplat CPU centers by @mvaligursky in #8691 * Align ScrollbarComponent with CameraComponent architecture by @willeastcott in #8693 * Tighten ScrollbarComponent internals by @willeastcott in #8694 * Handle lostpointercapture as well by @slimbuck in #8696 * Expose parsed manifest on GSplatOctreeResource by @MattiasVik in #8699 * TRACEID_BUFFERS trace, example control, and storage buffer debug names by @mvaligursky in #8705 * Reduce radix sort GPU buffer footprint by @mvaligursky in #8706 * Add XrBridge for backend-specific WebXR presentation by @mvaligursky in #8712 * Route XR framebuffer through XrBridge; refresh VR LOD example by @mvaligursky in #8713 * WebGPU immersive XR presentation by @mvaligursky in #8717 * WebGPU XR device loss, devicelost events, and graphics binding typings by @mvaligursky in #8719 * WebGPU AR camera color and syncCameraColorTexture bridge by @mvaligursky in #8720 * Parallelize engine builds with turbo by @kpal81xd in #8716 * WebGPU XR depth bridge, examples, and XR camera sync fixes by @mvaligursky in #8721 * WebGPU multiview frame pass, XR bridge, and clustered lights by @mvaligursky in #8723 * WebGPU texture-array projection and vr-basic gallery refresh by @mvaligursky in #8724 * Migrate js builds to esbuild by @kpal81xd in #8722 * WebGPU fragment shader optimization by @cabanier in #8733 * Data-driven gating for WGSL built-in inputs by @mvaligursky in #8739 * Align WGSL and GLSL clustered-light chunks by @mvaligursky in #8740 * Support for exporting aoMaps and the second UV channels by @tanaydimri in #8728 * Omit default texCoord in glTF exporter texture info by @mvaligursky in #8741 * Enable asset load retries by default by @mvaligursky in #8744 * Tidy ResourceLoader.enableRetry defaults and input handling by @mvaligursky in #8745 * Dual Color LUT crossfade and lut-grading example by @mvaligursky in #8749 * Warn when WebGPU or WebXR is used in an insecure context by @mvaligursky in #8752 * Log uncaptured WebGPU errors by @mvaligursky in #8757 * Mark platform.visionos as @ignore in JSDoc by @mvaligursky in #8765 * XrMenu label items, ray picking, always-visible HUD mode by @mvaligursky in #8770 * Add smooth turn mode to XrNavigation by @mvaligursky in #8771 * Update examples URL in READMEs to playcanvas.com/examples by @willeastcott in #8772 * Reuse clear options object in Renderer.clear by @willeastcott in #8776 * Replace pow(x, 5.0) with multiplies in Schlick Fresnel by @willeastcott in #8774 * Avoid iterator allocations in Frustum.setFromMat4 by @willeastcott in #8775 * Avoid per-frame clearVariants in unified material sync by @mvaligursky in #8778 * Align ScrollViewComponent with CameraComponent architecture by @willeastcott in #8777 * Add alphaDither property for independent dither strength by @mvaligursky in #8782 * Expose WGSL unrestricted_pointer_parameters as a device cap by @mvaligursky in #8785 * Expose WGSL pointer_composite_access as a device cap by @mvaligursky in #8786 * Expose WGSL packed_4x8_integer_dot_product as a device cap by @mvaligursky in #8787 * Expose WGSL texture_and_sampler_let as a device cap by @mvaligursky in #8790 * Make getGuid() part of the public API by @mvaligursky in #8795 * OneSweep on NVIDIA, indirect bind fix, GSplat bit alignment by @mvaligursky in #8657 * Expose guid as a public getter by @mvaligursky in #8797 * WebGPU XR rendering on Apple Vision Pro by @mvaligursky in #8756 * Release ImageBitmap memory after streamed SOG texture upload by @mvaligursky in #8758 ## Fixes * Fix incorrect JSDoc in public APIs by @willeastcott in #8612 * Fix more incorrect JSDoc in public APIs by @willeastcott in #8613 * Fix more incorrect JSDoc in public APIs by @willeastcott in #8614 * Fix more incorrect JSDoc in public APIs by @willeastcott in #8615 * Fix JSDoc link warnings for ShaderUtils and GSplatComponent by @willeastcott in #8618 * Fix url param JSDoc in ResourceHandler and ContainerHandler by @willeastcott in #8619 * Avoid camera_params redefinition in pickPS by @gameatp in #8640 * Avoid camera_params redefinition in pickPS (WGSL) by @mvaligursky in #8641 * Split batches by castShadow and shadowCascadeMask by @mvaligursky in #8650 * Fix Picker depth clear decoding by @slimbuck in #8651 * Defer GPU resource destruction for pending commands by @mvaligursky in #8655 * Fix executeCallback JSDoc type in gsplat-resolve-sh by @willeastcott in #8682 * Register StorageBuffer for WebGPU device lose/restore by @mvaligursky in #8692 * Texture#lock allocates when mip level slot is missing by @mvaligursky in #8695 * Default xrCompatible for WebGL and WebGPU paths by @mvaligursky in #8714 * TAA resolve premultiplied alpha for transparent clears by @mvaligursky in #8726 * visionOS MSAA resolve into XR framebuffer by @mvaligursky in #8730 * Avoid GLSL ES reserved word 'packed' in compact format shader by @mvaligursky in #8737 * Apply specular factor/color when map is present by @mvaligursky in #8742 * Include error message context in load failure logs by @mvaligursky in #8743 * Align projector projection with WebGPU shader uniforms by @mvaligursky in #8662 * Stereo XR viewport width for hybrid projector by @mvaligursky in #8664 * Fix renderSceneColorMap/renderSceneDepthMap toggling to false when already set by @MAG-AdrianMeredith in #8731 * Apply Color LUT in correct sRGB color space by @mvaligursky in #8747 * Resolve sog texture urls from document base by @kpal81xd in #8754 * Gate ImageBitmap release on opt-in flag, make it one-shot by @mvaligursky in #8760 * Fix WebGPU XR stereo viewport and harden vr-lod example by @mvaligursky in #8761 * Ensure opacity dither fully hides at alpha 0 and fully shows at alpha 1 by @lucaheft in #8767 * Make writeSortIndirectArgs WGSL helper portable for Firefox WebGPU by @mvaligursky in #8784 * Fire EVENT_PRECULL after frustum update by @mvaligursky in #8793 * Stop http retry tail from leaking across unit tests by @mvaligursky in #8794 * Unified-mode follow-ups (orbit-camera AABB, internal warnings) by @mvaligursky in #8803 ## Examples * Save whole GSplat benchmark page as PNG instead of chart only by @mvaligursky in #8611 * Viewer: support drag-and-drop of unpacked SOG by @mvaligursky in #8626 * Keep HIDDEN examples reachable by URL in production builds by @mvaligursky in #8629 * Overhaul GSplat benchmark example UI and measurement by @mvaligursky in #8660 * Unified gsplat for annotations and multi-splat by @mvaligursky in #8661 * GSplat renderer controls and unified orbit fixes by @mvaligursky in #8663 * Add animated sprite example using SpriteComponent by @willeastcott in #8676 * Add Camera Frame toggle to portal example by @mvaligursky in #8697 * Dual-backend shader-material example by @mvaligursky in #8698 * LOD streaming Debug panel and one-frame texture trace by @mvaligursky in #8702 * Update examples sidebar styling by @kpal81xd in #8704 * Scroll landscape controls panel when content overflows by @mvaligursky in #8707 * Add Gaussian splatting XR category with VR LOD demo by @mvaligursky in #8708 * Avoid clipping select dropdowns in landscape controls by @mvaligursky in #8715 * Convert XR examples to AppBase and WebGPU device setup by @mvaligursky in #8725 * Migrate examples to vite by @kpal81xd in #8729 * First-person and third-person gsplat examples on Sunnyvale by @mvaligursky in #8736 * Add splat-portal gaussian-splatting example by @mvaligursky in #8738 * Allow trycloudflare.com in Vite allowedHosts by @mvaligursky in #8750 * HTTPS dev mode for mobile / XR testing by @mvaligursky in #8753 * Inline example runtime urls by @kpal81xd in #8746 * Streamline HTTPS dev cert workflow by @kpal81xd in #8759 * Overhaul examples mobile view by @kpal81xd in #8766 * Support cross-origin example iframes by @kpal81xd in #8768 * Remove unified toggle from gsplat global-sorting by @mvaligursky in #8780 * Portal select dropdowns to body by @kpal81xd in #8781 * Example credits, keybinds, and description overlay by @kpal81xd in #8783 * Add @credit tags and relax credit field requirements by @mvaligursky in #8800 * Hide description overlay in fullscreen mode by @mvaligursky in #8801 * Set unified=false in gsplat-legacy picking example by @mvaligursky in #8804 ## Engine Dependencies * Update all npm dependencies in #8630 * Update all npm dependencies in #8751 * Update all npm dependencies in #8799 * Update all npm dependencies (major) in #8631 ### Contributors * @cabanier * @gameatp * @kpal81xd * @lucaheft * @MAG-AdrianMeredith * @MattiasVik * @mvaligursky * @slimbuck * @tanaydimri * @willeastcott ## New Contributors * @MattiasVik made their first contribution in #8699 * @cabanier made their first contribution in #8733 * @tanaydimri made their first contribution in #8728 **Full Changelog**: https://github.com/playcanvas/engine/compare/v2.18.2...v2.19.0