React Frontend Overview
The React frontend provides an interactive UI for controlling the ray tracer and displaying rendered images.
Component Architectureβ
src/
βββ components/
β βββ canvas/
β β βββ RaytracerCanvas.jsx # Main render canvas
β β βββ RaytracerCanvas.css
β β βββ SceneToolbar.jsx # Scene preset selector
β β βββ SceneToolbar.css
β βββ controls/
β β βββ ControlPanel.jsx # Tabbed control container
β β βββ ControlPanel.css
β β βββ LightControls.jsx # Light position controls
β β βββ MaterialControls.jsx # Material property controls
β β βββ CameraControls.jsx # Camera position/FOV controls
β β βββ ViewControls.jsx # Grid and resolution controls
β β βββ ControlSection.css # Shared control styles
β βββ layout/
β β βββ Header.jsx # App header with stats
β β βββ Header.css
β β βββ InfoModal.jsx # About dialog
β β βββ InfoModal.css
β βββ ui/
β βββ Tabs.jsx # Reusable tab component
β βββ Tabs.css
β βββ Slider.jsx # Reusable slider component
β βββ Slider.css
β βββ Toggle.jsx # Reusable toggle component
β βββ Toggle.css
βββ hooks/
β βββ useWasm.js # WebAssembly loading hook
βββ App.jsx # Main application
βββ App.css # Global styles
βββ main.jsx # Entry point
State Managementβ
The app uses React's useState for all state management:
// App.jsx
const [scenePreset, setScenePreset] = useState(0);
const [sphereCount, setSphereCount] = useState(1);
const [light, setLight] = useState({ x: 2, y: 3, z: -2 });
const [material, setMaterial] = useState({
color: { r: 0.9, g: 0.2, b: 0.15 },
specular: 0.5,
shininess: 32,
reflectivity: 0.3
});
const [camera, setCamera] = useState({
position: { x: 0, y: 0.5, z: -4 },
target: { x: 0, y: 0, z: 0 },
fov: 60
});
const [view, setView] = useState({
showGroundPlane: true,
showGrid: true,
gridScale: 1.0,
groundReflectivity: 0.15,
maxBounces: 5,
resolution: 512
});
Data Flowβ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β App.jsx β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β State: light, material, camera, view, scenePreset β β
β βββββββββββββββββββββββββ¬ββββββββββββββββββββββββββββββ β
β β β
β βββββββββββββββββΌββββββββββββββββ β
β βΌ βΌ βΌ β
β βββββββββββββββββ βββββββββββββ βββββββββββββββββββ β
β β ControlPanel β βSceneToolbarβ β RaytracerCanvas β β
β β (onChange) β β(onPreset) β β (renders image) β β
β βββββββββββββββββ βββββββββββββ ββββββββββ¬βββββββββ β
β β β
β βΌ β
β ββββββββββββββββ β
β β WASM Module β β
β β (C++ API) β β
β ββββββββββββββββ β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Key Design Decisionsβ
1. Prop Drilling vs Contextβ
We use prop drilling for simplicity. The component tree is shallow enough that Context would add unnecessary complexity.
2. Controlled Componentsβ
All inputs are controlled - their values come from React state:
<Slider
value={light.x}
onChange={(v) => setLight(prev => ({ ...prev, x: v }))}
/>
3. Debounced Renderingβ
Canvas renders are debounced with requestAnimationFrame to prevent excessive WASM calls:
useEffect(() => {
const frameId = requestAnimationFrame(renderFrame);
return () => cancelAnimationFrame(frameId);
}, [renderFrame]);
4. Modular UI Componentsβ
Reusable components (Slider, Toggle, Tabs) are in components/ui/ and can be used anywhere.
Styling Approachβ
- CSS Variables for theming in
:root - Component CSS files alongside each component
- BEM-like naming for clarity
- Dark theme by default
:root {
--bg-primary: #09090b;
--accent-primary: #e85d4c;
--text-primary: #f4f4f5;
/* ... */
}
Next Stepsβ
Explore each component category:
- Hooks - Custom React hooks
- Canvas - Render target and toolbar
- Controls - Control panels
- UI Components - Reusable elements