Renderer · interface

CreateRendererOptions

Explained in Hello world.

interface CreateRendererOptions
import type { CreateRendererOptions } from '@driftengine/core';

Properties

NameTypeDescription
preferWebGpureadonlyoptionalbooleanWhether to try WebGPU at all. Defaults to true: WebGPU is the backend this engine is built on now, and WebGL2 is the fallback beneath it rather than the path of record.
More

It defaulted to false for as long as the second backend was under construction, and the comment holding it there pointed at a numbered task in a plan. That is how a default outlives its reason: by the time it was wrong, every consumer in the workspace was already passing true, so the conservative default was overridden everywhere and true nowhere.

What this costs is an await on requestAdapter in a boot that previously had none, on every consumer that never mentioned a backend. Pass false to decline it and get the old boot exactly. What would make it wrong is an adapter request slow enough to be felt at startup on a device that was never going to yield a device anyway; nothing measured here shows that, and the fallback is silent when it happens.

pipelinereadonlyoptionalRenderPipelineWhich pipeline to draw with. Defaults to 'forward'.
More

'gpu-driven' selects the cluster pipeline: instances and clusters culled on the device against a depth pyramid built this frame, a visibility buffer, and one shading dispatch per material. It needs indirect draws and dispatches, which WebGL2 does not have.

Asking for it where it cannot run throws. Not a warning and not a fallback: a silent fallback means a consumer ships believing they have a pipeline they do not, and the difference only shows as a frame time on somebody else's machine. The throw carries the reason in words, from gpuDrivenRefusal.

This includes a WebGPU request that fell back, which is the case a check written against the asked-for backend would miss: every fallback path here is a path where the pipeline became unavailable after the choice was made.

probeShadersreadonlyoptionalreadonly string[]Shader sources the acceptance probe compiles before a device is accepted. Empty by default.
More

Empty because it was measured, not assumed. On an AMD Radeon RX 9070 XT (RADV, ANGLE over Vulkan) the draw-and-read check alone costs 4.2–5.4 ms and catches a device that validates everything and rasterises nothing; adding all 46 generated WGSL modules costs a further 58–62 ms on every boot and catches the class that packages/core/src/render/shaders/uniformStride.test.ts already catches at test time, by reading the committed files. Sixty milliseconds of every player's startup is a poor price for a second copy of a test.

What would make an empty default wrong is a device-specific compile failure in a shader that test cannot model — which is precisely what the iOS black screen was before that test existed. A consumer shipping to a platform nobody has certified should pass the real set and take the cost: a slower boot is worth more than a black screen. scripts/probe-check.mjs re-measures both figures on any machine.

backendTimeoutMsreadonlyoptionalnumberHow long the WebGPU path may take before WebGL2 is answered instead. Infinity waits forever.
More

A preference that cannot fall back is not a preference. Every other refusal arrives as an answer — a null adapter, a throw, a device that cannot draw — and each lands on WebGL2 with a reason. A request that never settles is none of those and has nothing under it: the boot stops at an await, nothing throws, and the player is left looking at the window the game was going to be drawn in. See BACKEND_TIMEOUT_MS for the default and what it was measured against.

splashreadonlyoptionalboolean | SplashOptionsThe engine badge shown over the page until the first frame reaches the screen.
More

On by default, and that default is the feature. A web-delivered game has no shell to show one for it — drift-package opens a window for exactly this and a browser tab has nowhere to put it — so the engine mounts it here, before the device probe, and a game inherits it by calling the function it was already calling. See ui/splash.ts.

false declines it. Pass that for anything that is not a game booting: a site whose canvas is one section of a page, a tool that opens into its own interface, a demo harness. Those are the cases where a full-screen badge is an interruption rather than a cover, and they are the reason this is a decision rather than a behaviour.

?splash=0 and ?splash=1 override it either way without a code change, which is how the badge is looked at on a deployed build.

highDynamicRangeDisplayreadonlyoptional() => booleanWhether the display shows a high dynamic range, asked only where highDynamicRange was. The browser's (dynamic-range: high) query by default; a host with no DOM, or one that knows better about its own display, answers instead. See displayRange.ts.