BabylonJS/Babylon.js · error

GPU particles cannot work without a full Engine. ThinEngine

Error message

GPU particles cannot work without a full Engine. ThinEngine is not supported

What it means

The WebGL2 GPU particle update pipeline relies on engine capabilities that only the full Engine class provides (specifically setState used inside enableEffect/update flow). preUpdateParticleBuffer casts this._engine to Engine and verifies engine.setState exists; if the system was created against a ThinEngine (or another engine lacking setState), it cannot run the GPU update pass and throws.

Source

Thrown at packages/dev/core/src/Particles/webgl2ParticleSystem.pure.ts:224

    }

    /** @internal */
    public bindDrawBuffers(index: number, effect: Effect, indexBuffer: Nullable<DataBuffer>): void {
        if (indexBuffer) {
            this._engine.bindBuffers(this._renderVertexBuffers, indexBuffer, effect);
        } else {
            this._engine.bindVertexArrayObject(this._renderVAO[index], null);
        }
    }

    /** @internal */
    public preUpdateParticleBuffer(): void {
        const engine = this._engine as Engine;

        this._engine.enableEffect(this._updateEffect);

        if (!engine.setState) {
            throw new Error("GPU particles cannot work without a full Engine. ThinEngine is not supported");
        }
    }

    /** @internal */
    public updateParticleBuffer(index: number, targetBuffer: Buffer, currentActiveCount: number): void {
        this._updateEffect.setTexture("randomSampler", this._parent._randomTexture);
        this._updateEffect.setTexture("randomSampler2", this._parent._randomTexture2);

        if (this._parent._flowMap) {
            this._updateEffect.setTexture("flowMapSampler", this._parent._flowMap);
        }

        if (this._parent._sizeGradientsTexture) {
            this._updateEffect.setTexture("sizeGradientSampler", this._parent._sizeGradientsTexture);
        }

        if (this._parent._angularSpeedGradientsTexture) {
            this._updateEffect.setTexture("angularSpeedGradientSampler", this._parent._angularSpeedGradientsTexture);

View on GitHub (pinned to 0592b347b8)

Solutions

  1. Create GPUParticleSystem with a full Engine (new Engine(canvas, ...)), not ThinEngine.
  2. Use the CPU ParticleSystem when only a ThinEngine is available.
  3. Feature-check engine.setState before wiring GPU particles and fall back to CPU particles.

Example fix

// before
const engine = new ThinEngine(canvas);
const ps = new GPUParticleSystem("p", { capacity: 1000 }, engine);
// after
const engine = new Engine(canvas, true);
const ps = new GPUParticleSystem("p", { capacity: 1000 }, engine);
Defensive patterns

Strategy: validation

Validate before calling

if (!("setState" in engine)) {
  throw new Error("GPUParticleSystem requires a full Engine, not ThinEngine");
}

Type guard

function supportsGpuParticles(engine: AbstractEngine): engine is Engine {
  return typeof (engine as Engine).setState === "function";
}

Prevention

When it happens

Trigger: GPUParticleSystem created with a ThinEngine (or engine without a setState method) then running its update loop: preUpdateParticleBuffer() checks !engine.setState and throws.

Common situations: Using GPUParticleSystem with ThinEngine/NullEngine (e.g. headless tests, minimal custom engines) where only ThinEngine is available; constructing the platform class manually against a thin engine.

Related errors


AI-assisted analysis of BabylonJS/Babylon.js@0592b347b8 (2026-08-30). Data as JSON: /api/errors/b93969cd4ebad619. Report an issue: GitHub.