Nordic Skating to LittleJS Conversion Plan
Overview
This document outlines the strategy for converting the Nordic Skating project from Three.js to LittleJS.
Current Project Analysis
Key Components Using Three.js:
-
Main Render Loop (
src/main.ts) - WebGLRenderer setup - Scene and camera management - Animation loop -
Render Components -
ice.ts: Ice surface with textures and materials -sky.ts: Sky dome with custom shader (322 lines of GLSL) -marks.ts: Skating trail marks with line geometry -drifts.ts: Drift effects -snow.ts: Particle system with custom shader -shores.ts: Shore elements
Three.js Features Used:
- WebGLRenderer
- Scene, PerspectiveCamera
- Mesh, Geometry (PlaneGeometry, SphereGeometry, BufferGeometry)
- Materials (MeshLambertMaterial, LineBasicMaterial, ShaderMaterial)
- Textures and texture loading
- Custom GLSL shaders for sky and snow effects
- Points system for particles
LittleJS Capabilities Analysis
Key Features Available in LittleJS:
- 2D/3D Hybrid Rendering: WebGL2 + Canvas2D
- 3D Support: - Built-in 3D renderer that shares canvas with 2D - Shape builders, OBJ and glTF model loading - Shadow maps, lights, cameras
- Shaders: Shadertoy-style shaders for post-processing or object-level effects
- Particles: Robust particle effect system
- Physics: Arcade physics and Box2D integration
- Textures: Runtime texture loading and atlas support
- Audio: Sound and music support with spatial audio
- Input: Comprehensive input handling (mouse, keyboard, touch)
Compatibility Notes:
- LittleJS has its own 3D renderer, but also supports Three.js as a plugin
- Custom shaders can be adapted to LittleJS's shader system
- Particle systems need conversion from THREE.Points to LittleJS particles
- Material system is different but conceptually similar
Conversion Strategy
Phase 1: Setup and Prototyping (Week 1)
Tasks: 1. Install LittleJS alongside Three.js 2. Create minimal prototype showing basic scene 3. Test shader compatibility 4. Verify performance characteristics
Implementation:
npm install littlejsengine --save
Phase 2: Core Infrastructure Migration (Week 2-3)
Tasks: 1. Replace Three.js renderer with LittleJS 2. Migrate scene management 3. Convert camera system 4. Set up animation loop compatibility
Key Changes:
- Replace THREE.WebGLRenderer with engineInit() pattern
- Adapt gameUpdate() and gameRender() callbacks
- Convert coordinate systems (LittleJS uses different conventions)
Phase 3: Component-by-Component Migration (Week 4-6)
Order of Migration:
1. Sky System (sky.ts)
- Highest priority due to complex shader
- Test shader compatibility first
-
Ice Surface (
ice.ts) - Plane geometry conversion - Material adaptation -
Snow Particles (
snow.ts) - Convert THREE.Points to LittleJS particles - Adapt custom shader -
Skating Marks (
marks.ts) - Line geometry to LittleJS equivalent -
Drifts and Shores - Final render components
Phase 4: Integration and Testing (Week 7-8)
Tasks: 1. Verify all components work together 2. Test performance characteristics 3. Optimize shaders and rendering 4. Ensure mobile compatibility 5. Test on various devices/browsers
Technical Challenges and Solutions
1. Custom Shaders Conversion
Challenge: Sky and snow use complex GLSL shaders with custom uniforms.
Solution: - Adapt shaders to LittleJS's shader system - Map Three.js uniforms to LittleJS equivalents - Test shader performance in LittleJS context
2. Coordinate System Differences
Challenge: LittleJS may use different coordinate conventions.
Solution: - Create conversion utilities for positions/rotations - Test thoroughly with existing lake geometries
3. Particle System Migration
Challenge: THREE.Points with custom shader to LittleJS particles.
Solution: - Use LittleJS's particle system API - Adapt vertex/fragment shaders to LittleJS format - Maintain same visual appearance and performance
4. Material System Differences
Challenge: Different material property names and capabilities.
Solution: - Create material conversion utilities - Test all material combinations (transparent, emissive, etc.)
Implementation Plan
Step 1: Setup Dual Environment
// In main.ts
import * as THREE from 'three'
import { engineInit } from 'littlejsengine'
// Keep Three.js for now, add LittleJS alongside
function gameInit() {
// Initialize both systems
}
function gameUpdate() {
// Update both systems in parallel during transition
}
function gameRender() {
// Render with both, compare results
}
engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRenderPost)
Step 2: Create Adapter Utilities
// src/adapters/littlejs-adapter.ts
export class LittleJSAdapter {
static convertColor(threeColor: THREE.Color): number {
// Convert Three.js Color to LittleJS format
}
static convertVector3(vec: THREE.Vector3): [number, number, number] {
// Convert Three.js Vector3 to tuple
}
static createMaterial(options: {
color?: THREE.Color,
transparent?: boolean,
emissive?: THREE.Color
}): any {
// Create LittleJS material equivalent
}
}
Step 3: Migrate Sky Component First
// New sky-littlejs.ts
import { engine, createShaderMaterial } from 'littlejsengine'
export class SkyLittleJS {
private mesh: any
constructor() {
// Create LittleJS equivalent of sky dome
this.mesh = engine.createMesh({
geometry: engine.createSphereGeometry(1000, 32, 16),
material: createShaderMaterial({
vertex: adaptedVertexShader,
fragment: adaptedFragmentShader,
uniforms: this.setupUniforms()
})
})
}
private setupUniforms() {
// Map Three.js uniforms to LittleJS format
}
}
Testing Strategy
- Unit Tests: Test each component in isolation
- Visual Regression: Compare screenshots before/after
- Performance Benchmarks: Measure FPS and memory usage
- Cross-Browser Testing: Ensure compatibility across browsers
- Mobile Testing: Verify touch controls and performance
Rollback Plan
If conversion proves problematic: 1. Maintain Three.js as primary renderer 2. Use LittleJS for specific optimized components only 3. Keep both renderers with feature flags 4. Gradual migration path remains open
Success Criteria
- All visual elements maintain same appearance
- Performance meets or exceeds current implementation
- Mobile compatibility improved
- Bundle size reduced (target: < 50KB gzipped)
- No regression in functionality
- Easier to maintain and extend
Timeline Estimate
- Total Duration: 8 weeks
- Critical Path: Shader conversion and particle system migration
- Risk Areas: Complex shaders, coordinate system differences
Resources
- LittleJS Documentation: https://github.com/KilledByAPixel/LittleJS
- Three.js to LittleJS Migration Guide (to be created)
- Test cases for visual regression
- Performance benchmarks