Pixel Office Remotion Composition — Design Spec
Goal
Create a parametrizable Remotion composition that renders the Agent Squad 3D office as video via Lambda. Supports multiple scenes, camera movements, formats, and can optionally reflect real agent status. Triggered from CLI, Claude Code, or Telegram.
Decisions
| Decision |
Choice |
| Architecture |
Modular — composición orquestadora + componentes R3F reutilizables |
| 3D Library |
react-three-fiber + @react-three/drei (via @remotion/three) |
| Parametrización |
Todo: visuales, estructura, escenas, cámaras |
| Output |
Múltiples formatos: landscape (1920x1080), portrait (1080x1920), square (1080x1080) |
| Trigger |
Comando CLI invocable desde Claude Code / Telegram |
| Estética |
Base del playground + mejoras + libertad para elevar calidad |
| Datos |
Híbrido: props estáticos por defecto, opcionalmente status.json real |
Architecture
src/
├── PixelOffice.tsx ← Composición principal (orquestador)
├── pixel-office/
│ ├── types.ts ← Props, AgentDef, ScenePreset, CameraPreset
│ ├── presets.ts ← Presets de escena/cámara + agentes default
│ ├── scene/
│ │ ├── OfficeScene.tsx ← Escena completa (ensambla todo)
│ │ ├── Floor.tsx ← Piso con zonas coloreadas
│ │ ├── BrandWall.tsx ← Pared con logo canvas (ambas caras)
│ │ ├── Furniture.tsx ← Escritorios, sillas, mesa conferencia
│ │ ├── Props.tsx ← Plantas, café, server rack
│ │ └── Whiteboard.tsx ← Pizarra
│ ├── characters/
│ │ ├── VoxelCharacter.tsx ← Personaje parametrizable (color, escala, rol, label)
│ │ └── CharacterAnimations.tsx ← Animaciones por estado usando useCurrentFrame()
│ ├── camera/
│ │ ├── CameraController.tsx ← Mueve cámara según preset + frame actual
│ │ └── cameraPresets.ts ← orbit360, dolly, zoneZoom, cenital, static
│ └── lighting/
│ └── OfficeLighting.tsx ← Iluminación parametrizable
Data Flow
PixelOffice recibe props (agentes, escena, cámara, formato, duración)
- Si
statusUrl presente, hace fetch y mergea estados reales
OfficeScene ensambla Floor + BrandWall + Furniture + Props + Characters
CameraController interpola posición de cámara frame a frame via useCurrentFrame()
- Cada
VoxelCharacter anima según estado usando frame actual + seed determinista
- Remotion renderiza frame por frame → Lambda genera MP4
Props Interface
interface PixelOfficeProps {
// Agents
agents: AgentDef[];
// Scene
scene: "full" | "conference" | "zone" | "cenital";
focusZone?: "miles" | "pmo" | "alexa";
// Visual
wallColor: string;
logoText: [string, string];
logoColors: [string, string];
floorColor: string;
ambientIntensity: number;
// Camera
camera: "orbit360" | "dolly" | "zoneZoom" | "cenital" | "static";
cameraSpeed: number;
// Output
duration: number;
format: "landscape" | "portrait" | "square";
// Optional real data
statusUrl?: string;
// Thumbnail
thumbnail?: boolean;
}
interface AgentDef {
id: string;
label: string;
team: "miles" | "pmo" | "alexa";
isChief: boolean;
zone: "conference" | "miles" | "pmo" | "alexa";
state?: "idle" | "working" | "thinking";
}
Scene Presets
| Preset |
Description |
full |
Planta completa — todas las zonas, sala de juntas, pared con logo |
conference |
Solo sala de juntas — 3 chiefs, mesa oval, pared de fondo |
zone |
Zoom a una zona específica (requiere focusZone) |
cenital |
Vista cenital de toda la planta |
Camera Presets
| Preset |
Movement |
orbit360 |
PerspectiveCamera, vuelta completa 360° alrededor de la oficina |
dolly |
Paneo lateral suave de izquierda a derecha |
zoneZoom |
Empieza general → zoom a una zona → vuelve a general |
cenital |
Vista fija desde arriba con rotación lenta |
static |
Cámara fija isométrica (ideal para thumbnail) |
Default Agents
Miles Team — #3B82F6
| ID |
Label |
Chief |
| miles |
Miles |
Yes |
| miles-dev |
Miles/Dev |
No |
| miles-architect |
Miles/Architect |
No |
| miles-research |
Miles/Research |
No |
| miles-community |
Miles/Community |
No |
| miles-content |
Miles/Content |
No |
| miles-advisor |
Miles/Advisor |
No |
PMO Team — #10B981
| ID |
Label |
Chief |
| pmo |
PMO |
Yes |
| pmo-dev |
PMO/Dev |
No |
| pmo-architect |
PMO/Architect |
No |
| pmo-qa |
PMO/QA |
No |
Growth Squad — #F59E0B
| ID |
Label |
Chief |
| alexa |
Alexa |
Yes |
| alexa-scout |
Alexa/Scout |
No |
| alexa-writer |
Alexa/Writer |
No |
| alexa-developer |
Alexa/Developer |
No |
| alexa-advisor |
Alexa/Advisor |
No |
Total: 3 chiefs + 13 subagents = 16 characters
State Animation
- Each agent has a deterministic state cycle based on
useCurrentFrame() + seed derived from agent ID
- States rotate organically: idle → working → thinking → idle
- Cycle period varies per agent (3-8 seconds equivalent in frames) for natural feel
- If
state is explicitly set in props, that agent stays fixed in that state
- If
statusUrl provided, real states override at frame 0
Render Pipeline
render_pixel_office.mjs
- Receives JSON props from CLI
- Calculates dimensions: landscape=1920x1080, portrait=1080x1920, square=1080x1080
- If
statusUrl present, fetches and merges real agent states
- Calls
renderMediaOnLambda() with composition PixelOffice
- Downloads MP4 to specified output path
- If
thumbnail: true, extracts frame at 30% duration as PNG
- Copies to
/home/clawd/playgrounds/ for immediate web access
render_pixel_office_all.mjs
- Wrapper that renders all 3 formats sequentially
- Output:
pixel-office-landscape.mp4, pixel-office-portrait.mp4, pixel-office-square.mp4
Trigger
- CLI:
node ~/thalx-web-app/remotion/render_pixel_office.mjs '{...json...}'
- Claude Code / Miles / Telegram can invoke the CLI command directly
- No cron needed — on-demand rendering
Deploy
- After render completes, auto-copies to
/home/clawd/playgrounds/
- Available at
playgrounds.digitalhubassist.ai/pixel-office-*.mp4
Example Invocations
// Default orbit 360° landscape
node render_pixel_office.mjs '{
"scene": "full",
"camera": "orbit360",
"duration": 20,
"format": "landscape",
"wallColor": "#414141",
"logoText": ["Agent", "Squad"],
"logoColors": ["#ffffff", "#ffffff"],
"floorColor": "#b8a88a",
"ambientIntensity": 1.4,
"cameraSpeed": 1
}'
// Zoom to Miles zone, portrait for Instagram
node render_pixel_office.mjs '{
"scene": "zone",
"focusZone": "miles",
"camera": "zoneZoom",
"duration": 10,
"format": "portrait"
}'
// Conference room with real status, static for thumbnail
node render_pixel_office.mjs '{
"scene": "conference",
"camera": "static",
"duration": 1,
"format": "landscape",
"thumbnail": true,
"statusUrl": "https://playgrounds.digitalhubassist.ai/api/status.json"
}'
// All 3 formats at once
node render_pixel_office_all.mjs '{
"scene": "full",
"camera": "orbit360",
"duration": 15
}'
Dependencies to Install
npm install three @react-three/fiber @react-three/drei @remotion/three
Lambda Considerations
- Three.js rendering in Lambda uses software rendering (no GPU)
- Lambda function already configured: 2048MB RAM, 240s timeout
- Expected render time: ~2-4 min for 20s video (similar to current compositions)
framesPerLambda: 20 for parallelization (already configured)
- Site bundle needs update after adding new composition:
npx remotion lambda sites create src/index.ts --site-name=thalx-remotion
Out of Scope
- Sound/music
- Real-time dashboard (that's pixel-office-app, separate project)
- Mobile responsive playground
- Physics simulation
- Texture mapping (solid colors + emissive only, matching voxel aesthetic)