BUILT BYSCROLL.

- STUDIO
- MILWEB
- TYPE
- SCROLL EXPERIENCE
- STATUS
- LIVE
02 / THE IDEA
Scrolling builds the building. Scrolling back tears it down.
An architecture firm sells execution, not photos. The site is a real construction film — scaffolding to façade — whose time belongs entirely to the scroll, no autoplay. Midway, the video dims into a light table: the floor plan draws itself with real dimensions.
01 / SCRUB
One ScrollTrigger per section writes (scene, progress); a rAF loop reads it every frame and chases the video's currentTime with damping. Jumping scenes is a time dolly, never a hard cut.

02 / SHEET
The video dims into a light table and the floor plan draws itself in sync with scroll: walls, furniture, dimensions and a drafting stamp (SCALE 1:75).

03 / DELIVERED
After the film, the site becomes the studio's archive: works, process and contact, with a progress rail and a custom cursor.

SCRUB
SHEET
DELIVERED
04 / UNDER THE HOOD
SCROLL
SCROLL / REVERSE
ScrollTrigger
progress = 0.000
world.ts
{ scene, progress }
rAF + damping
damped = 0.000
video.currentTime
DAY 000 / 132
SCROLL ENGINEShared mutable state (world.ts): ScrollTrigger writes, rAF reads and chases the video with damping. Script in scenes.ts.+
The architecture splits video from scroll via a shared mutable state (world.ts), the same base as AUREX MOTORS. One ScrollTrigger per section writes (scene, progress), and a rAF loop reads it every frame to chase the video's currentTime with damping, so jumping scenes becomes a time dolly, never a hard cut. The film's script lives in one file (scenes.ts) mapping each scene to a fraction of the video's duration. The video itself is encoded with GOP 1, meaning every frame is a keyframe, because without it seeking during scrub stutters. The tradeoff is a larger file, paid knowingly.
VIDEO SCRUBGOP 1 — every frame is a keyframe, otherwise seeking stutters. Bigger file, paid knowingly.+
The architecture splits video from scroll via a shared mutable state (world.ts), the same base as AUREX MOTORS. One ScrollTrigger per section writes (scene, progress), and a rAF loop reads it every frame to chase the video's currentTime with damping, so jumping scenes becomes a time dolly, never a hard cut. The film's script lives in one file (scenes.ts) mapping each scene to a fraction of the video's duration. The video itself is encoded with GOP 1, meaning every frame is a keyframe, because without it seeking during scrub stutters. The tradeoff is a larger file, paid knowingly.
BLUEPRINTFloor plan via pathLength=1; scene boundaries calibrated by extracting frames every 0.5 s (real transformation between 40% and 70%).+
The most distinctive scene is 'Prancha' (Blueprint): the video darkens into a light table and the floor plan draws itself in sync with scroll, with walls, furniture, dimension lines carrying real measurements and a drafting stamp, using the same pathLength=1 technique from an earlier project in the same family. The script's boundaries weren't guessed. I extracted frames from the source video every 0.5s to find where the real visual transformation happens, which turned out to be concentrated between 40% and 70% of the duration, then recalibrated the scenes so 'Vedação' and 'Fachada' actually read as different stages. Before that fix, two of the three scenes showed nearly the same static scaffolding.
DESIGN AUDIT0–10 self-audit against the real build: illegible header and a mobile menu broken by backdrop-filter — both fixed.+
Like the earlier stadiums project, the site went through a critical self-audit of its design (hero, storytelling, UX, UI, responsiveness, scored 0-10 per axis), run against the actual production build rather than against intent. The most serious finding was the fixed header, which had no real opaque background: any light-colored heading scrolling underneath it turned into illegible text-on-text. The fix surfaced a subtle CSS gotcha. A 'backdrop-filter' on the header becomes a containing block for 'position: fixed', so the fullscreen mobile menu was being sized relative to the header's own height instead of the viewport, until the panel was moved outside that element. After the audit came a real hamburger mobile nav, differentiated CTAs (one closes the film, only the other converts), a film progress rail and a custom cursor.
BUILT WITH — Next.js · TypeScript · GSAP · ScrollTrigger · Lenis · Tailwind · FFmpegCODE ↗
05 / DETAILS
100%
OF THE FILM ON SCROLL
GOP 1
EVERY FRAME A KEYFRAME
0,5s
BETWEEN SAMPLED FRAMES
1:75
BLUEPRINT SCALE
06 / RESULT



TIME,DISASSEMBLED.
