An interactive 3D Olympic stadium and its surrounding Olympic citadel, built with Three.js (vanilla JavaScript, no build step) for the Interactive Graphics course at Sapienza University of Rome.
The project brings together a complete stadium, procedural sports animations and event systems, an interactive activities menu, cinematic cameras, post-processing, PBR materials, HDR image-based lighting, cascaded shadow maps, a pooled particle system, and a large procedural exterior campus set on a small "floating island" world with a day/night sky.
Everything that moves — athletes, vehicles, drones, flags — is authored by hand in JavaScript with delta-time. No imported animation clips are played, in line with the course requirements.
The project targets GitHub Pages and uses no build step.
- Full Olympic stadium: football pitch, IAAF-style 400 m eight-lane athletics track, procedural seating bowl, tensile roof canopy and light-concrete exterior facade with a colonnade.
- Long-jump runway and pit, and a high-jump facility (fan, mat, uprights, crossbar) — both persistent geometry, revealed only during their events.
- LED ribbon, roof LED matrix and an oversized front videowall (the interactive home screen).
- Sprint — full state machine: on your marks, set, start, running, finish, celebration, with a broadcast-style start list and results overlay.
- Long Jump — event animation plus a playable minigame (timing-based run-up, take-off and foul detection).
- High Jump — AI event plus a playable minigame (timed take-off, progressive bar raises).
- Football — match playback with goals, entrance ceremony and a penalty shoot-out (best of five, click the goal mouth).
- Videogames tile — entry point to the playable minigames from the home menu.
- Hierarchical, articulated athlete model driven entirely by parent-child transformations.
- Hand-written joint rotations (pelvis, torso, shoulders, elbows, wrists, hips, knees, ankles).
- Procedural running, jumping, event poses and celebrations — no imported clips.
- A stadium-sized videowall leaning on the front facade acts as the Activities home screen: clickable tiles for Football, Sprint, Long Jump, High Jump, Videogames and the Exterior Tour, with a "go to home view" shortcut.
The stadium sits inside a procedural citadel organized into coherent precincts:
- ceremonial Main Plaza with the Olympic rings, cauldron and a central pedestrian axis;
- a double Olympic flag avenue of animated waving national flags;
- civic / administration and hospitality / public precincts;
- district / support and sports / event-support blocks;
- parking and mobility boulevard and fields;
- service / logistics area;
- a green belt landscape buffer and a dense procedural forest park behind the stadium;
- a ring road, sidewalks, crossings and roundabout.
Reusable procedural building typologies (offices, hotels, sports halls, mixed-use and service blocks) with instanced windows, facade fins, slab lines, plinths, entrance panels, parapets, rooftop HVAC and solar equipment, and deterministic warm/cool/dim emissive windows. Repeated detail is drawn with InstancedMesh to keep draw calls low.
Hand-authored, delta-time motion brings the campus alive:
- animated shuttle and media van following the exterior road network;
- a fleet of drones;
- national flags waving on the flag avenue (vertex animation);
- an advertising blimp circling the venue with a trailing banner;
- LED billboards cycling messages and a scrolling ticker.
- The campus sits on a procedural organic island: a noise-based coastline with promontories under the distant precincts, a faceted rock skirt and a craggy grass relief on the outer ring, reading as a "small world" surrounded by haze.
- Linear fog tied to the sky-horizon colour (day and night) dissolves the far edge into the sky.
- A fog plane masks the sky dome's lower hemisphere so the horizon never reads as a hard edge.
- A camera-following sky dome: the procedural gradient sky and celestial bodies re-centre on the camera every frame, so the sky always reads as infinite and never as a finite bubble, from any distance.
PBR materials on the main exterior surfaces (asphalt, concrete pavement, concrete pavers, grass), using base-color, NormalGL and roughness maps, alongside a shared pool of tuned procedural materials (lawn, stone, curb, metal, LED emissives, foliage tones).
- Procedural gradient sky with an instant day/night switch driven by a central
LightingManager. - Hemisphere fill, a directional shadow-casting sun/moon, stadium floodlights and emissive LED systems.
- Cascaded Shadow Maps (CSM) for crisp, campus-wide shadows across the large exterior.
- HDR image-based lighting through
EXRLoader+PMREMGenerator, assigned toscene.environmentfor PBR reflections while the procedural sky stays the visible background. - ACES Filmic tone mapping.
A shared, pooled particle core (THREE.Points, additive, zero-allocation, disposable) powers:
- fountain water jets on the plaza fountains;
- ceremony fireworks: rockets with spark trails that burst into radial explosions with secondary crackles, launched in auto-scheduled show waves.
RenderPass → depth of field (BokehPass) → bloom (UnrealBloomPass) → OutputPass.
A Director-based camera architecture with smooth tweened transitions.
- Inner stadium views: Default, Campus View, Home Screen, Broadcast TV, Spider-cam, Free Explore.
- Exterior views: Exterior Overview, Main Plaza, East Zone, Green Park, West Zone.
- An in-sport camera switcher for event-specific angles.
A lightweight startup overlay with the Olympic rings that light up in sequence (CSS-only, so the animation keeps running while the scene builds) and CURLYMPICS branding, shown for a short minimum and then fading out once the first frame is ready — masking the initial boot cost.
The project uses a lightweight custom on-screen HUD instead of a debug GUI panel: control pills for the home menu, the exterior views and an in-sport camera switcher, plus a full set of keyboard shortcuts. Press Esc for a full-screen overlay listing every control.
The project uses vanilla JavaScript ES Modules with no build step. Three.js and related libraries are loaded through the import map in index.html.
⚠️ Do not openindex.htmlviafile://. ES modules and local assets must be served over HTTP.
From the project root:
python3 -m http.server 8000Then open http://localhost:8000. A Node alternative: npx serve .
Press Esc at any time for the full on-screen shortcuts panel.
| Action | Control |
|---|---|
| Show / hide the shortcuts panel | Esc |
| Home — stop the current sport | H |
| Toggle day / night | L |
| Start sport | 1 Sprint · 2 Long Jump · 3 High Jump · 4 Football · 5 Penalty |
| Play jump minigame | 6 Long Jump · 7 High Jump — then Space / Tab to take off at the mark |
| Switch camera | Shift+1..6 (Campus · Overview · Main Plaza · East Zone · Green Park · West Zone), or the HUD pills |
| Free-fly move | W A S D, Q / E down / up |
| Free-fly look / zoom | Mouse drag / scroll |
| Open an activity | Click a tile on the home videowall |
src/
├── assets/
│ ├── hdri/ # runtime HDR environment (EXR)
│ ├── models/ # curated glTF/GLB assets (nature, buildings, urban props)
│ └── textures/ # PBR texture sets
│
├── athletes/ # articulated athlete models and poses
├── cameras/ # Director and camera modes
├── core/ # renderer, scene, camera, loop, resize, post-processing
├── events/ # sprint, long/high jump, football, penalty, minigames
├── fx/ # pooled particle core, fountain water, fireworks
├── lighting/ # lighting rig, CSM and central LightingManager
├── stadium/ # stadium, exterior campus, environment, roads, vehicles, drones
├── ui/ # HUD pills, exterior-nav, in-sport camera switcher, shortcuts overlay
└── utils/ # geometry and texture helpers
index.html # entry point, import map, home button and loading overlay
src/main.js # bootstrap: scene, loop, systems and hotkey wiring
src/core/loop.js # single delta-time animation loop
src/stadium/environment.js # island, fog plane, camera-following sky dome, day/night sky
src/lighting/lightingManager.js # day/night state, fog, floodlights, emissives
src/stadium/exteriorCampus.js # procedural citadel (precincts, buildings, roads, greenery)
src/stadium/exteriorAnimations.js # flags, blimp, drones, billboards
src/cameras/director.js # camera modes and cinematic direction
src/fx/particlePool.js # shared pooled particle system
src/ui/shortcutsOverlay.js # Esc keyboard-shortcuts overlay
One THREE.Scene, one renderer, one animation loop, one clock. ES Modules, no framework, no bundler, no build pipeline.
The scene is a hierarchical scene graph: stadium, athletes, event systems, camera rigs and the exterior campus live in separate modules. Repeated architecture and environment elements share geometry and materials and use InstancedMesh where it pays off, to keep draw calls down.
- Three.js — rendering and scene graph
- GLSL — custom sky-gradient shader and effects
- three-csm — cascaded shadow maps
- tween.js — smooth camera transitions
- OrbitControls — free exploration
- EffectComposer (
RenderPass,BokehPass,UnrealBloomPass,OutputPass) — post-processing - EXRLoader + PMREMGenerator — HDR image-based lighting
- GLTFLoader — curated static model loading
- Custom on-screen HUD (no GUI framework)
- shared geometries and materials, and a shared material pool;
InstancedMeshfor repeated architecture and vegetation;- a pooled, zero-allocation particle system;
- capped device pixel ratio and controlled shadow usage;
- a single renderer and a single loop;
- deterministic procedural generation (hash-based, reproducible);
- a reduced sky dome + near/far plane (
near 1.0 / far 2000) for better depth-buffer precision and less overdraw; - PMREM generated once at setup.
- scene graphs and hierarchical transformations
- local and world coordinate systems
- procedural modeling (hash-based, deterministic)
- rasterization through the Three.js / WebGL pipeline
- lighting and shading; PBR materials
- texture mapping and normal mapping
- image-based lighting (HDR EXR → PMREM)
- shadow mapping and cascaded shadow maps (CSM)
- a custom GLSL gradient-sky shader and a camera-following sky dome
- depth-buffer management and z-fighting resolution (Y-separation and polygon offset)
- fog as a depth cue enabling a reduced far plane
- camera transformations and cinematic direction
- animation state machines and hand-authored, delta-time animation
- hierarchical, JavaScript-authored character animation
- a pooled particle system
- post-processing (DoF, bloom, tone mapping)
- instancing and draw-call reduction
- interactive visualization and performance / visual-quality trade-offs
The project provides the required hierarchical model, lighting and textures, user interaction and JavaScript-authored animation. External static models may be used, but imported animation clips are never played: all motion — athletes, vehicles, drones, flags — is written directly in JavaScript and exploits the transform hierarchy.
Only a curated subset of each third-party pack is committed, under src/assets/: glTF/GLB models (nature, buildings, urban props), PBR texture sets and one HDR environment. Every pack is CC0 / public domain. Any embedded animation clips in bundled models are not played — all motion is authored programmatically. See the full attribution table under Credits → Asset licensing.
Developed for the Interactive Graphics course, MSc in Engineering in Computer Science, Sapienza University of Rome.
- Authors: Nicholas Caggiula, Matteo Giuliani
- Course instructor: Prof. Marco Schaerf
The project builds on Three.js and related open-source libraries (see the Tech Stack). All third-party 3D models, texture sets and the HDR environment are CC0 / public domain, and only a curated subset of each pack is committed under src/assets/. All athletes and crowd figures in the scene are the hand-built articulated athlete rig (Section on Athlete Animation), authored in JavaScript — not imported character models.
| Asset pack | Author / provider | Source | License | Files used (in src/assets/) |
Modifications |
|---|---|---|---|---|---|
| Stylized Nature MegaKit (free) | Quaternius | quaternius.com | CC0 | models/nature/ — loaded: CommonTree_1/3, Pine_1, Bush_Common, Bush_Common_Flowers (.gltf/.bin) + textures (Bark_NormalTree, Leaf_Pine_C, Leaves_NormalTree_C, Leaves_TwistedTree_C, Grass, Flowers); other bundled nature files are unused |
Selected files only; instanced and re-scaled at runtime |
| City Kit Commercial / City Kit Industrial (buildings) | Kenney | kenney.nl/assets/city-kit-commercial · kenney.nl/assets/city-kit-industrial | CC0 | models/buildings/kenney-commercial/ — building-e/j/k.glb; models/buildings/kenney-industrial/ — building-a/b/r.glb (+ Textures/colormap.png) |
Selected files only; instanced across the exterior precincts |
| City Kit Roads (urban props) | Kenney | kenney.nl/assets/city-kit-roads | CC0 | models/urban-props/ — loaded: light-square, sign-highway, construction-barrier, construction-cone (.glb); other bundled props are unused |
Selected files only; placed as street furniture |
| PBR surface textures | ambientCG | ambientcg.com | CC0 | textures/concrete/ — concrete-pavement-02, concrete-pavers; textures/grass/grass005; textures/asphalt/road012b — each color + normal-gl + roughness map |
Downscaled/renamed; tiled on ground/pavement/track surfaces |
| HDR environment — Solitude Night | Poly Haven | polyhaven.com/a/solitude_night | CC0 | hdri/solitude-night/environment.exr |
Used for IBL via EXRLoader → PMREMGenerator (scene.environment only; procedural sky stays the visible background) |
| Spot / break splash images | (own / project art) | — | — | assets/spot_image/spot_main.jpeg, spot_break.jpeg |
Used on the in-game spot cards |
All external packs above are distributed under CC0 1.0 (public domain), which permits use, modification and redistribution without attribution; credits are given here as good practice.
