Virtual office — Victor Agnew

Victor Agnew

Staff AI Platform Engineer · Vancouver, BC

Boring deploys. work/ Safe AI. ai-lab/ Happy teams. virtual-office.md

PIPELINE
>20 min → <5 min
DEPLOYS
weekly → hundreds/week
FAILED PIPELINES
<1%
CREDENTIAL ROTATION
6 months → 24 h
REVIEW ENV
~$9/day
GCP SPEND
−40% YoY
MCP TOOLS
160+ → 23
VIRTUAL OFFICE
~1 h 40 min
virtual-office.md Virtual office three.js · Cloudflare Workers · LiveKit Built in a day; 26 days later, 1,600+ commits and 12 coworkers committing.
Close case-studies/virtual-office.md Back

case-studies/virtual-office.md

[ VIDEO ]

Virtual office

A 3D voxel office in the browser, built in a day, that the team made its own.

Side project for my team

Kickoff to production
~1 h 40 min
Tests by end of day one
643
Commits in 26 days
1,600+
Coworkers committing
12
  • three.js
  • TypeScript
  • Cloudflare Workers
  • Durable Objects
  • LiveKit
  • Vitest
  • Playwright

Why

Remote teams lose the hallway. I wanted a place to hang out between meetings and on Friday afternoons, where you can walk up to someone and just talk.

What it is

A 3D voxel office in the browser. You walk around as a character you design (human, animal or a special skin), talk to whoever is near you with proximity voice, share your screen to a TV in a private pod, and play: pool, ping pong, darts, mini-golf and racing, with a Hall of Fame for the best scores. There's a koi pond to fish in, too.

How it's built

  • The client is three.js and TypeScript, served by Cloudflare Workers. A Durable Object holds the live office state, so there's no server to run.
  • Proximity voice and screen sharing run on LiveKit.
  • Sign-in is the company's Google Workspace.
  • Every push to main runs the checks, deploys, and smoke-tests production.
[ FIG. 1 ] No server to run
No server to run Left: a "browser" zone holding a "three.js client" box. Right, stacked: a "Cloudflare" zone holding a "Workers" box (sub-label "serve the client") above a "Durable Object" box (sub-label "live office state"), joined by a two-way arrow; below the zone, a "LiveKit" box (sub-label "proximity voice · screen sharing"). Two-way arrows join the client to Workers and to LiveKit. Along the bottom, two lines in mono: "sign-in: Google Workspace" and "every push to main: checks → deploy → smoke-test production". browser three.js client Cloudflare Workers serve the client Durable Object live office state LiveKit proximity voice · screen sharing sign-in: Google Workspace every push to main: checks → deploy → smoke-test production

Built in a day

I planned the work, then fanned it out to parallel AI coding agents, each in its own git worktree. Kickoff to production took about an hour and forty minutes. By the end of day one, 643 tests passed, and a second batch had shipped mini-golf, a Hall of Fame and flag sweaters.

[ FIG. 2 ] From kickoff to 12 coworkers committing
From kickoff to 12 coworkers committing A left-to-right timeline. It starts at "plan the work", which fans out to three parallel lanes, each "AI coding agent" with the sub-label "own git worktree". The lanes rejoin at three milestones along the line: "production" ("~1 h 40 min after kickoff"); "end of day one" ("643 tests passing", "second batch: mini-golf, Hall of Fame, flag sweaters"); and "26 days after the first commit" ("1,600+ commits", "12 coworkers committing"). plan the work AI coding agent own git worktree AI coding agent own git worktree AI coding agent own git worktree production ~1 h 40 min after kickoff end of day one 643 tests passing second batch: mini-golf, Hall of Fame, flag sweaters 26 days after the first commit 1,600+ commits 12 coworkers committing

Fixing the lag

With two people walking around, remote avatars stuttered. A simulation of the real interpolation code over a modelled network found two causes: the playout buffer shrank by the one-way latency, and the clients' sends beat against the server's snapshots. Past about 67 ms of round-trip time, remote avatars froze between snapshots, on 38% of frames at 120 ms and 81% at 250 ms. An arrival-aligned playout clock brought stalls under 1% from 10 to 250 ms.

[ FIG. 3 ] Frames with frozen remote avatars, before and after
Frames with frozen remote avatars, before and after Horizontal bar chart on one linear scale from 0% to 100% of frames, ticks every 20%. Two groups by round-trip time, "120 ms" and "250 ms", each with a "before" bar (38% and 81%) and an "after" bar drawn as a 1% stub labelled "<1%". Two notes in small mono text: "before: remote avatars froze between snapshots past about 67 ms of round-trip time" and "after: an arrival-aligned playout clock brought stalls under 1% from 10 to 250 ms". 120 ms before 38% after <1% 250 ms before 81% after <1% 0% 20% 40% 60% 80% 100% before: remote avatars froze between snapshots past about 67 ms of round-trip time after: an arrival-aligned playout clock brought stalls under 1% from 10 to 250 ms

Rendering was never the bottleneck: measured on an M3 Pro, frames held 60 fps with 0.8–1.6 ms of main-thread work each.

What happened next

Twenty-six days after the first commit, the repo had more than 1,600 commits, and 12 coworkers had committed to it, adding features and fixes.

next case study In-app AI assistant