Feedback on realistic lighting and performance

Built a browser-based 3D reconstruction of a Turkish hotel resort my son and I stayed at. He didn’t want the vacation to end, so we rebuilt it — entirely with AI pair programming (Claude Code).

Play it: Dream World Hotels – Side, Kumköy (3D)
Source: GitHub - pmmathias/hotelSim: 3D WebGL walkthrough of Dream World Hotels (Side, Turkey) – built entirely by Claude Code, zero human-written code · GitHub

What’s in the scene

  • Two multi-story hotel buildings with enterable lobbies, climbable staircases, and furnished hotel rooms (beds, bathrooms, mirrors)
  • 6 swimming pools with reflective water
  • 11 waterslides across 6 towers (you can climb them!)
  • Day/night cycle with animated LED strips and disco lighting
  • Procedural Mediterranean terrain
  • All textures procedurally generated (marble with veins, damask wallpaper, wood grain) — zero external texture files

Performance techniques

The scene has ~2000 meshes, so performance required some work:

  • Quadtree + frustum culling — custom spatial index, only visible objects are rendered (~200-400 per frame instead of 2000)
  • LOD system — buildings switch to simplified geometry beyond 60m, minimal beyond 200m
  • InstancedMesh — amphitheater seating (hundreds of chairs as one draw call)
  • Material caching — shared material pool, no duplicates
  • Per-floor exterior culling — balcony decorations grouped per floor, entire floors culled when out of view
  • Spatial collision grid — for first-person physics without checking every mesh

Runs at 30-60fps on mid-range hardware, 60fps on M-series Macs.

Where I’d love feedback

1. Lighting. Despite PBR materials (MeshStandardMaterial + MeshPhysicalMaterial), envMap reflections, and AgX tone mapping — the scene still looks fairly “flat,” especially the building exteriors. No ambient occlusion, no indirect light bouncing. Has anyone had success with SSAO or N8AO in scenes this large without tanking performance? Or is baked lightmaps the way to go for procedural geometry?

2. More performance ideas? I’m already doing quadtree culling + LOD + instancing. Anything else that could squeeze out more frames — maybe geometry merging per quadtree node, or occlusion culling beyond frustum?

Built with Three.js (ES modules, no bundler). Happy to answer questions about the architecture.