Why Add TypeScript to the Classic Techniques Module?
Bruno’s Classic Techniques module teaches the core building blocks every Three.js project needs. The plain JavaScript style works great for learning, but it’s easy to introduce silent bugs (wrong light properties, shadow typos, mismatched particle attributes, etc.).
With just a few targeted types you instantly get:
- Errors caught in your editor before runtime
- Full autocomplete
- Self-documenting code
- Safe refactoring and proper memory cleanup
Everything below is a direct port — the logic, math, and final visuals are 100% identical to the course lessons.
Quick Setup (for new readers)
- Three.js (latest version) — official types are included
- TypeScript 5+ with
"strict": trueintsconfig.json - Recommended: Vite +
@vitejs/plugin-react(or plain TS setup)
npm install three
npm install --save-dev typescript vite
Part 1: Lights – Type Inference in Action
Original JS (course style):
const light = new THREE.AmbientLight(0xffffff, 0.4);
light.intensityyyy = 0.5; // silent failure
Typed:
const light = new THREE.AmbientLight(0xffffff, 0.4);
light.intensityyyy = 0.5; // ❌ Property does not exist on type 'AmbientLight'
TypeScript respects each light’s unique API:
const ambient = new THREE.AmbientLight();
ambient.castShadow = true; // ❌ Error
const directional = new THREE.DirectionalLight();
directional.target = mesh; // ✅
directional.castShadow = true; // ✅
Grouped lights (most useful pattern):
interface LightingSetup {
ambient: THREE.AmbientLight;
directional: THREE.DirectionalLight;
accent?: THREE.PointLight;
}
function setupLighting(): LightingSetup { ... } // exact course logic
Key takeaway: One interface = autocomplete everywhere.
Part 2: Shadows – Typed Config Objects
Shadow settings are nested and typo-prone. Interfaces fix that.
interface ShadowQuality {
mapSize: number;
cameraTop: number;
cameraBottom: number;
radius?: number;
cameraLeft?: number;
cameraRight?: number;
cameraNear?: number;
cameraFar?: number;
}
function configureShadows(light: THREE.DirectionalLight, quality: ShadowQuality) {
light.shadow.mapSize.width = quality.mapSize;
// ... rest of the course settings
}
The classic 3-step setup (renderer → meshes → light) stays exactly the same.
Part 3: Particles & Galaxy Generator
The famous galaxy lesson, now typed and safe to regenerate.
interface GalaxyParameters {
count: number;
size: number;
radius: number;
branches: number;
spin: number;
randomness: number;
randomnessPower: number;
}
interface GeneratedGalaxy {
particles: THREE.Points;
geometry: THREE.BufferGeometry;
material: THREE.PointsMaterial;
dispose(): void;
}
function generateGalaxy(params: GalaxyParameters): GeneratedGalaxy {
// exact position + color gradient logic from the course
const geometry = new THREE.BufferGeometry();
const positions = new Float32Array(params.count * 3);
const colors = new Float32Array(params.count * 3);
// ... course math (radius, branchAngle, randomness, lerp)
// material + Points setup (identical to lesson)
return { particles, geometry, material, dispose() { ... } };
}
Regeneration (course challenge) becomes trivial and safe:
let currentGalaxy = generateGalaxy(defaultParams);
// later
currentGalaxy.dispose();
scene.remove(currentGalaxy.particles);
currentGalaxy = generateGalaxy(newParams);
Key takeaway: Typed parameters + explicit dispose() = no memory leaks when tweaking values live.
Part 4: Scroll State & Mouse Parallax
Simple state objects become self-documenting:
interface ScrollState {
scrollY: number;
lastScrollY: number;
velocity: number;
}
const scrollState: ScrollState = { scrollY: 0, lastScrollY: 0, velocity: 0 };
Mouse cursor object from the parallax lesson uses the exact same pattern.
Part 5: Procedural Scene Composition (House, Graves, Ghosts)
interface HouseStructure {
group: THREE.Group;
walls: THREE.Mesh;
roof: THREE.Mesh;
door: THREE.Mesh;
}
function createHouse(...) : HouseStructure { ... }
Same clean interface pattern for graves (array of meshes) and the ghost system (with update() method).
Part 6: Asset Management & Cleanup
interface LoadedAssets {
textures: Map<string, THREE.Texture>;
geometries: Map<string, THREE.BufferGeometry>;
materials: Map<string, THREE.Material>;
dispose(): void;
}
function createAssetManager(): LoadedAssets { ... }
Summary
| Concept | Used in the Course | What TypeScript Adds |
|---|---|---|
| Type Inference | Lights, BufferAttributes | Instant errors + autocomplete |
| Interfaces | Shadows, galaxy params, structures | Clear contracts for every system |
| Typed Configs | Shadow settings, particles | No silent typos |
| Dispose Methods | Galaxy, assets | Reliable memory management |
These small type additions turn the entire Classic Techniques module into production-ready code while preserving Bruno’s original teaching style and logic.
Have you converted any Three.js Journey lessons to TypeScript yet? Drop your experience (or questions) in the comments — happy to help!
Top comments (0)