Skip to content

Lazy subsystems

Fabric’s size is a build outcome, not just lean code. Three mechanisms decide what a given scene ships, in order of effect.

The largest mechanism is ordinary tree-shaking. The main entry exports only Camera, Scene, View, Text and Mesh as named, side-effect-free exports; shapes, modifiers and composers sit on direct import paths. Anything not imported is absent.

On top of that, each subsystem initialises behind a dynamic import(), so it becomes its own chunk that is fetched only on first use:

SubsystemPulled in by
SDF engine, and the grid and SDF shadersthe first shape mounting
Mesh engine and the glTF loadera <Mesh>
Text enginea <Text>
Stats overlaystats
AxesshowAxes
Point-cloud loadergeometryUrl
Background mipsglass, a <Mesh>, or the reflection and refraction flags
Gizmoa <Rig>
Blita drag-resize (pre-warmed at boot)

The particle shaders are the exception — they are statically imported because boot creates the particle buffers, so they land in the eager chunk whether or not a particle component mounts. That is not dead code.

A build plugin removes marked regions from fabric’s source when the app’s own source never turns the feature on. It scans your source files, .phaze files included and comments removed, for fingerprints rather than tracking imports, and scans again when you edit one in dev. The strip runs on the source, before vite-plugin-glsl resolves #include lines and minifies, so dev renders the shader a build ships.

  • SDF_TYPE — one per primitive: its #include, its dispatch case in the SDF shader and its envelope case in the grid shader. [2d] is the whole 2D block.
  • SHADER — a material branch, in both fs and fs_fast.
  • MODIFIER — a modifier branch in the distance function.
  • GLASS_FEATURE — glass is not one feature but fifteen. Silhouette, sheen, refraction, slab refraction, reflection, metalness, iridescence, grain, distortion, clearcoat, chromatic aberration, attenuation, anisotropy, gloss and bicubic are each their own region, stripped independently.
  • MESH_FEATURE — mesh glass, roughness, phong.
  • RIG_FEATURE — the drag rig, down to its type definitions.
  • COND_FEATURE — removes the import() site itself, so the chunk is never emitted at all.

The conditional strips are driven by regular expressions over your source, and they are deliberately conservative: anything but a literal false counts as on.

stats={false} // stripped
stats // kept
stats={showStats} // kept — not a literal false

The same applies to showAxes and to enableReflection / enableRefraction. geometryUrl is kept whenever it is mentioned at all. Glass detection keys on shader: 'glass' and on the presence of a <Mesh tag.

Primitives and modifiers key on their JSX tag: <Box followed by a space, / or >, so <Box does not keep BoxFrame, and <Twist for the twist branch. The number behind a shape’s tag comes from the type: N, line in its own component. A material branch keys on shader: 'phong' and the like. With no shape tag anywhere nothing is stripped, and any 2D shape keeps all three 2D primitives.

If a feature is being stripped that you need — because you enable it in a way the scan cannot see — pin it by name in the plugin’s keep list.

The plugin’s older approach cut distance functions out of the bundled chunk by counting braces. It over-stripped under multi-environment builds and cannot match minified text, so consumers run regionsOnly: true and it never runs. The SDF_TYPE, SHADER and MODIFIER regions do the same job on the source.