Inspiration

The phrase "hurry up and wait" captures a common paradox: moving faster does not always mean arriving sooner. Traffic lights, intersections, and urban layouts force even the fastest travelers to stop and wait. Beyond the mechanical, life also isn't about speed first — there is a philosophical and emotional dimension to slowing down, patience, and presence. This scene plays that idea literally by pitting a courier against a snail through the same procedurally generated city.

What it does

"Hurry Up & Wait - Faster isn't always sooner" is a procedurally generated 3D animation built in Blender. It shows a courier and a snail traveling through the same city. Despite the courier's much higher speed, traffic signals and the structure of the city create stops that level the playing field. The animation makes the point visually: being faster does not guarantee arriving first.

How we built it

The scene is generated entirely through code, not manual Blender edits:

  • Config-driven generation: All key numbers (timings, sizes, colors, seed) live in config/scene.json. scripts/build_scene.py reads this config to build the scene from scratch.
  • Deterministic procedural generation: common.rng_for(cfg, salt) derives every random stream from meta.seed, so the same config always produces the identical city.
  • Strict scene hygiene: Builders only remove objects/collections whose names start with RJ_. The scene is named HurryUpAndWait. Every object goes into a named RJ_* collection (RJ_City, RJ_Road, RJ_Props, RJ_Courier, RJ_Snail, RJ_Lights, RJ_Cameras, RJ_FX).
  • Procedural materials only: No image textures or external asset files. All materials are generated procedurally.
  • Portable packaging: scripts/pack_renderjuice.py builds a self-contained package in renderjuice/, and scripts/validate_portable.py renders test frames from the packaged .blend/.zip to verify portability.
  • Verification and QA: scripts/qa_frames.py checks specific frames and expected objects. scripts/benchmark.py measures render cost (important since Cycles runs on CPU). scripts/wip_renders.py produces labeled WIP stills.
  • Deterministic build workflow: Running scripts/build_scene.py regenerates blender/hurry-up-and-wait.blend so the file always matches config/scene.json.

Challenges we ran into

  • CPU-only rendering: The environment runs Cycles on CPU (no GPU for EEVEE). Emissive materials/panels are expensive, so geometry and lighting had to be budgeted carefully. benchmark.py helped track cost.
  • Procedural layout without manual fixes: Creating a coherent road network, intersections, and sensible routes procedurally while keeping it reproducible took careful parameterization.
  • Determinism across changes: Keeping everything seeded via rng_for with salts meant any change had to preserve the "only touch what the salt covers" discipline. Small edits couldn't just be fudged in the .blend.
  • Portable validation: Ensuring the packaged scene renders correctly outside the original project tree (paths, linked data, etc.) required explicit validation with validate_portable.py.
  • Staying config-driven: Avoiding hardcoded values in scripts was essential. Anything that affects timing/size/color belongs in config/scene.json.

Accomplishments that we're proud of

  • Fully reproducible build: One command (scripts/build_scene.py) regenerates the entire scene deterministically from config.
  • Clean, disciplined generation: Strict RJ_ prefixing and non-destructive cleanup prevent accidental data loss.
  • Portable, contest-ready package: The build produces a validated portable zip that renders correctly in an isolated environment.
  • Performance-aware design: Render cost is measurable and considered during changes because of CPU-only constraints.
  • Clear verification workflow: QA checks are scripted (specific frames, expected objects, timing match). Claims about renders are backed by measured numbers.

What we learned

  • Procedural generation rewards discipline: Config-driven + deterministic RNG makes iteration safer and debugging easier.
  • Constraints drive better design: CPU-only Cycles forced prioritizing readability/performance over gratuitous geometry/glow.
  • "Faster isn't always sooner" is a strong procedural premise: Signal timing and layout create narrative tension even with very simple actors (courier vs snail).
  • Validation is part of the build: Portability/render checks catch real failures, not just warnings.

What's next for Hurry Up & Wait - Faster isn't always sooner

  • Explore timing variations: Try different signal phasings/routes to show more clearly when/why the courier waits.
  • More city variety: Generate different block sizes, intersection types, or lane counts while keeping determinism.
  • Tighter storytelling: Highlight key moments (arrivals, waits) with framing/camera choices or clearer visual cues.
  • Benchmark-driven polish: Find safe optimizations that reduce CPU render time without changing the core "hurry up and wait" behavior.
  • Broader scenario set: Test other "faster isn't always sooner" cases while keeping the same config-driven, reproducible approach.

Built With

  • 3d-animation
  • algorithmic-city-generation
  • asset-free-pipeline
  • automated-testing
  • benchmarking
  • blender-4.5
  • config-driven-pipeline
  • cpu-rendering
  • cycles
  • deterministic-builds
  • deterministic-rng
  • json-configuration
  • path-following
  • portable-packaging
  • portable-validation
  • procedural-generation
  • procedural-materials
  • python
  • qa-automation
  • render-validation
  • reproducible-builds
  • scene-generation
  • scripted-build
  • timing-analysis
  • traffic-simulation
Share this project:

Updates

Submission history