Skip to content
TREN
Atlıhan Harita

LCC for Web User Manual

WebGPU Development Guide

Chapter 04 / 07Manual version v0.6.4English

This guide explains how to use WebGPU to render 3DGS scenes in your project and the differences from WebGL integration.

Engine Support

The WebGPU pipeline only supports the Three.js engine. CesiumJS does not support WebGPU rendering.

Version Requirements

SDK version: SDK v0.6.4+

Data Format Limitations

The WebGPU pipeline currently only supports the .lcc2 format. The .lcc format does not yet support WebGPU rendering; support will be added in future releases.

If an .lcc data path is provided, you should fall back to the WebGL renderer. The sample code implements this logic via the forceWebGL parameter:

const forceWebGL = new URL(dataPath, location.origin).pathname.endsWith('.lcc');
const renderer = await createRenderer(canvas, forceWebGL);

WebXR Limitations

Currently, the SDK only supports WebXR in the WebGL pipeline. The WebGPU pipeline does not support WebXR.

Mobile Limitations

WebGPU is not recommended for mobile devices. The SDK's WebGL pipeline currently offers better mobile performance.

Three.js Rendering Paths

Three.js has three rendering paths. Understanding their differences is essential for correct SDK integration.

Path 1: Classic WebGLRenderer (widely used)

WebGLRenderer has been the core renderer since Three.js was created and remains the most widely used choice. It is built directly on the WebGL 2 API, and its material system uses GLSL shaders.

Import:

import * as THREE from 'three';
const renderer = new THREE.WebGLRenderer();

The SDK supports this path, with both .lcc and .lcc2 formats.

Path 2: New Architecture WebGPURenderer + WebGPU Backend

WebGPURenderer is the next-generation renderer for Three.js, featuring a new abstract backend architecture. It introduces a node-based material system (Node Material) and TSL (Three.js Shading Language). Shaders written in TSL are automatically compiled to WGSL or GLSL depending on the backend. When the browser supports WebGPU, WebGPURenderer uses the native WebGPU backend.

Import:

import * as THREE from 'three';
import * as THREE_WEBGPU from 'three/webgpu';
import * as THREE_TSL from 'three/tsl';

const renderer = new THREE_WEBGPU.WebGPURenderer({ canvas });
await renderer.init();

The SDK supports this path, with .lcc2 format only.

Path 3: New Architecture WebGPURenderer + WebGL 2 Backend

WebGPURenderer includes a built-in WebGL 2 backend as a fallback. When the browser does not support WebGPU, WebGPURenderer automatically switches to the WebGL 2 backend. Although it still uses WebGL 2, the material system and shader pipeline are the same as Path 2 (Node Material + TSL), with GLSL as the final compilation target.

The SDK does not support this path.

Path Comparison

PathRendererGraphics APIMaterial / Shader SystemSDK SupportSupported Formats
1. Classic WebGLWebGLRendererWebGL 2GLSL (ShaderMaterial)✅.lcc .lcc2
2. New Arch WebGPUWebGPURendererWebGPUNode Material / TSL / WGSL✅.lcc2
3. New Arch WebGL FallbackWebGPURendererWebGL 2 (backend fallback)Node Material / TSL / GLSL❌-

WebGPU vs WebGL Integration Differences

Compared to WebGL integration, WebGPU changes are concentrated in the following areas. All other business code (camera controls, performance parameters, render loop, etc.) remains identical.

1. Additional Imports: three/webgpu and three/tsl

WebGL only requires three. WebGPU requires two additional modules:

<!-- importmap -->
<script type="importmap">
{
    "imports": {
        "three": "./engine/three/three.module.js",
        "three/webgpu": "./engine/three/three.webgpu.js",
        "three/tsl": "./engine/three/three.tsl.js",
        "three/addons/": "./engine/three/jsm/"
    }
}
</script>
import * as THREE from 'three';
import * as THREE_WEBGPU from 'three/webgpu';
import * as THREE_TSL from 'three/tsl';

2. Async Renderer Creation

The WebGL renderer is created synchronously. The WebGPU renderer requires async initialization:

// WebGL (synchronous)
const renderer = new THREE.WebGLRenderer({ canvas, antialias: false });

// WebGPU (asynchronous)
const renderer = new THREE_WEBGPU.WebGPURenderer({ canvas, antialias: false });
await renderer.init();

We recommend using the createRenderer utility function provided in the SDK examples, which implements a complete fallback chain:

  1. Browser does not support navigator.gpu → fall back to WebGLRenderer
  2. Cannot get GPU adapter → fall back to WebGLRenderer
  3. WebGPURenderer silently falls back to WebGL 2 backend after init (no GPUDevice) → destroy and fall back to WebGLRenderer
  4. All checks pass → return a true WebGPU renderer
import { createRenderer } from './engine/three/util.js';

const renderer = await createRenderer(canvas, forceWebGL);
const isWebGPU = renderer.isWebGPURenderer === true;

3. Disable sRGB Output and Tone Mapping

In WebGPU mode, you must disable Three.js default sRGB output conversion and tone mapping. The default sRGB output adds an extra linear → sRGB conversion pass that conflicts with 3DGS premultiplied alpha blending, causing color inaccuracy:

if (isWebGPU) {
    renderer.outputColorSpace = THREE.LinearSRGBColorSpace;
    renderer.toneMapping = THREE.NoToneMapping;
}

WebGL mode does not need this.

4. LCCRender.load Parameters

WebGPU requires additional renderLibWebgpu and renderLibTSL parameters:

// WebGL
LCCRender.load({
    camera, scene, renderer,
    dataPath,
    renderLib: THREE,
    canvas: renderer.domElement,
    // ...
});

// WebGPU
LCCRender.load({
    camera, scene, renderer,
    dataPath,
    renderLib: THREE,
    renderLibWebgpu: THREE_WEBGPU,  // new
    renderLibTSL: THREE_TSL,        // new
    canvas: renderer.domElement,
    // ...
});

GPU Device Limits Configuration

When creating a WebGPURenderer, it is recommended to query the GPU adapter's actual limits and pass requiredLimits to fully utilize device capabilities:

const adapter = await navigator.gpu.requestAdapter();
const requiredLimits = {
    maxBufferSize: adapter.limits.maxBufferSize,
    maxStorageBufferBindingSize: adapter.limits.maxStorageBufferBindingSize,
    maxTextureDimension2D: adapter.limits.maxTextureDimension2D
};

const renderer = new THREE_WEBGPU.WebGPURenderer({
    canvas,
    antialias: false,
    requiredLimits
});
await renderer.init();

Complete WebGPU Example

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>LCC Three.js WebGPU Demo</title>
    <style>
        html, body { margin: 0; padding: 0; height: 100%; overflow: hidden; }
        canvas { display: block; }
    </style>
</head>
<body>
    <script type="importmap">
    {
        "imports": {
            "three": "./engine/three/three.module.js",
            "three/webgpu": "./engine/three/three.webgpu.js",
            "three/tsl": "./engine/three/three.tsl.js",
            "three/addons/": "./engine/three/jsm/"
        }
    }
    </script>
    <script type="module">
        import * as THREE from 'three';
        import * as THREE_WEBGPU from 'three/webgpu';
        import * as THREE_TSL from 'three/tsl';
        import { createRenderer } from './engine/three/util.js';
        import { LCCRender } from '../sdk/lcc-sdk-three.js';

        const dataPath = 'https://your-server.com/assets/scene/meta.lcc2';

        // WebGPU only supports .lcc2; force WebGL for .lcc assets
        const forceWebGL = new URL(dataPath, location.origin).pathname.endsWith('.lcc');

        const scene = new THREE.Scene();
        const canvas = document.createElement('canvas');
        document.body.appendChild(canvas);

        // Create renderer with automatic WebGPU → WebGL fallback
        const renderer = await createRenderer(canvas, forceWebGL);
        const isWebGPU = renderer.isWebGPURenderer === true;
        renderer.setSize(window.innerWidth, window.innerHeight);
        renderer.setPixelRatio(window.devicePixelRatio);
        renderer.setClearColor(0x000000);

        // Disable sRGB output for WebGPU to avoid color space conflicts with 3DGS
        if (isWebGPU) {
            renderer.outputColorSpace = THREE.LinearSRGBColorSpace;
            renderer.toneMapping = THREE.NoToneMapping;
        }

        const camera = new THREE.PerspectiveCamera(
            45, window.innerWidth / window.innerHeight, 0.1, 150000
        );
        camera.position.set(0, 2, 0);

        // Load 3DGS scene
        const lccObj = LCCRender.load({
            camera, scene, renderer,
            dataPath,
            renderLib: THREE,
            renderLibWebgpu: THREE_WEBGPU,
            renderLibTSL: THREE_TSL,
            canvas: renderer.domElement,
            useEnv: true,
            useIndexDB: true,
            useLoadingEffect: true,
            appKey: null  // replace with your key
          },
          (mesh) => { console.log('Loaded', mesh); },
          (percent) => { console.log('Loading ' + (percent * 100).toFixed(1) + '%'); },
          () => { console.log('Loading failed'); }
        );

        // Configure performance parameters
        lccObj.setStartLod(0);
        lccObj.setMaxSplats(8000000);
        lccObj.setMaxNodeSplats(4000000);
        lccObj.setMaxDistance(200);

        window.addEventListener('resize', () => {
            camera.aspect = window.innerWidth / window.innerHeight;
            camera.updateProjectionMatrix();
            renderer.setSize(window.innerWidth, window.innerHeight);
        });

        renderer.setAnimationLoop(() => {
            LCCRender.update();
            renderer.render(scene, camera);
        });
    </script>
</body>
</html>

This chapter is from the official XGRIDS user manual. Official source (opens in a new tab) · Updated:

XGRIDS Türkiye

We're With You During Setup and in the Field

Atlıhan Harita provides training and technical support for device setup, firmware updates, software use, and field applications. Send us your question and our specialist will get back to you.

+90 216 546 0804