Parent: Roadmap to v2 (#95)
Phase 1: Reduce | Effort: Medium | Depends on: #96 (monorepo decision)
Problem
27 files + 5 production dependencies (~17% of the codebase) for a feature most projects won't use. Even though it's lazy-loaded via OptionalFeatures, it's still:
- In
node_modules (inflating install time with Three.js + R3F + postprocessing)
- In the mental model (developers see
webgl/, GlobalCanvas, FlowMap, Fluid, PostProcessing)
- Leaking dev tools into production:
// lib/webgl/utils/fluid/index.tsx:1-5 — Theatre.js imported unconditionally
import { types } from '@theatre/core'
import { useCurrentSheet } from '@/dev/theatre'
import { useTheatre } from '@/dev/theatre/hooks/use-theatre'
import { Fluid } from '@/webgl/utils/fluid/fluid-sim'
@theatre/core and @/dev/theatre are imported at the top level — not gated behind process.env.NODE_ENV and not dynamically imported. This means Theatre.js ships in production bundles for any page using the fluid simulation.
Additionally, fluid-sim.ts is 724 LOC (the largest single file in the codebase) with WebGL shader code as template literals and zero documentation on the algorithm or shader passes.
Proposal
Before: After:
lib/webgl/ # 27 files @satus/webgl # Separate package
components/effects/ # 6 files (uses WebGL) bun add @satus/webgl
package.json: package.json:
three (none — 5 fewer prod deps)
@react-three/fiber
@react-three/drei
postprocessing
tunnel-rat
The OptionalFeatures component already handles lazy loading:
// lib/features/index.tsx — Already dynamic, just needs a package check
const LazyGlobalCanvas = dynamic(
() => import('@/webgl/components/global-canvas').then((mod) => ({
default: mod.LazyGlobalCanvas,
})),
{ ssr: false }
)
This would become:
// After — only loads if @satus/webgl is installed
let LazyGlobalCanvas: ComponentType | null = null
try {
LazyGlobalCanvas = dynamic(
() => import('@satus/webgl/global-canvas'),
{ ssr: false }
)
} catch { /* package not installed */ }
Also Fix: Theatre.js Production Leak
Regardless of extraction, the Theatre.js imports in fluid/index.tsx and flowmaps/index.tsx need to be gated:
// Before — always imported
import { types } from '@theatre/core'
import { useCurrentSheet } from '@/dev/theatre'
// After — development only
const theatreTypes = process.env.NODE_ENV === 'development'
? require('@theatre/core').types : null
Or better: extract Theatre.js integration into a separate hook that's only imported in dev.
Impact on Dev Time
| Metric |
Before |
After |
bun install on non-WebGL project |
~15 sec slower (Three.js tree) |
No Three.js deps |
| Production bundle (non-WebGL) |
Theatre.js leaks in |
Zero WebGL code |
| Mental model for new developers |
Must understand webgl/ exists |
Doesn't exist unless added |
Tasks
Parent: Roadmap to v2 (#95)
Phase 1: Reduce | Effort: Medium | Depends on: #96 (monorepo decision)
Problem
27 files + 5 production dependencies (~17% of the codebase) for a feature most projects won't use. Even though it's lazy-loaded via
OptionalFeatures, it's still:node_modules(inflating install time with Three.js + R3F + postprocessing)webgl/,GlobalCanvas,FlowMap,Fluid,PostProcessing)@theatre/coreand@/dev/theatreare imported at the top level — not gated behindprocess.env.NODE_ENVand not dynamically imported. This means Theatre.js ships in production bundles for any page using the fluid simulation.Additionally,
fluid-sim.tsis 724 LOC (the largest single file in the codebase) with WebGL shader code as template literals and zero documentation on the algorithm or shader passes.Proposal
The
OptionalFeaturescomponent already handles lazy loading:This would become:
Also Fix: Theatre.js Production Leak
Regardless of extraction, the Theatre.js imports in
fluid/index.tsxandflowmaps/index.tsxneed to be gated:Or better: extract Theatre.js integration into a separate hook that's only imported in dev.
Impact on Dev Time
bun installon non-WebGL projectTasks
lib/webgl/into@satus/webglpackagecomponents/effects/animated-gradient/(depends on WebGL)OptionalFeaturesto conditionally loadWrappercomponent to optionally supportwebglproppackage.json@satus/webglsetup and usage