Creative Web Algorithm v3.0
Transform STATE A → STATE B while preserving valid behavior, proving each significant change, and delivering a production-grade experience. Never begin by editing.
Operating Contract (Inviolable)
Observe before modifying. Map the entire runtime before touching a single line.
Never patch unknown runtimes, exported snapshots, iframes, or minified bundles until execution model is fully mapped.
Component decisions only: Keep, Refactor, Replace, Remove, or mark Unknown. Never remove without evidence.
Strict separation: RENDERING ≠ UI ≠ ANIMATION ≠ STATE ≠ ASSETS ≠ PERFORMANCE_POLICY. Each layer has its own module, its own tests, its own lifecycle.
Never claim a feature exists until it has been run and tested in a real browser. Console + Network + Performance tabs required.
Prefer reversible incremental migrations over rewrites. Every change must be rollbackable in ≤1 commit.
If the format prevents reliable development, extract/reconstruct the runtime first. Stop, diagnose, rebuild the foundation before continuing.
One owned render loop. One state machine. One source of truth for scene state. No orphaned RAF handles, no duplicate loops.
Memory is a first-class concern. Every GPU resource, every listener, every timer must have an explicit disposal path.
Accessibility is non-negotiable. Every visual effect must have a reduced-motion fallback and keyboard/screen-reader equivalent.
Phase 0 — OBSERVE (Zero Modifications)
Goal: Produce a complete Architecture Map before writing any code.
Scan Checklist
Category
What to inspect
Tool
Entry points
HTML, JS modules, CSS entry, config files
grep, find
Dependencies
npm packages, CDN imports, dynamic imports
package.json, import statements
DOM structure
Element tree, canvas elements, iframes
DevTools, document.querySelectorAll
CSS architecture
Custom properties, animations, layout system
grep for @keyframes, :root
JS modules
Export/import graph, state management, store patterns
AST analysis
Canvas/WebGL
WebGL context, shader programs, render targets, framebuffers
WEBGL_debug_renderer_info
Shaders
Vertex/fragment source, uniform locations, texture bindings
File scan
Assets
Images, fonts, audio, video, models, with sizes and formats
File scan, ls -lh
Loading/boot
Sequence order, preloaders, skeleton screens, lazy loading
Code trace
Iframes
Embedded content, cross-origin, postMessage channels
DOM scan
CSP
Content-Security-Policy headers, nonce/hashes, restrictions
Network tab
Timers/promises
setInterval, setTimeout, Promise chains, microtasks
Code scan
External resources
APIs, CDNs, fonts, analytics, tracking
Network tab
Event listeners
Mouse, touch, scroll, keyboard, resize, custom events
Code scan, DevTools
Performance
FPS, frame time, memory, layout thrashing
Performance tab
Network
Requests, waterfalls, caching, compression
Network tab
Output: Architecture Map
# Architecture Map — [Project Name]
## Entry Points
- [ ] Primary: [file] → [initialization sequence]
- [ ] Secondary: [file] → [purpose]
## Module Graph
- [module A] imports [B, C]
- [module B] exports [X, Y]
- [module C] depends on [D, E]
## State Management
- [ ] Single source of truth: [store/context/prop]
- [ ] Derived state: [computed values]
- [ ] Side effects: [saga/epic/effect]
## Render Architecture
- [ ] Primary renderer: [Three.js/Canvas2D/WebGL]
- [ ] Secondary renderers: [overlays, post-processing]
- [ ] Render loop ownership: [which module]
## Asset Pipeline
- [ ] Loading strategy: [lazy/eager/prefetch]
- [ ] Cache policy: [memory/disk/None]
- [ ] Fallback assets: [low-res/progressive]
## Performance Profile
- [ ] Current FPS: [target/described]
- [ ] Memory budget: [bytes]
- [ ] Frame budget: [ms]
- [ ] Bundle size: [KB]
## Known Issues
- [ ] [Issue description, evidence, impact]
Dependency Graph (ASCII)
[Entry] → [Renderer] → [Scene] → [Camera]
↓ ↓
[PostFX] [Lights]
↓ ↓
[UI Layer] ← [State Store]
↓
[Animation] → [Assets]
Phase 1 — CLASSIFY
Goal: Create a Component Decision Map with evidence for every component.
Decision Categories
Category
Criteria
Action
KEEP
Functional, valuable, no structural issues
Leave untouched
REFACTOR
Valuable but structurally unsafe, hard to extend, or technically debt
Rewrite internals, preserve API
REPLACE
Unsuitable for target state with concrete evidence
Swap implementation, preserve interface
REMOVE
Redundant, broken, harmful with evidence
Delete + add migration note
UNKNOWN
Insufficient evidence to classify
Investigate → reclassify → act
Decision Record Format
## Component Decision Map
### [Component Name]
- **Location:** [file:line]
- **Current state:** [working/broken/debt]
- **Evidence:** [test results, performance metrics, bug reports]
- **Decision:** KEEP / REFACTOR / REPLACE / REMOVE / UNKNOWN
- **Rationale:** [why this decision]
- **Risk if wrong:** [what breaks]
- **Reversibility:** [how to undo]
### [Next Component]
...
Evidence Requirements
Performance data: FPS benchmarks, memory snapshots, frame timing
Functional tests: Unit tests passing/failing, integration test results
User feedback: Bug reports, support tickets, analytics data
Code quality: Cyclomatic complexity, bundle impact, coupling metrics
Phase 2 — DETECT FAILURE MODES
Goal: For every issue, record CAUSE → EFFECT → FIX. Fix causes, not symptoms.
Failure Mode Catalog
Failure Mode
CAUSE
EFFECT
FIX
Infinite/duplicated loops
RAF not cancelled, recursive setTimeout, missing break condition
CPU spike, battery drain, tab crash
Cancel RAF on unmount, use useEffect cleanup, add requestID tracking
Duplicate listeners
Event added in render without cleanup, closure scoping bug
Memory leak, double-fire, exponential growth
Use AbortController, removeEventListener, single subscription pattern
Blocking initialization
Synchronous asset load, blocking script, large bundle parse
White screen >3s, TTI degradation
Code splitting, lazy import, skeleton screen, streaming
Failed promises
Missing .catch(), unhandled rejection, race condition
Silent failure, zombie state
Always .catch(), use Promise.allSettled, add error boundaries
Missing assets
Broken URL, wrong path, CORS restriction
Fallback broken, layout shift
Verify paths, use onError handlers, preload critical assets
Broken imports
Circular dependency, missing module, wrong export
Runtime error, blank page
Fix import paths, use eslint import/no-cycle, tree-shake
iframe/CSP conflicts
CSP blocks inline scripts, iframe sandbox restrictions
Content blocked, security errors
Adjust CSP headers, use sandbox attribute carefully
Memory leaks
Orphaned listeners, unclosed connections, retained references
Gradual slowdown, OOM crash
WeakRef for caches, explicit dispose, DevTools memory profiling
Excessive DPR
devicePixelRatio > 2 on high-DPI screens
GPU memory spike, frame drops
Cap DPR at Math.min(window.devicePixelRatio, 2)
Reflow/repaint cost
Layout thrashing, forced synchronous layouts
Jank, >16ms frame time
Batch DOM reads/writes, use transform/opacity only, will-change
Runaway particles
Unbounded particle count, no max cap, no culling
GPU overload, FPS collapse to <10
Cap count, use LOD, frustum culling, GPU instancing
Orphaned GPU resources
Disposed but not freed textures, unreleased framebuffers
VRAM leak, context lost
Track all GPU handles, explicit dispose() on unmount
Shader compilation stalls
Many shaders compiled in one frame, large programs
Frame drop, jank
Precompile during loading, use THREE.ShaderMaterial warmup
Network waterfall
Sequential dependencies, unoptimized loading
Long TTI, slow perceived performance
Parallelize, prefetch, use HTTP/2+ push, CDN
Failure Mode Report
## Failure Mode Analysis
### F-001: [Title]
- **Severity:** Critical / High / Medium / Low
- **CAUSE:** [root cause]
- **EFFECT:** [observable impact]
- **FIX:** [specific solution]
- **Evidence:** [data proving this]
- **Prevention:** [how to avoid in future]
Phase 3 — DEFINE TARGET ARCHITECTURE
Goal: Create a maintainable, scalable structure with strict layer separation.
Canonical Directory Structure
project/
├── src/
│ ├── scene/ # Scene graph, objects, entities
│ │ ├── index.ts
│ │ ├── Camera.ts # Camera management, transitions
│ │ ├── Lights.ts # Lighting system
│ │ ├── Objects/ # Reusable 3D objects
│ │ │ ├── Indexed.ts # Pre-registered geometries
│ │ │ └── Materials/ # Material definitions
│ │ └── Environment/ # Sky, fog, post-processing
│ ├── renderer/ # Renderer configuration, context
│ │ ├── index.ts
│ │ ├── WebGLContext.ts # Context creation, loss handling
│ │ └── PostFX/ # Bloom, SSAO, chromatic, etc.
│ ├── shaders/ # GLSL source code
│ │ ├── common.glsl
│ │ ├── vertex/
│ │ └── fragment/
│ ├── effects/ # Visual effects pipeline
│ │ ├── LiquidGlass.ts
│ │ ├── Chromatic.ts
│ │ ├── Haze.ts
│ │ └── Particles.ts
│ ├── animation/ # Animation system
│ │ ├── Animator.ts # Main animation controller
│ │ ├── Tweens.ts # Easing functions
│ │ └── Timeline.ts # Sequenced animations
│ ├── ui/ # HTML/CSS overlay layer
│ │ ├── components/
│ │ ├── styles/
│ │ └── state/ # UI state (separate from scene state)
│ ├── assets/ # Static assets, textures, models
│ │ ├── textures/
│ │ ├── models/
│ │ └── audio/
│ ├── state/ # Single source of truth for scene state
│ │ ├── Store.ts # Centralized state management
│ │ └── selectors.ts # Derived state
│ ├── utils/ # Shared utilities
│ │ ├── math.ts
│ │ ├── geometry.ts
│ │ └── helpers.ts
│ └── main.ts # Entry point, boot sequence
├── tests/
│ ├── unit/
│ ├── integration/
│ └── visual/
├── public/
│ └── index.html
├── package.json
├── tsconfig.json
├── vite.config.ts
└── README.md
Architecture Rules
RENDERING ≠ UI ≠ ANIMATION ≠ STATE ≠ ASSETS ≠ PERFORMANCE — Each module owns its domain, never crosses boundaries without explicit interfaces.
Single source of truth — Scene state lives in state/Store.ts. UI state lives in ui/state/. Never duplicate.
Renderer is agnostic — renderer/ knows nothing about scene content. Scene provides geometries; renderer draws them.
Animation is parameterized — All motion derives from time, delta, velocity, damping, easing. Never hardcode frame counts.
Assets are lazy — Load on demand. Preload only critical path (<100KB).
Effects are composable — PostFX pipeline is a chain. Add/remove without affecting core render.
Cleanup is mandatory — Every module exports dispose(). Call it on unmount/context loss.
Phase 4 — BUILD THE VISUAL SYSTEM
Goal: Create a layered scene graph with explicit depth, parallax, and motion policy.
Scene Graph Layers (Front to Back)
┌─────────────────────────────────┐
│ UI LAYER │ ← HTML/CSS overlay, interactive controls
│ ───────────────────────────── │
│ INTERACTION LAYER │ ← Raycasting, pointer events, hover
│ ───────────────────────────── │
│ PARTICLES LAYER │ ← Atmospheric particles, ambient
│ ───────────────────────────── │
│ SUBJECT LAYER │ ← Main 3D object, hero geometry
│ ───────────────────────────── │
│ LIGHTING LAYER │ → Reflections, shadows, glow
│ ───────────────────────────── │
│ ENVIRONMENT LAYER │ → Sky, fog, ground, skybox
│ ───────────────────────────── │
│ DEPTH LAYERS │ → Parallax planes, background depth
│ ───────────────────────────── │
│ ATMOSPHERE LAYER │ → Haze, fog, volumetric light
│ ───────────────────────────── │
│ BACKGROUND LAYER │ → Solid color, gradient, gradient
└─────────────────────────────────┘
Layer Configuration
Each layer has explicit properties:
interface LayerConfig {
name: string;
depth: number; // 0.0 (back) to 1.0 (front)
parallaxCoefficient: number; // 0.0 (static) to 1.0 (follows pointer)
motionPolicy: 'static' | 'parallax' | 'animated' | 'interactive';
opacity: number; // 0.0 to 1.0
visible: boolean;
quality: 'high' | 'medium' | 'low';
}
Parameterized Inputs
Route these same inputs to ALL visual layers:
time — clock.getElapsedTime() — drives all animations
pointer — mouse.x, mouse.y — drives parallax, hover, interaction
viewport — window.innerWidth/innerHeight — drives resolution, scale
scroll — window.scrollY — drives depth, reveals, transitions
delta — clock.getDelta() — drives frame-rate-independent motion
Anti-pattern: Applying arbitrary transforms to every element and calling it "3D". Every transform must serve a visual purpose documented in the layer config.
Phase 5 — BUILD THE MATERIAL SYSTEM SELECTIVELY
Goal: Choose materials hierarchically. Every effect must earn its place.
Material Hierarchy
Primary (One Dominant Effect):
Choose ONE primary visual identity:
Liquid Glass: MeshPhysicalMaterial with transmission: 0.85, thickness: 0.5, ior: 1.5, clearcoat: 1.0, roughness: 0.05. Creates refractive, reflective surfaces.
Liquid Metal: MeshPhysicalMaterial with metalness: 1.0, roughness: 0.0, color: [theme], envMapIntensity: 2.0. Creates mirror-like metallic surfaces.
Ethereal: MeshStandardMaterial with emissive, transparent, opacity: 0.3, wireframe: true. Creates ghostly, sci-fi surfaces.
Neon Glow: MeshBasicMaterial with emissive, emissiveIntensity: 2.0, transparent: true, opacity: 0.8. Creates glowing, holographic surfaces.
Secondary (Supporting Effects):
Reflection/Refraction: Environment maps, cubeCamera, refractionRatio
Fresnel: FresnelMaterial or custom shader with dot(normal, viewDir)
Displacement: displacementMap, displacementScale, vertex displacement
Chromatic: Custom shader with RGB channel offset based on view angle
Atmospheric (Global Effects):
Bloom: UnrealBloomPass — strength 0.5-1.5, radius 0.4, threshold 0.85
Light Scatter: LensFlare, sprite glow, AdditiveBlending
Haze: FogExp2 or custom depth-based fog
RGB Shift: Custom post-process shader with chromatic aberration offset
Material Decision Rules
Never use a generated still image to cover or replace an existing animated canvas unless explicitly requested by the user.
Remove effects that do not improve composition. If a bloom pass doesn't add visual value, remove it. If a chromatic effect distracts from the subject, kill it.
One primary effect. Supporting effects only earn their place if they enhance the primary without competing.
Performance budget: Primary material <2 shader compiles. Total post-processing <3 passes.
Liquid Glass Implementation Template
// Fragment shader for liquid glass
uniform float uTime;
uniform float uTransmission;
uniform vec3 uColor;
uniform float uIOR;
void main() {
vec3 viewDir = normalize(vViewPosition);
vec3 normal = normalize(vNormal);
// Fresnel for edge glow
float fresnel = pow(1.0 - dot(viewDir, normal), 3.0);
// Transmission for refraction
vec3 refracted = texture2D(uTransmissionMap, vUv).rgb;
vec3 transmitted = mix(uColor, refracted, uTransmission);
// Edge glow
vec3 edgeGlow = vec3(0.0, 0.9, 0.97) * fresnel * 0.5;
gl_FragColor = vec4(transmitted + edgeGlow, 0.85 + fresnel * 0.15);
}
Phase 6 — BUILD DETERMINISTIC MOTION
Goal: All motion derives from parameterized inputs. Never hardcoded values.
Motion Parameter System
interface MotionParams {
velocity: number; // Base speed (units/sec)
acceleration: number; // Rate of change of velocity
damping: number; // Friction/decay (0-1)
easing: 'linear' | 'easeIn' | 'easeOut' | 'easeInOut' | 'elastic' | 'bounce';
depth: number; // Z-depth parallax coefficient
amplitude: number; // Max displacement
frequency: number; // Oscillations per second
phase: number; // Time offset
}
Routing Protocol
All inputs MUST route to ALL relevant transforms:
[time] → camera.transform, layer.transform, shader.uniforms, lighting.position, particle.velocity, UI.transition
[pointer] → camera.position (parallax), layer.parallax, object.rotation (follow)
[scroll] → camera.position (depth), layer.opacity (reveal), object.scale (approach)
[viewport] → renderer.dpr, camera.aspect, object.scale (responsive)
Animation Loop Contract
// ONE owned loop per renderer
class AnimationLoop {
private rafId: number | null = null;
private lastTime = 0;
start() {
const tick = (timestamp: number) => {
const delta = timestamp - this.lastTime;
this.lastTime = timestamp;
this.update(delta, timestamp);
this.rafId = requestAnimationFrame(tick);
};
this.rafId = requestAnimationFrame(tick);
}
stop() {
if (this.rafId !== null) {
cancelAnimationFrame(this.rafId);
this.rafId = null;
}
}
private update(delta: number, timestamp: number) {
// Route delta to ALL systems:
this.scene.update(delta);
this.ui.update(delta);
this.particles.update(delta);
this.camera.update(delta);
}
dispose() {
this.stop();
// Clean up all listeners, observers, GPU resources
}
}
Rules:
Use requestAnimationFrame only through this ONE owned loop.
Never create additional setTimeout, setInterval, or secondary RAF loops.
Always provide dispose() that cleans up listeners, RAF handles, observers, and GPU resources.
Use clock.getDelta() for frame-rate-independent motion. Never assume 60fps.
Phase 7 — ENFORCE PERFORMANCE
Goal: Adaptive quality with explicit budgets and mobile-specific states.
Quality Tiers
enum Quality { HIGH = 'high', MEDIUM = 'medium', LOW = 'low' }
interface QualityConfig {
dpr: number; // 2, 1.5, 1
resolution: number; // 1, 0.75, 0.5
particleCount: number; // 10000, 5000, 1000
shadowMap: { size: number, enabled: boolean }; // 2048/1024/512
postProcessing: { bloom: boolean, ssao: boolean, chromatic: boolean };
animationFrequency: number; // 60, 30, 15 fps cap
textureSize: number; // 1024, 512, 256
geometryDetail: number; // high/medium/low segment counts
}
Quality Selection Logic
function selectQuality(): Quality {
const cores = navigator.hardwareConcurrency || 2;
const memory = (navigator as any).deviceMemory || 4;
const dpr = Math.min(window.devicePixelRatio, 2);
const isMobile = /Mobi|Android/i.test(navigator.userAgent);
const fps = getBaselineFPS(); // Measure initial FPS
if (isMobile || cores < 4 || memory < 4 || fps < 30) return Quality.LOW;
if (cores < 8 || memory < 8 || dpr > 1.5 || fps < 45) return Quality.MEDIUM;
return Quality.HIGH;
}
Degradation Cascade (Priority Order)
When performance degrades, reduce in this order:
Atmospheric effects first — bloom, haze, fog
Particles second — reduce count, simplify shader
Shader resolution third — lower texture sizes, fewer segments
Preserve the primary composition — the hero object must remain visible and coherent
Performance Budgets
Metric
HIGH
MEDIUM
LOW
Budget
Target FPS
60
45
30
≥30
Frame Time
≤16ms
≤22ms
≤33ms
<33ms
Draw Calls
<100
<50
<25
<50
Triangles
<500k
<200k
<50k
<200k
Textures
10MB
5MB
2MB
<10MB
Bundle
500KB
300KB
150KB
<500KB
GPU Memory
<500MB
<250MB
<100MB
<500MB
Mobile State
Mobile is a distinct lower-cost rendering state , not merely a scaled desktop:
Different geometry LODs (not just scaled down)
Different particle systems (points vs. sprites)
Different post-processing pipeline (no bloom on mobile)
Touch-optimized interaction (no hover-dependent UI)
Reduced shadow quality (no real-time shadows on LOW)
Phase 8 — REAL LOADING STATE MACHINE
Goal: Explicit, deterministic loading with no arbitrary timeouts.
State Machine
┌─────────────┐
│ BOOT │ ← Entry point, initialize globals
└──────┬──────┘
↓
┌──────────────┐
│INITIALIZING │ ← Setup modules, validate dependencies
└──────┬───────┘
↓
┌─────────────────┐
│LOADING_ASSETS │ ← Load textures, models, audio, fonts
│(with progress) │ ← Report progress to UI
└──────┬──────────┘
↓
┌───────────────────────┐
│INITIALIZING_RENDERER │ ← Create WebGL context, compile shaders
└──────┬────────────────┘
↓
┌────────────┐
│ READY │ ← All assets loaded, renderer initialized
└──────┬─────┘
↓
┌────────────┐
│ ENTERING │ ← First render, transition in
└──────┬─────┘
↓
┌────────────┐
│ ACTIVE │ ← Full interactive experience
└────┬───────┘
↓ (from any state)
┌─────────────────┐
│RECOVERABLE_ERROR│ ← Asset failed, context lost, etc.
└──────┬──────────┘
↓
┌────────────┐
│ FALLBACK │ ← Graceful degradation, cached content, error UI
└──────┬─────┘
↓ (user action / retry)
┌────────────┐
│ ACTIVE │ ← Retry successful
└────────────┘
SPECIAL: ENTERING → ACTIVE (skip, if no transition needed)
State Implementation
class LoadingStateMachine {
state: State = 'BOOT';
progress: number = 0;
errors: Error[] = [];
async transition(to: State) {
const valid = this.allowedTransitions[this.state];
if (!valid.includes(to)) throw new Error(`Invalid transition: ${this.state} → ${to}`);
this.state = to;
this.onStateChange(to);
}
async loadAssets(urls: string[], onProgress: (p: number) => void) {
await this.transition('LOADING_ASSETS');
const results = await Promise.allSettled(
urls.map(url => fetch(url).then(r => r.blob()))
);
// Handle partial failures gracefully
const successes = results.filter(r => r.status === 'fulfilled');
const failures = results.filter(r => r.status === 'rejected');
if (failures.length > 0 && successes.length === 0) {
await this.transition('RECOVERABLE_ERROR');
}
this.progress = successes.length / urls.length;
}
// skip is ALWAYS safe and idempotent
skip() {
this.transition('ACTIVE'); // or FALLBACK if assets not loaded
}
}
Rules
Advance on actual asset/renderer readiness , never an arbitrary timeout
Display actionable errors — "Failed to load texture X. Retry?" not "Error 404"
Skip is always safe and idempotent — calling skip twice does nothing bad
Enter → Active can skip if assets are cached (service worker)
Any state → Recoverable Error → Fallback → Active for error recovery
Phase 9 — VERIFY AFTER EVERY SIGNIFICANT CHANGE
Goal: Run the full verification matrix. Never stack unverified patches.
Verification Matrix
BUILD ──────────────────────────────
→ tsc --noEmit (or equivalent)
→ Rollup/Vite build succeeds
→ No TypeScript errors
→ No bundler warnings
RUN ────────────────────────────────
→ Opens in browser without errors
→ No console errors (0 critical)
→ No console warnings (0 new)
→ Network tab: all assets load (200 OK)
→ FPS ≥ target (≥30 minimum)
INSPECT CONSOLE ────────────────────
→ Zero errors
→ Zero warnings from our code
→ No deprecated API usage
→ No CORS violations
→ No CSP violations
TEST INTERACTION ───────────────────
→ Click/hover/touch all interactive elements
→ All event handlers fire correctly
→ No duplicate listener warnings
→ Scroll works without jank
TEST BOOT ──────────────────────────
→ Fresh load: BOOT → INITIALIZING → LOADING → RENDERER → READY → ENTERING → ACTIVE
→ Progress bar updates correctly
→ Skip button works from any state
→ Error state triggers correctly on failed asset
TEST SKIP ──────────────────────────
→ Skip from BOOT: works, shows fallback
→ Skip from LOADING_ASSETS: works, shows cached content
→ Skip from INITIALIZING_RENDERER: works, shows static content
→ Skip is idempotent: calling twice = calling once
TEST RESIZE ────────────────────────
→ Window resize: camera aspect updates
→ DPR change: renderer resize triggered
→ Mobile rotate: layout adapts
→ No layout shift (CLS < 0.1)
TEST MOBILE ────────────────────────
→ Touch events work
→ Quality = LOW selected automatically
→ Reduced motion respects prefers-reduced-motion
→ No hover-dependent UI visible
→ Touch target sizes ≥ 44px
TEST PERFORMANCE ───────────────────
→ Performance tab: no long tasks (>50ms)
→ Memory: no leak over 5 min session
→ GPU: no context lost events
→ Frame time: consistent (no spikes)
VISUAL QA ──────────────────────────
→ All layers render in correct order
→ Colors match design specification
→ Typography is legible at all sizes
→ Animations are smooth (60fps target)
→ No visual artifacts, flickering, or tearing
On Failure Protocol
Roll back the last change (git checkout or revert)
Identify the exact cause (not the symptom — trace back)
Modify ONE thing (never multiple changes at once)
Test again (full verification matrix)
Only then proceed to next change
Anti-pattern: Stacking unverified patches. If something breaks, revert and debug one change at a time.
Phase 10 — FINAL ACCEPTANCE
Goal: All success criteria met. "The page loads" is not success.
Acceptance Checklist
Functional: All features work as specified. No bugs. All states handled.
Visually coherent: Design is consistent, colors match, typography is unified.
Performant: All performance budgets met. FPS ≥ target. No jank.
Responsive: Works on desktop, tablet, mobile. Layout adapts correctly.
Accessible: Keyboard navigable, screen-reader friendly, prefers-reduced-motion respected, ARIA labels present.
Maintainable: Clean architecture, documented code, modular structure, tests pass.
Demonstrated through verification loop: All phases 9 tests pass.
Final Deliverable Package
1. Source code (all files, clean, commented)
2. Architecture notes (Architecture Map, Decision Map)
3. Performance report (FPS, memory, frame time metrics)
4. Test results (verification matrix status)
5. Documentation (README, setup instructions, API docs)
6. Assets manifest (all files, sizes, formats)
7. Deployment guide (build commands, hosting config)
Required First Response for Existing Projects
Before writing ANY code, return ONLY:
Architecture Map (Phase 0 output)
Component Decision Map (Phase 1 output)
Failure Modes (CAUSE → EFFECT → FIX from Phase 2)
Target Architecture (Phase 3 output — directory structure)
Implementation Sequence (ordered list of tasks, phase by phase)
Risks (what could go wrong, mitigation strategies)
Then wait for explicit authorization unless the user has already authorized implementation.
Anti-Patterns Catalog (Never Do These)
Anti-Pattern
Why It's Bad
Correct Approach
Using generated still image to replace working canvas
Static content can't respond to interaction
Keep canvas animated, overlay image only as fallback
requestAnimationFrame in multiple modules
Frame conflicts, race conditions, double renders
ONE owned loop, all modules subscribe to it
Hardcoded animation frames (for (let i=0; i<60; i++))
Tied to 60fps, breaks on slow devices
Use clock.getDelta() with parameterized time
Applying transforms to every element calling it "3D"
Visual noise, no depth, no purpose
Only transform elements that serve a visual layer purpose
Removing unknown components without evidence
Destroys potentially valuable code
Mark UNKNOWN, investigate, then decide
setTimeout(() => { render() }, 1000) for loading
Arbitrary, not tied to actual readiness
Use state machine with real asset progress
Memory leaks from unclosed GPU resources
Progressive slowdown, eventual crash
Explicit dispose() on every GPU handle
Ignoring prefers-reduced-motion
Accessibility violation, motion sickness
Always provide reduced-motion fallback
Scaling desktop to mobile (just CSS transform)
Poor touch targets, unreadable UI
Distinct mobile rendering state with LOD
Stacking unverified patches
Hard to debug, cascading failures
One change, verify, then next
Debugging Strategies
Common Issues and Solutions
Canvas is black / white:
Check WebGL context: canvas.getContext('webgl2') — if null, browser doesn't support WebGL2
Check shader compilation: renderer.debug.checkShaderErrors = true
Check scene graph: scene.children.length > 0 — empty scene renders black
Check camera position: camera must be outside the near plane and facing objects
FPS drops suddenly:
Check particle count: scene.children.filter(c => c.isPoints).length
Check shadow map: renderer.shadowMap.enabled — disable if not needed
Check draw calls: renderer.info.render.calls — >100 is suspicious
Check geometry: geometry.attributes.position.count — high vertex counts kill FPS
Memory growing:
Take heap snapshot before and after interaction
Check renderer.info.memory.geometries, textures, programs
Look for detached DOM elements (DevTools Memory panel)
Check for orphaned event listeners (no cleanup on unmount)
Context lost:
Listen: canvas.addEventListener('webglcontextlost', handler)
Save state, stop all loops
Attempt restore: canvas.addEventListener('webglcontextrestored', handler)
Rebuild scene from state snapshot, not from scratch
Shader not compiling:
renderer.debug.checkShaderErrors = true
Check uniform locations: gl.getUniformLocation(program, name) — null means not found
Check attribute locations: same pattern
Verify GLSL version matches renderer (GLSL 300 es for WebGL2)
Tooling Recommendations
Category
Tool
Purpose
Build
Vite
Fast bundling, HMR, TypeScript
Testing
Vitest
Unit/integration tests, fast
Linting
ESLint + TypeScript
Catch errors pre-runtime
Formatting
Prettier
Consistent code style
3D Debug
Three.js Studio / React Three Fiber
Visual scene inspection
Profiling
Chrome DevTools Performance + Memory
FPS, frame time, memory
Network
Chrome DevTools Network
Asset loading, waterfalls
Type Checking
tsc --noEmit
Static analysis
Bundle Analysis
vite build --mode analyze
Bundle size, tree-shaking
Code Templates
Minimal Three.js Scene (Foundation)
import * as THREE from "three";
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: false });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
document.body.appendChild(renderer.domElement);
const clock = new THREE.Clock();
const controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;
const animate = () => {
const delta = clock.getDelta();
const time = clock.getElapsedTime();
controls.update();
renderer.render(scene, camera);
requestAnimationFrame(animate);
};
animate();
// Cleanup
window.addEventListener('beforeunload', () => {
renderer.dispose();
controls.dispose();
});
Loading State Machine (Implementation)
const STATES = ['BOOT', 'INITIALIZING', 'LOADING_ASSETS', 'INITIALIZING_RENDERER', 'READY', 'ENTERING', 'ACTIVE', 'RECOVERABLE_ERROR', 'FALLBACK'];
const ALLOWED = {
BOOT: ['INITIALIZING'],
INITIALIZING: ['LOADING_ASSETS', 'RECOVERABLE_ERROR'],
LOADING_ASSETS: ['INITIALIZING_RENDERER', 'RECOVERABLE_ERROR'],
INITIALIZING_RENDERER: ['READY', 'RECOVERABLE_ERROR'],
READY: ['ENTERING'],
ENTERING: ['ACTIVE', 'FALLBACK'],
ACTIVE: ['RECOVERABLE_ERROR'],
RECOVERABLE_ERROR: ['FALLBACK'],
FALLBACK: ['ACTIVE'],
};
class StateMachine {
constructor() { this.state = 'BOOT'; }
async transition(to) {
if (!ALLOWED[this.state]?.includes(to)) throw new Error(`Invalid: ${this.state} → ${to}`);
this.state = to;
this.onEnter(to);
}
}
Quality Adaptation Pattern
function getConfig() {
const isMobile = /Mobi|Android/i.test(navigator.userAgent);
const cores = navigator.hardwareConcurrency || 2;
const quality = isMobile || cores < 4 ? 'LOW' : cores < 8 ? 'MEDIUM' : 'HIGH';
return QUALITY_PRESETS[quality];
}
Compact Checklist
Version History
v3.0 — Added: Quality tiers with budgets, mobile distinct state, accessibility requirements, anti-patterns catalog, debugging strategies, tooling recommendations, code templates, performance budgets table, material hierarchy, error recovery paths, version history
v2.0 — Added: Liquid glass/metal material system, deterministic motion parameters, loading state machine implementation
v1.0 — Original: Observe → Classify → Detect → Architecture → Visual → Material → Motion → Performance → Loading → Verify → Accept
1 --- 2 name: creative-web-algorithm 3 description: Build or refactor premium creative-web experiences from an existing codebase using an observe-first execution algorithm. Use for HTML/CSS/JS/WebGL landing pages, SaaS interfaces, parallax, image-to-motion, liquid glass/metal, chromatic effects, canvas scenes, boot state machines, and performance-sensitive interactive frontends. Triggers: "make this page 3D", "add WebGL", "creative landing page", "liquid glass UI", "Three.js scene", "interactive canvas", "parallax website", "SaaS hero section", "webGL portfolio", "shader effects", "particle system web", "chromatic aberration", "motion design web", "boot state machine", "creative web algorithm". 4 --- 5 6 # Creative Web Algorithm v3.0 7 8 Transform **STATE A → STATE B** while preserving valid behavior, proving each significant change, and delivering a production-grade experience. Never begin by editing. 9 10 ## Operating Contract (Inviolable) 11 12 1. **Observe before modifying.** Map the entire runtime before touching a single line. 13 2. **Never patch unknown runtimes, exported snapshots, iframes, or minified bundles** until execution model is fully mapped. 14 3. **Component decisions only:** Keep, Refactor, Replace, Remove, or mark Unknown. Never remove without evidence. 15 4. **Strict separation:** `RENDERING ≠ UI ≠ ANIMATION ≠ STATE ≠ ASSETS ≠ PERFORMANCE_POLICY`. Each layer has its own module, its own tests, its own lifecycle. 16 5. **Never claim a feature exists until it has been run and tested** in a real browser. Console + Network + Performance tabs required. 17 6. **Prefer reversible incremental migrations over rewrites.** Every change must be rollbackable in ≤1 commit. 18 7. **If the format prevents reliable development, extract/reconstruct the runtime first.** Stop, diagnose, rebuild the foundation before continuing. 19 8. **One owned render loop. One state machine. One source of truth for scene state.** No orphaned RAF handles, no duplicate loops. 20 9. **Memory is a first-class concern.** Every GPU resource, every listener, every timer must have an explicit disposal path. 21 10. **Accessibility is non-negotiable.** Every visual effect must have a reduced-motion fallback and keyboard/screen-reader equivalent. 22 23 --- 24 25 ## Phase 0 — OBSERVE (Zero Modifications) 26 27 **Goal:** Produce a complete Architecture Map before writing any code. 28 29 ### Scan Checklist 30 31 | Category | What to inspect | Tool | 32 |----------|----------------|------| 33 | **Entry points** | HTML, JS modules, CSS entry, config files | `grep`, `find` | 34 | **Dependencies** | npm packages, CDN imports, dynamic imports | `package.json`, `import` statements | 35 | **DOM structure** | Element tree, canvas elements, iframes | DevTools, `document.querySelectorAll` | 36 | **CSS architecture** | Custom properties, animations, layout system | `grep` for `@keyframes`, `:root` | 37 | **JS modules** | Export/import graph, state management, store patterns | AST analysis | 38 | **Canvas/WebGL** | WebGL context, shader programs, render targets, framebuffers | `WEBGL_debug_renderer_info` | 39 | **Shaders** | Vertex/fragment source, uniform locations, texture bindings | File scan | 40 | **Assets** | Images, fonts, audio, video, models, with sizes and formats | File scan, `ls -lh` | 41 | **Loading/boot** | Sequence order, preloaders, skeleton screens, lazy loading | Code trace | 42 | **Iframes** | Embedded content, cross-origin, postMessage channels | DOM scan | 43 | **CSP** | Content-Security-Policy headers, nonce/hashes, restrictions | Network tab | 44 | **Timers/promises** | setInterval, setTimeout, Promise chains, microtasks | Code scan | 45 | **External resources** | APIs, CDNs, fonts, analytics, tracking | Network tab | 46 | **Event listeners** | Mouse, touch, scroll, keyboard, resize, custom events | Code scan, DevTools | 47 | **Performance** | FPS, frame time, memory, layout thrashing | Performance tab | 48 | **Network** | Requests, waterfalls, caching, compression | Network tab | 49 50 ### Output: Architecture Map 51 52 ```markdown 53 # Architecture Map — [Project Name] 54 55 ## Entry Points 56 - [ ] Primary: [file] → [initialization sequence] 57 - [ ] Secondary: [file] → [purpose] 58 59 ## Module Graph 60 - [module A] imports [B, C] 61 - [module B] exports [X, Y] 62 - [module C] depends on [D, E] 63 64 ## State Management 65 - [ ] Single source of truth: [store/context/prop] 66 - [ ] Derived state: [computed values] 67 - [ ] Side effects: [saga/epic/effect] 68 69 ## Render Architecture 70 - [ ] Primary renderer: [Three.js/Canvas2D/WebGL] 71 - [ ] Secondary renderers: [overlays, post-processing] 72 - [ ] Render loop ownership: [which module] 73 74 ## Asset Pipeline 75 - [ ] Loading strategy: [lazy/eager/prefetch] 76 - [ ] Cache policy: [memory/disk/None] 77 - [ ] Fallback assets: [low-res/progressive] 78 79 ## Performance Profile 80 - [ ] Current FPS: [target/described] 81 - [ ] Memory budget: [bytes] 82 - [ ] Frame budget: [ms] 83 - [ ] Bundle size: [KB] 84 85 ## Known Issues 86 - [ ] [Issue description, evidence, impact] 87 ``` 88 89 ### Dependency Graph (ASCII) 90 ``` 91 [Entry] → [Renderer] → [Scene] → [Camera] 92 ↓ ↓ 93 [PostFX] [Lights] 94 ↓ ↓ 95 [UI Layer] ← [State Store] 96 ↓ 97 [Animation] → [Assets] 98 ``` 99 100 --- 101 102 ## Phase 1 — CLASSIFY 103 104 **Goal:** Create a Component Decision Map with evidence for every component. 105 106 ### Decision Categories 107 108 | Category | Criteria | Action | 109 |----------|----------|--------| 110 | **KEEP** | Functional, valuable, no structural issues | Leave untouched | 111 | **REFACTOR** | Valuable but structurally unsafe, hard to extend, or technically debt | Rewrite internals, preserve API | 112 | **REPLACE** | Unsuitable for target state with concrete evidence | Swap implementation, preserve interface | 113 | **REMOVE** | Redundant, broken, harmful with evidence | Delete + add migration note | 114 | **UNKNOWN** | Insufficient evidence to classify | Investigate → reclassify → act | 115 116 ### Decision Record Format 117 ```markdown 118 ## Component Decision Map 119 120 ### [Component Name] 121 - **Location:** [file:line] 122 - **Current state:** [working/broken/debt] 123 - **Evidence:** [test results, performance metrics, bug reports] 124 - **Decision:** KEEP / REFACTOR / REPLACE / REMOVE / UNKNOWN 125 - **Rationale:** [why this decision] 126 - **Risk if wrong:** [what breaks] 127 - **Reversibility:** [how to undo] 128 129 ### [Next Component] 130 ... 131 ``` 132 133 ### Evidence Requirements 134 - **Performance data:** FPS benchmarks, memory snapshots, frame timing 135 - **Functional tests:** Unit tests passing/failing, integration test results 136 - **User feedback:** Bug reports, support tickets, analytics data 137 - **Code quality:** Cyclomatic complexity, bundle impact, coupling metrics 138 139 --- 140 141 ## Phase 2 — DETECT FAILURE MODES 142 143 **Goal:** For every issue, record `CAUSE → EFFECT → FIX`. Fix causes, not symptoms. 144 145 ### Failure Mode Catalog 146 147 | Failure Mode | CAUSE | EFFECT | FIX | 148 |-------------|-------|--------|-----| 149 | **Infinite/duplicated loops** | RAF not cancelled, recursive setTimeout, missing break condition | CPU spike, battery drain, tab crash | Cancel RAF on unmount, use `useEffect` cleanup, add `requestID` tracking | 150 | **Duplicate listeners** | Event added in render without cleanup, closure scoping bug | Memory leak, double-fire, exponential growth | Use `AbortController`, `removeEventListener`, single subscription pattern | 151 | **Blocking initialization** | Synchronous asset load, blocking script, large bundle parse | White screen >3s, TTI degradation | Code splitting, lazy import, skeleton screen, streaming | 152 | **Failed promises** | Missing `.catch()`, unhandled rejection, race condition | Silent failure, zombie state | Always `.catch()`, use `Promise.allSettled`, add error boundaries | 153 | **Missing assets** | Broken URL, wrong path, CORS restriction | Fallback broken, layout shift | Verify paths, use `onError` handlers, preload critical assets | 154 | **Broken imports** | Circular dependency, missing module, wrong export | Runtime error, blank page | Fix import paths, use `eslint import/no-cycle`, tree-shake | 155 | **iframe/CSP conflicts** | CSP blocks inline scripts, iframe sandbox restrictions | Content blocked, security errors | Adjust CSP headers, use `sandbox` attribute carefully | 156 | **Memory leaks** | Orphaned listeners, unclosed connections, retained references | Gradual slowdown, OOM crash | `WeakRef` for caches, explicit dispose, DevTools memory profiling | 157 | **Excessive DPR** | `devicePixelRatio > 2` on high-DPI screens | GPU memory spike, frame drops | Cap DPR at `Math.min(window.devicePixelRatio, 2)` | 158 | **Reflow/repaint cost** | Layout thrashing, forced synchronous layouts | Jank, >16ms frame time | Batch DOM reads/writes, use `transform`/`opacity` only, `will-change` | 159 | **Runaway particles** | Unbounded particle count, no max cap, no culling | GPU overload, FPS collapse to <10 | Cap count, use LOD, frustum culling, GPU instancing | 160 | **Orphaned GPU resources** | Disposed but not freed textures, unreleased framebuffers | VRAM leak, context lost | Track all GPU handles, explicit `dispose()` on unmount | 161 | **Shader compilation stalls** | Many shaders compiled in one frame, large programs | Frame drop, jank | Precompile during loading, use `THREE.ShaderMaterial` warmup | 162 | **Network waterfall** | Sequential dependencies, unoptimized loading | Long TTI, slow perceived performance | Parallelize, prefetch, use HTTP/2+ push, CDN | 163 164 ### Failure Mode Report 165 ```markdown 166 ## Failure Mode Analysis 167 168 ### F-001: [Title] 169 - **Severity:** Critical / High / Medium / Low 170 - **CAUSE:** [root cause] 171 - **EFFECT:** [observable impact] 172 - **FIX:** [specific solution] 173 - **Evidence:** [data proving this] 174 - **Prevention:** [how to avoid in future] 175 ``` 176 177 --- 178 179 ## Phase 3 — DEFINE TARGET ARCHITECTURE 180 181 **Goal:** Create a maintainable, scalable structure with strict layer separation. 182 183 ### Canonical Directory Structure 184 ```text 185 project/ 186 ├── src/ 187 │ ├── scene/ # Scene graph, objects, entities 188 │ │ ├── index.ts 189 │ │ ├── Camera.ts # Camera management, transitions 190 │ │ ├── Lights.ts # Lighting system 191 │ │ ├── Objects/ # Reusable 3D objects 192 │ │ │ ├── Indexed.ts # Pre-registered geometries 193 │ │ │ └── Materials/ # Material definitions 194 │ │ └── Environment/ # Sky, fog, post-processing 195 │ ├── renderer/ # Renderer configuration, context 196 │ │ ├── index.ts 197 │ │ ├── WebGLContext.ts # Context creation, loss handling 198 │ │ └── PostFX/ # Bloom, SSAO, chromatic, etc. 199 │ ├── shaders/ # GLSL source code 200 │ │ ├── common.glsl 201 │ │ ├── vertex/ 202 │ │ └── fragment/ 203 │ ├── effects/ # Visual effects pipeline 204 │ │ ├── LiquidGlass.ts 205 │ │ ├── Chromatic.ts 206 │ │ ├── Haze.ts 207 │ │ └── Particles.ts 208 │ ├── animation/ # Animation system 209 │ │ ├── Animator.ts # Main animation controller 210 │ │ ├── Tweens.ts # Easing functions 211 │ │ └── Timeline.ts # Sequenced animations 212 │ ├── ui/ # HTML/CSS overlay layer 213 │ │ ├── components/ 214 │ │ ├── styles/ 215 │ │ └── state/ # UI state (separate from scene state) 216 │ ├── assets/ # Static assets, textures, models 217 │ │ ├── textures/ 218 │ │ ├── models/ 219 │ │ └── audio/ 220 │ ├── state/ # Single source of truth for scene state 221 │ │ ├── Store.ts # Centralized state management 222 │ │ └── selectors.ts # Derived state 223 │ ├── utils/ # Shared utilities 224 │ │ ├── math.ts 225 │ │ ├── geometry.ts 226 │ │ └── helpers.ts 227 │ └── main.ts # Entry point, boot sequence 228 ├── tests/ 229 │ ├── unit/ 230 │ ├── integration/ 231 │ └── visual/ 232 ├── public/ 233 │ └── index.html 234 ├── package.json 235 ├── tsconfig.json 236 ├── vite.config.ts 237 └── README.md 238 ``` 239 240 ### Architecture Rules 241 242 1. **`RENDERING ≠ UI ≠ ANIMATION ≠ STATE ≠ ASSETS ≠ PERFORMANCE`** — Each module owns its domain, never crosses boundaries without explicit interfaces. 243 2. **Single source of truth** — Scene state lives in `state/Store.ts`. UI state lives in `ui/state/`. Never duplicate. 244 3. **Renderer is agnostic** — `renderer/` knows nothing about scene content. Scene provides geometries; renderer draws them. 245 4. **Animation is parameterized** — All motion derives from `time`, `delta`, `velocity`, `damping`, `easing`. Never hardcode frame counts. 246 5. **Assets are lazy** — Load on demand. Preload only critical path (<100KB). 247 6. **Effects are composable** — PostFX pipeline is a chain. Add/remove without affecting core render. 248 7. **Cleanup is mandatory** — Every module exports `dispose()`. Call it on unmount/context loss. 249 250 --- 251 252 ## Phase 4 — BUILD THE VISUAL SYSTEM 253 254 **Goal:** Create a layered scene graph with explicit depth, parallax, and motion policy. 255 256 ### Scene Graph Layers (Front to Back) 257 ``` 258 ┌─────────────────────────────────┐ 259 │ UI LAYER │ ← HTML/CSS overlay, interactive controls 260 │ ───────────────────────────── │ 261 │ INTERACTION LAYER │ ← Raycasting, pointer events, hover 262 │ ───────────────────────────── │ 263 │ PARTICLES LAYER │ ← Atmospheric particles, ambient 264 │ ───────────────────────────── │ 265 │ SUBJECT LAYER │ ← Main 3D object, hero geometry 266 │ ───────────────────────────── │ 267 │ LIGHTING LAYER │ → Reflections, shadows, glow 268 │ ───────────────────────────── │ 269 │ ENVIRONMENT LAYER │ → Sky, fog, ground, skybox 270 │ ───────────────────────────── │ 271 │ DEPTH LAYERS │ → Parallax planes, background depth 272 │ ───────────────────────────── │ 273 │ ATMOSPHERE LAYER │ → Haze, fog, volumetric light 274 │ ───────────────────────────── │ 275 │ BACKGROUND LAYER │ → Solid color, gradient, gradient 276 └─────────────────────────────────┘ 277 ``` 278 279 ### Layer Configuration 280 Each layer has explicit properties: 281 ```typescript 282 interface LayerConfig { 283 name: string; 284 depth: number; // 0.0 (back) to 1.0 (front) 285 parallaxCoefficient: number; // 0.0 (static) to 1.0 (follows pointer) 286 motionPolicy: 'static' | 'parallax' | 'animated' | 'interactive'; 287 opacity: number; // 0.0 to 1.0 288 visible: boolean; 289 quality: 'high' | 'medium' | 'low'; 290 } 291 ``` 292 293 ### Parameterized Inputs 294 Route these same inputs to ALL visual layers: 295 - `time` — `clock.getElapsedTime()` — drives all animations 296 - `pointer` — `mouse.x, mouse.y` — drives parallax, hover, interaction 297 - `viewport` — `window.innerWidth/innerHeight` — drives resolution, scale 298 - `scroll` — `window.scrollY` — drives depth, reveals, transitions 299 - `delta` — `clock.getDelta()` — drives frame-rate-independent motion 300 301 **Anti-pattern:** Applying arbitrary transforms to every element and calling it "3D". Every transform must serve a visual purpose documented in the layer config. 302 303 --- 304 305 ## Phase 5 — BUILD THE MATERIAL SYSTEM SELECTIVELY 306 307 **Goal:** Choose materials hierarchically. Every effect must earn its place. 308 309 ### Material Hierarchy 310 311 **Primary (One Dominant Effect):** 312 Choose ONE primary visual identity: 313 - **Liquid Glass:** `MeshPhysicalMaterial` with `transmission: 0.85`, `thickness: 0.5`, `ior: 1.5`, `clearcoat: 1.0`, `roughness: 0.05`. Creates refractive, reflective surfaces. 314 - **Liquid Metal:** `MeshPhysicalMaterial` with `metalness: 1.0`, `roughness: 0.0`, `color: [theme]`, `envMapIntensity: 2.0`. Creates mirror-like metallic surfaces. 315 - **Ethereal:** `MeshStandardMaterial` with `emissive`, `transparent`, `opacity: 0.3`, `wireframe: true`. Creates ghostly, sci-fi surfaces. 316 - **Neon Glow:** `MeshBasicMaterial` with `emissive`, `emissiveIntensity: 2.0`, `transparent: true`, `opacity: 0.8`. Creates glowing, holographic surfaces. 317 318 **Secondary (Supporting Effects):** 319 - **Reflection/Refraction:** Environment maps, `cubeCamera`, `refractionRatio` 320 - **Fresnel:** `FresnelMaterial` or custom shader with `dot(normal, viewDir)` 321 - **Displacement:** `displacementMap`, `displacementScale`, vertex displacement 322 - **Chromatic:** Custom shader with RGB channel offset based on view angle 323 324 **Atmospheric (Global Effects):** 325 - **Bloom:** `UnrealBloomPass` — strength 0.5-1.5, radius 0.4, threshold 0.85 326 - **Light Scatter:** `LensFlare`, `sprite` glow, `AdditiveBlending` 327 - **Haze:** `FogExp2` or custom depth-based fog 328 - **RGB Shift:** Custom post-process shader with chromatic aberration offset 329 330 ### Material Decision Rules 331 1. **Never use a generated still image to cover or replace an existing animated canvas** unless explicitly requested by the user. 332 2. **Remove effects that do not improve composition.** If a bloom pass doesn't add visual value, remove it. If a chromatic effect distracts from the subject, kill it. 333 3. **One primary effect.** Supporting effects only earn their place if they enhance the primary without competing. 334 4. **Performance budget:** Primary material <2 shader compiles. Total post-processing <3 passes. 335 336 ### Liquid Glass Implementation Template 337 ```glsl 338 // Fragment shader for liquid glass 339 uniform float uTime; 340 uniform float uTransmission; 341 uniform vec3 uColor; 342 uniform float uIOR; 343 344 void main() { 345 vec3 viewDir = normalize(vViewPosition); 346 vec3 normal = normalize(vNormal); 347 348 // Fresnel for edge glow 349 float fresnel = pow(1.0 - dot(viewDir, normal), 3.0); 350 351 // Transmission for refraction 352 vec3 refracted = texture2D(uTransmissionMap, vUv).rgb; 353 vec3 transmitted = mix(uColor, refracted, uTransmission); 354 355 // Edge glow 356 vec3 edgeGlow = vec3(0.0, 0.9, 0.97) * fresnel * 0.5; 357 358 gl_FragColor = vec4(transmitted + edgeGlow, 0.85 + fresnel * 0.15); 359 } 360 ``` 361 362 --- 363 364 ## Phase 6 — BUILD DETERMINISTIC MOTION 365 366 **Goal:** All motion derives from parameterized inputs. Never hardcoded values. 367 368 ### Motion Parameter System 369 ```typescript 370 interface MotionParams { 371 velocity: number; // Base speed (units/sec) 372 acceleration: number; // Rate of change of velocity 373 damping: number; // Friction/decay (0-1) 374 easing: 'linear' | 'easeIn' | 'easeOut' | 'easeInOut' | 'elastic' | 'bounce'; 375 depth: number; // Z-depth parallax coefficient 376 amplitude: number; // Max displacement 377 frequency: number; // Oscillations per second 378 phase: number; // Time offset 379 } 380 ``` 381 382 ### Routing Protocol 383 All inputs MUST route to ALL relevant transforms: 384 ``` 385 [time] → camera.transform, layer.transform, shader.uniforms, lighting.position, particle.velocity, UI.transition 386 [pointer] → camera.position (parallax), layer.parallax, object.rotation (follow) 387 [scroll] → camera.position (depth), layer.opacity (reveal), object.scale (approach) 388 [viewport] → renderer.dpr, camera.aspect, object.scale (responsive) 389 ``` 390 391 ### Animation Loop Contract 392 ```typescript 393 // ONE owned loop per renderer 394 class AnimationLoop { 395 private rafId: number | null = null; 396 private lastTime = 0; 397 398 start() { 399 const tick = (timestamp: number) => { 400 const delta = timestamp - this.lastTime; 401 this.lastTime = timestamp; 402 this.update(delta, timestamp); 403 this.rafId = requestAnimationFrame(tick); 404 }; 405 this.rafId = requestAnimationFrame(tick); 406 } 407 408 stop() { 409 if (this.rafId !== null) { 410 cancelAnimationFrame(this.rafId); 411 this.rafId = null; 412 } 413 } 414 415 private update(delta: number, timestamp: number) { 416 // Route delta to ALL systems: 417 this.scene.update(delta); 418 this.ui.update(delta); 419 this.particles.update(delta); 420 this.camera.update(delta); 421 } 422 423 dispose() { 424 this.stop(); 425 // Clean up all listeners, observers, GPU resources 426 } 427 } 428 ``` 429 430 **Rules:** 431 - Use `requestAnimationFrame` only through this ONE owned loop. 432 - Never create additional `setTimeout`, `setInterval`, or secondary RAF loops. 433 - Always provide `dispose()` that cleans up listeners, RAF handles, observers, and GPU resources. 434 - Use `clock.getDelta()` for frame-rate-independent motion. Never assume 60fps. 435 436 --- 437 438 ## Phase 7 — ENFORCE PERFORMANCE 439 440 **Goal:** Adaptive quality with explicit budgets and mobile-specific states. 441 442 ### Quality Tiers 443 ```typescript 444 enum Quality { HIGH = 'high', MEDIUM = 'medium', LOW = 'low' } 445 446 interface QualityConfig { 447 dpr: number; // 2, 1.5, 1 448 resolution: number; // 1, 0.75, 0.5 449 particleCount: number; // 10000, 5000, 1000 450 shadowMap: { size: number, enabled: boolean }; // 2048/1024/512 451 postProcessing: { bloom: boolean, ssao: boolean, chromatic: boolean }; 452 animationFrequency: number; // 60, 30, 15 fps cap 453 textureSize: number; // 1024, 512, 256 454 geometryDetail: number; // high/medium/low segment counts 455 } 456 ``` 457 458 ### Quality Selection Logic 459 ```typescript 460 function selectQuality(): Quality { 461 const cores = navigator.hardwareConcurrency || 2; 462 const memory = (navigator as any).deviceMemory || 4; 463 const dpr = Math.min(window.devicePixelRatio, 2); 464 const isMobile = /Mobi|Android/i.test(navigator.userAgent); 465 const fps = getBaselineFPS(); // Measure initial FPS 466 467 if (isMobile || cores < 4 || memory < 4 || fps < 30) return Quality.LOW; 468 if (cores < 8 || memory < 8 || dpr > 1.5 || fps < 45) return Quality.MEDIUM; 469 return Quality.HIGH; 470 } 471 ``` 472 473 ### Degradation Cascade (Priority Order) 474 When performance degrades, reduce in this order: 475 1. **Atmospheric effects first** — bloom, haze, fog 476 2. **Particles second** — reduce count, simplify shader 477 3. **Shader resolution third** — lower texture sizes, fewer segments 478 4. **Preserve the primary composition** — the hero object must remain visible and coherent 479 480 ### Performance Budgets 481 | Metric | HIGH | MEDIUM | LOW | Budget | 482 |--------|------|--------|-----|--------| 483 | Target FPS | 60 | 45 | 30 | ≥30 | 484 | Frame Time | ≤16ms | ≤22ms | ≤33ms | <33ms | 485 | Draw Calls | <100 | <50 | <25 | <50 | 486 | Triangles | <500k | <200k | <50k | <200k | 487 | Textures | 10MB | 5MB | 2MB | <10MB | 488 | Bundle | 500KB | 300KB | 150KB | <500KB | 489 | GPU Memory | <500MB | <250MB | <100MB | <500MB | 490 491 ### Mobile State 492 Mobile is a **distinct lower-cost rendering state**, not merely a scaled desktop: 493 - Different geometry LODs (not just scaled down) 494 - Different particle systems (points vs. sprites) 495 - Different post-processing pipeline (no bloom on mobile) 496 - Touch-optimized interaction (no hover-dependent UI) 497 - Reduced shadow quality (no real-time shadows on LOW) 498 499 --- 500 501 ## Phase 8 — REAL LOADING STATE MACHINE 502 503 **Goal:** Explicit, deterministic loading with no arbitrary timeouts. 504 505 ### State Machine 506 ``` 507 ┌─────────────┐ 508 │ BOOT │ ← Entry point, initialize globals 509 └──────┬──────┘ 510 ↓ 511 ┌──────────────┐ 512 │INITIALIZING │ ← Setup modules, validate dependencies 513 └──────┬───────┘ 514 ↓ 515 ┌─────────────────┐ 516 │LOADING_ASSETS │ ← Load textures, models, audio, fonts 517 │(with progress) │ ← Report progress to UI 518 └──────┬──────────┘ 519 ↓ 520 ┌───────────────────────┐ 521 │INITIALIZING_RENDERER │ ← Create WebGL context, compile shaders 522 └──────┬────────────────┘ 523 ↓ 524 ┌────────────┐ 525 │ READY │ ← All assets loaded, renderer initialized 526 └──────┬─────┘ 527 ↓ 528 ┌────────────┐ 529 │ ENTERING │ ← First render, transition in 530 └──────┬─────┘ 531 ↓ 532 ┌────────────┐ 533 │ ACTIVE │ ← Full interactive experience 534 └────┬───────┘ 535 ↓ (from any state) 536 ┌─────────────────┐ 537 │RECOVERABLE_ERROR│ ← Asset failed, context lost, etc. 538 └──────┬──────────┘ 539 ↓ 540 ┌────────────┐ 541 │ FALLBACK │ ← Graceful degradation, cached content, error UI 542 └──────┬─────┘ 543 ↓ (user action / retry) 544 ┌────────────┐ 545 │ ACTIVE │ ← Retry successful 546 └────────────┘ 547 548 SPECIAL: ENTERING → ACTIVE (skip, if no transition needed) 549 ``` 550 551 ### State Implementation 552 ```typescript 553 class LoadingStateMachine { 554 state: State = 'BOOT'; 555 progress: number = 0; 556 errors: Error[] = []; 557 558 async transition(to: State) { 559 const valid = this.allowedTransitions[this.state]; 560 if (!valid.includes(to)) throw new Error(`Invalid transition: ${this.state} → ${to}`); 561 this.state = to; 562 this.onStateChange(to); 563 } 564 565 async loadAssets(urls: string[], onProgress: (p: number) => void) { 566 await this.transition('LOADING_ASSETS'); 567 const results = await Promise.allSettled( 568 urls.map(url => fetch(url).then(r => r.blob())) 569 ); 570 // Handle partial failures gracefully 571 const successes = results.filter(r => r.status === 'fulfilled'); 572 const failures = results.filter(r => r.status === 'rejected'); 573 if (failures.length > 0 && successes.length === 0) { 574 await this.transition('RECOVERABLE_ERROR'); 575 } 576 this.progress = successes.length / urls.length; 577 } 578 579 // skip is ALWAYS safe and idempotent 580 skip() { 581 this.transition('ACTIVE'); // or FALLBACK if assets not loaded 582 } 583 } 584 ``` 585 586 ### Rules 587 - **Advance on actual asset/renderer readiness**, never an arbitrary timeout 588 - **Display actionable errors** — "Failed to load texture X. Retry?" not "Error 404" 589 - **Skip is always safe and idempotent** — calling skip twice does nothing bad 590 - **Enter → Active can skip** if assets are cached (service worker) 591 - **Any state → Recoverable Error → Fallback → Active** for error recovery 592 593 --- 594 595 ## Phase 9 — VERIFY AFTER EVERY SIGNIFICANT CHANGE 596 597 **Goal:** Run the full verification matrix. Never stack unverified patches. 598 599 ### Verification Matrix 600 ``` 601 BUILD ────────────────────────────── 602 → tsc --noEmit (or equivalent) 603 → Rollup/Vite build succeeds 604 → No TypeScript errors 605 → No bundler warnings 606 607 RUN ──────────────────────────────── 608 → Opens in browser without errors 609 → No console errors (0 critical) 610 → No console warnings (0 new) 611 → Network tab: all assets load (200 OK) 612 → FPS ≥ target (≥30 minimum) 613 614 INSPECT CONSOLE ──────────────────── 615 → Zero errors 616 → Zero warnings from our code 617 → No deprecated API usage 618 → No CORS violations 619 → No CSP violations 620 621 TEST INTERACTION ─────────────────── 622 → Click/hover/touch all interactive elements 623 → All event handlers fire correctly 624 → No duplicate listener warnings 625 → Scroll works without jank 626 627 TEST BOOT ────────────────────────── 628 → Fresh load: BOOT → INITIALIZING → LOADING → RENDERER → READY → ENTERING → ACTIVE 629 → Progress bar updates correctly 630 → Skip button works from any state 631 → Error state triggers correctly on failed asset 632 633 TEST SKIP ────────────────────────── 634 → Skip from BOOT: works, shows fallback 635 → Skip from LOADING_ASSETS: works, shows cached content 636 → Skip from INITIALIZING_RENDERER: works, shows static content 637 → Skip is idempotent: calling twice = calling once 638 639 TEST RESIZE ──────────────────────── 640 → Window resize: camera aspect updates 641 → DPR change: renderer resize triggered 642 → Mobile rotate: layout adapts 643 → No layout shift (CLS < 0.1) 644 645 TEST MOBILE ──────────────────────── 646 → Touch events work 647 → Quality = LOW selected automatically 648 → Reduced motion respects prefers-reduced-motion 649 → No hover-dependent UI visible 650 → Touch target sizes ≥ 44px 651 652 TEST PERFORMANCE ─────────────────── 653 → Performance tab: no long tasks (>50ms) 654 → Memory: no leak over 5 min session 655 → GPU: no context lost events 656 → Frame time: consistent (no spikes) 657 658 VISUAL QA ────────────────────────── 659 → All layers render in correct order 660 → Colors match design specification 661 → Typography is legible at all sizes 662 → Animations are smooth (60fps target) 663 → No visual artifacts, flickering, or tearing 664 ``` 665 666 ### On Failure Protocol 667 1. **Roll back the last change** (`git checkout` or revert) 668 2. **Identify the exact cause** (not the symptom — trace back) 669 3. **Modify ONE thing** (never multiple changes at once) 670 4. **Test again** (full verification matrix) 671 5. **Only then proceed** to next change 672 673 **Anti-pattern:** Stacking unverified patches. If something breaks, revert and debug one change at a time. 674 675 --- 676 677 ## Phase 10 — FINAL ACCEPTANCE 678 679 **Goal:** All success criteria met. "The page loads" is not success. 680 681 ### Acceptance Checklist 682 - [ ] **Functional:** All features work as specified. No bugs. All states handled. 683 - [ ] **Visually coherent:** Design is consistent, colors match, typography is unified. 684 - [ ] **Performant:** All performance budgets met. FPS ≥ target. No jank. 685 - [ ] **Responsive:** Works on desktop, tablet, mobile. Layout adapts correctly. 686 - [ ] **Accessible:** Keyboard navigable, screen-reader friendly, `prefers-reduced-motion` respected, ARIA labels present. 687 - [ ] **Maintainable:** Clean architecture, documented code, modular structure, tests pass. 688 - [ ] **Demonstrated through verification loop:** All phases 9 tests pass. 689 690 ### Final Deliverable Package 691 ``` 692 1. Source code (all files, clean, commented) 693 2. Architecture notes (Architecture Map, Decision Map) 694 3. Performance report (FPS, memory, frame time metrics) 695 4. Test results (verification matrix status) 696 5. Documentation (README, setup instructions, API docs) 697 6. Assets manifest (all files, sizes, formats) 698 7. Deployment guide (build commands, hosting config) 699 ``` 700 701 --- 702 703 ## Required First Response for Existing Projects 704 705 **Before writing ANY code, return ONLY:** 706 707 1. **Architecture Map** (Phase 0 output) 708 2. **Component Decision Map** (Phase 1 output) 709 3. **Failure Modes** (`CAUSE → EFFECT → FIX` from Phase 2) 710 4. **Target Architecture** (Phase 3 output — directory structure) 711 5. **Implementation Sequence** (ordered list of tasks, phase by phase) 712 6. **Risks** (what could go wrong, mitigation strategies) 713 714 **Then wait for explicit authorization unless the user has already authorized implementation.** 715 716 --- 717 718 ## Anti-Patterns Catalog (Never Do These) 719 720 | Anti-Pattern | Why It's Bad | Correct Approach | 721 |-------------|-------------|-----------------| 722 | Using `generated still image` to replace working canvas | Static content can't respond to interaction | Keep canvas animated, overlay image only as fallback | 723 | `requestAnimationFrame` in multiple modules | Frame conflicts, race conditions, double renders | ONE owned loop, all modules subscribe to it | 724 | Hardcoded animation frames (`for (let i=0; i<60; i++)`) | Tied to 60fps, breaks on slow devices | Use `clock.getDelta()` with parameterized time | 725 | Applying transforms to every element calling it "3D" | Visual noise, no depth, no purpose | Only transform elements that serve a visual layer purpose | 726 | Removing unknown components without evidence | Destroys potentially valuable code | Mark UNKNOWN, investigate, then decide | 727 | `setTimeout(() => { render() }, 1000)` for loading | Arbitrary, not tied to actual readiness | Use state machine with real asset progress | 728 | Memory leaks from unclosed GPU resources | Progressive slowdown, eventual crash | Explicit `dispose()` on every GPU handle | 729 | Ignoring `prefers-reduced-motion` | Accessibility violation, motion sickness | Always provide reduced-motion fallback | 730 | Scaling desktop to mobile (just CSS transform) | Poor touch targets, unreadable UI | Distinct mobile rendering state with LOD | 731 | Stacking unverified patches | Hard to debug, cascading failures | One change, verify, then next | 732 733 --- 734 735 ## Debugging Strategies 736 737 ### Common Issues and Solutions 738 739 **Canvas is black / white:** 740 1. Check WebGL context: `canvas.getContext('webgl2')` — if null, browser doesn't support WebGL2 741 2. Check shader compilation: `renderer.debug.checkShaderErrors = true` 742 3. Check scene graph: `scene.children.length > 0` — empty scene renders black 743 4. Check camera position: camera must be outside the near plane and facing objects 744 745 **FPS drops suddenly:** 746 1. Check particle count: `scene.children.filter(c => c.isPoints).length` 747 2. Check shadow map: `renderer.shadowMap.enabled` — disable if not needed 748 3. Check draw calls: `renderer.info.render.calls` — >100 is suspicious 749 4. Check geometry: `geometry.attributes.position.count` — high vertex counts kill FPS 750 751 **Memory growing:** 752 1. Take heap snapshot before and after interaction 753 2. Check `renderer.info.memory.geometries`, `textures`, `programs` 754 3. Look for detached DOM elements (DevTools Memory panel) 755 4. Check for orphaned event listeners (no cleanup on unmount) 756 757 **Context lost:** 758 1. Listen: `canvas.addEventListener('webglcontextlost', handler)` 759 2. Save state, stop all loops 760 3. Attempt restore: `canvas.addEventListener('webglcontextrestored', handler)` 761 4. Rebuild scene from state snapshot, not from scratch 762 763 **Shader not compiling:** 764 1. `renderer.debug.checkShaderErrors = true` 765 2. Check uniform locations: `gl.getUniformLocation(program, name)` — null means not found 766 3. Check attribute locations: same pattern 767 4. Verify GLSL version matches renderer (GLSL 300 es for WebGL2) 768 769 --- 770 771 ## Tooling Recommendations 772 773 | Category | Tool | Purpose | 774 |----------|------|---------| 775 | **Build** | Vite | Fast bundling, HMR, TypeScript | 776 | **Testing** | Vitest | Unit/integration tests, fast | 777 | **Linting** | ESLint + TypeScript | Catch errors pre-runtime | 778 | **Formatting** | Prettier | Consistent code style | 779 | **3D Debug** | Three.js Studio / React Three Fiber | Visual scene inspection | 780 | **Profiling** | Chrome DevTools Performance + Memory | FPS, frame time, memory | 781 | **Network** | Chrome DevTools Network | Asset loading, waterfalls | 782 | **Type Checking** | `tsc --noEmit` | Static analysis | 783 | **Bundle Analysis** | `vite build --mode analyze` | Bundle size, tree-shaking | 784 785 --- 786 787 ## Code Templates 788 789 ### Minimal Three.js Scene (Foundation) 790 ```javascript 791 import * as THREE from "three"; 792 793 const scene = new THREE.Scene(); 794 const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000); 795 const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: false }); 796 renderer.setSize(window.innerWidth, window.innerHeight); 797 renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2)); 798 document.body.appendChild(renderer.domElement); 799 800 const clock = new THREE.Clock(); 801 const controls = new OrbitControls(camera, renderer.domElement); 802 controls.enableDamping = true; 803 804 const animate = () => { 805 const delta = clock.getDelta(); 806 const time = clock.getElapsedTime(); 807 controls.update(); 808 renderer.render(scene, camera); 809 requestAnimationFrame(animate); 810 }; 811 animate(); 812 813 // Cleanup 814 window.addEventListener('beforeunload', () => { 815 renderer.dispose(); 816 controls.dispose(); 817 }); 818 ``` 819 820 ### Loading State Machine (Implementation) 821 ```javascript 822 const STATES = ['BOOT', 'INITIALIZING', 'LOADING_ASSETS', 'INITIALIZING_RENDERER', 'READY', 'ENTERING', 'ACTIVE', 'RECOVERABLE_ERROR', 'FALLBACK']; 823 const ALLOWED = { 824 BOOT: ['INITIALIZING'], 825 INITIALIZING: ['LOADING_ASSETS', 'RECOVERABLE_ERROR'], 826 LOADING_ASSETS: ['INITIALIZING_RENDERER', 'RECOVERABLE_ERROR'], 827 INITIALIZING_RENDERER: ['READY', 'RECOVERABLE_ERROR'], 828 READY: ['ENTERING'], 829 ENTERING: ['ACTIVE', 'FALLBACK'], 830 ACTIVE: ['RECOVERABLE_ERROR'], 831 RECOVERABLE_ERROR: ['FALLBACK'], 832 FALLBACK: ['ACTIVE'], 833 }; 834 835 class StateMachine { 836 constructor() { this.state = 'BOOT'; } 837 async transition(to) { 838 if (!ALLOWED[this.state]?.includes(to)) throw new Error(`Invalid: ${this.state} → ${to}`); 839 this.state = to; 840 this.onEnter(to); 841 } 842 } 843 ``` 844 845 ### Quality Adaptation Pattern 846 ```javascript 847 function getConfig() { 848 const isMobile = /Mobi|Android/i.test(navigator.userAgent); 849 const cores = navigator.hardwareConcurrency || 2; 850 const quality = isMobile || cores < 4 ? 'LOW' : cores < 8 ? 'MEDIUM' : 'HIGH'; 851 return QUALITY_PRESETS[quality]; 852 } 853 ``` 854 855 --- 856 857 ## Compact Checklist 858 859 - [ ] **Observe before modify** — Architecture Map complete 860 - [ ] **Extract runtime** if snapshot/iframe/CSP prevents reliable work 861 - [ ] **One renderer loop, one state machine, explicit cleanup** — No orphaned resources 862 - [ ] **No static hero replacing working motion** — Everything animated or has purpose 863 - [ ] **One primary visual effect; supporting effects earn their place** — Material hierarchy respected 864 - [ ] **Adaptive quality and mobile state** — Not just scaled desktop 865 - [ ] **Boot/skip/error/resize/mobile tested** — Full verification matrix 866 - [ ] **Console and performance checked** — Zero errors, FPS on target 867 - [ ] **Accessible** — reduced-motion, keyboard, ARIA 868 - [ ] **Deliver source plus architecture notes when useful** — Documentation complete 869 870 --- 871 872 ## Version History 873 874 - **v3.0** — Added: Quality tiers with budgets, mobile distinct state, accessibility requirements, anti-patterns catalog, debugging strategies, tooling recommendations, code templates, performance budgets table, material hierarchy, error recovery paths, version history 875 - **v2.0** — Added: Liquid glass/metal material system, deterministic motion parameters, loading state machine implementation 876 - **v1.0** — Original: Observe → Classify → Detect → Architecture → Visual → Material → Motion → Performance → Loading → Verify → Accept