For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (- [ ]) syntax for tracking.
Goal: Create a parametrizable Remotion composition that renders the Agent Squad 3D voxel office as video via Lambda, supporting multiple scenes, camera movements, and output formats.
Architecture: Modular React Three Fiber components inside a Remotion <ThreeCanvas>. PixelOffice composition orchestrates scene assembly, camera controller, and lighting. Renders via existing Lambda pipeline. Render scripts follow the same pattern as lambda_render.mjs.
Tech Stack: Remotion 4.x, @remotion/three, react-three-fiber, @react-three/drei, Three.js, TypeScript, AWS Lambda
Spec: docs/superpowers/specs/2026-04-01-pixel-office-remotion-design.md
Waves:
| Wave | Tasks | Depende de | Parallelizable |
|---|---|---|---|
| 0 | 1, 2 | — | Sí (deps + types/presets) |
| 1 | 3, 4, 5 | Wave 0 | Sí (componentes 3D independientes) |
| 2 | 6, 7 | Wave 0 | Sí (camera + lighting independientes) |
| 3 | 8 | Wave 1, 2 | No (ensambla todo) |
| 4 | 9, 10 | Wave 3 | No (composición + render + deploy) |
Files:
~/thalx-web-app/remotion/package.json~/thalx-web-app/remotion/src/pixel-office/types.tsDone when:
npm ls three @react-three/fiber @react-three/drei @remotion/three shows all installednpx tsc --noEmit passes with new types filecd ~/thalx-web-app/remotion
npm install three @react-three/fiber @react-three/drei @remotion/three
npm install -D @types/three
Create src/pixel-office/types.ts:
export interface AgentDef {
id: string;
label: string;
team: "miles" | "pmo" | "alexa";
isChief: boolean;
zone: "conference" | "miles" | "pmo" | "alexa";
state?: "idle" | "working" | "thinking";
}
export type ScenePreset = "full" | "conference" | "zone" | "cenital";
export type CameraPreset = "orbit360" | "dolly" | "zoneZoom" | "cenital" | "static";
export type OutputFormat = "landscape" | "portrait" | "square";
export interface PixelOfficeProps {
agents: AgentDef[];
scene: ScenePreset;
focusZone?: "miles" | "pmo" | "alexa";
wallColor: string;
logoText: [string, string];
logoColors: [string, string];
floorColor: string;
ambientIntensity: number;
camera: CameraPreset;
cameraSpeed: number;
duration: number;
format: OutputFormat;
statusUrl?: string;
thumbnail?: boolean;
}
export interface TeamConfig {
color: number;
name: string;
floorColor: number;
}
export const TEAMS: Record<string, TeamConfig> = {
miles: { color: 0x3b82f6, name: "MILES TEAM", floorColor: 0xa8b8d0 },
pmo: { color: 0x10b981, name: "PMO TEAM", floorColor: 0xa8c8b0 },
alexa: { color: 0xf59e0b, name: "GROWTH SQUAD", floorColor: 0xd0c8a0 },
};
export const STATES = ["idle", "working", "thinking"] as const;
export type AgentState = (typeof STATES)[number];
export const STATE_COLORS: Record<AgentState, number> = {
idle: 0x666666,
working: 0x3b82f6,
thinking: 0xf59e0b,
};
cd ~/thalx-web-app/remotion && npx tsc --noEmit
Expected: no errors (warnings OK)
cd ~/thalx-web-app/remotion
git add package.json package-lock.json src/pixel-office/types.ts
git commit -m "feat(pixel-office): install R3F deps + types"
Files:
~/thalx-web-app/remotion/src/pixel-office/presets.tsDone when:
DEFAULT_AGENTS has 16 entries (3 chiefs + 13 subagents)npx tsc --noEmit passesCreate src/pixel-office/presets.ts:
import type { AgentDef, PixelOfficeProps } from "./types";
export const DEFAULT_AGENTS: AgentDef[] = [
// Chiefs
{ id: "miles", label: "Miles", team: "miles", isChief: true, zone: "conference" },
{ id: "pmo", label: "PMO", team: "pmo", isChief: true, zone: "conference" },
{ id: "alexa", label: "Alexa", team: "alexa", isChief: true, zone: "conference" },
// Miles subagents
{ id: "miles-dev", label: "Miles/Dev", team: "miles", isChief: false, zone: "miles" },
{ id: "miles-architect", label: "Miles/Architect", team: "miles", isChief: false, zone: "miles" },
{ id: "miles-research", label: "Miles/Research", team: "miles", isChief: false, zone: "miles" },
{ id: "miles-community", label: "Miles/Community", team: "miles", isChief: false, zone: "miles" },
{ id: "miles-content", label: "Miles/Content", team: "miles", isChief: false, zone: "miles" },
{ id: "miles-advisor", label: "Miles/Advisor", team: "miles", isChief: false, zone: "miles" },
// PMO subagents
{ id: "pmo-dev", label: "PMO/Dev", team: "pmo", isChief: false, zone: "pmo" },
{ id: "pmo-architect", label: "PMO/Architect", team: "pmo", isChief: false, zone: "pmo" },
{ id: "pmo-qa", label: "PMO/QA", team: "pmo", isChief: false, zone: "pmo" },
// Alexa subagents
{ id: "alexa-scout", label: "Alexa/Scout", team: "alexa", isChief: false, zone: "alexa" },
{ id: "alexa-writer", label: "Alexa/Writer", team: "alexa", isChief: false, zone: "alexa" },
{ id: "alexa-developer", label: "Alexa/Developer", team: "alexa", isChief: false, zone: "alexa" },
{ id: "alexa-advisor", label: "Alexa/Advisor", team: "alexa", isChief: false, zone: "alexa" },
];
export const CHIEF_POSITIONS: Record<string, { pos: [number, number, number]; rot: number }> = {
miles: { pos: [-1.8, 0, -1.8], rot: Math.PI * 0.2 },
pmo: { pos: [1.8, 0, -1.8], rot: -Math.PI * 0.2 },
alexa: { pos: [0, 0, 2.2], rot: Math.PI },
};
export const SUB_POSITIONS: Record<string, [number, number, number][]> = {
miles: [
[-14, 0, -7.2], [-10.5, 0, -7.2], [-7, 0, -7.2],
[-14, 0, -3.2], [-10.5, 0, -3.2], [-7, 0, -3.2],
],
pmo: [
[-14, 0, 7.8], [-10.5, 0, 7.8], [-7, 0, 7.8],
],
alexa: [
[9, 0, -7.2], [12.5, 0, -7.2],
[9, 0, -3.2], [12.5, 0, -3.2],
],
};
export const DESK_POSITIONS: Record<string, { positions: [number, number, number][]; chairOffset: number }> = {
miles: {
positions: [
[-14, 0, -8], [-10.5, 0, -8], [-7, 0, -8],
[-14, 0, -4], [-10.5, 0, -4], [-7, 0, -4],
],
chairOffset: 1.2,
},
pmo: {
positions: [[-14, 0, 7], [-10.5, 0, 7], [-7, 0, 7]],
chairOffset: 1.2,
},
alexa: {
positions: [
[9, 0, -8], [12.5, 0, -8],
[9, 0, -4], [12.5, 0, -4],
],
chairOffset: 1.2,
},
};
export const DEFAULT_PROPS: PixelOfficeProps = {
agents: DEFAULT_AGENTS,
scene: "full",
camera: "orbit360",
cameraSpeed: 1,
duration: 20,
format: "landscape",
wallColor: "#414141",
logoText: ["Agent", "Squad"],
logoColors: ["#ffffff", "#ffffff"],
floorColor: "#b8a88a",
ambientIntensity: 1.4,
};
/** Deterministic hash for agent ID → seed for animations */
export function agentSeed(id: string): number {
let hash = 0;
for (let i = 0; i < id.length; i++) {
hash = ((hash << 5) - hash + id.charCodeAt(i)) | 0;
}
return Math.abs(hash);
}
/** Get agent state at a given frame based on deterministic cycling */
export function getAgentState(
agent: AgentDef,
frame: number,
fps: number,
): "idle" | "working" | "thinking" {
if (agent.state) return agent.state; // Explicit override
const seed = agentSeed(agent.id);
const cyclePeriod = (3 + (seed % 5)) * fps; // 3-8 seconds in frames
const phase = seed % 3;
const cyclePos = Math.floor((frame + seed * 7) / cyclePeriod) % 3;
const states: ("idle" | "working" | "thinking")[] = ["idle", "working", "thinking"];
return states[(cyclePos + phase) % 3];
}
cd ~/thalx-web-app/remotion && npx tsc --noEmit
cd ~/thalx-web-app/remotion
git add src/pixel-office/presets.ts
git commit -m "feat(pixel-office): default agents, positions, presets"
Files:
~/thalx-web-app/remotion/src/pixel-office/scene/Floor.tsx~/thalx-web-app/remotion/src/pixel-office/scene/BrandWall.tsxDone when:
npx tsc --noEmit passesCreate src/pixel-office/scene/Floor.tsx:
import React, { useMemo } from "react";
import * as THREE from "three";
import { TEAMS } from "../types";
interface FloorProps {
floorColor: string;
}
export const Floor: React.FC<FloorProps> = ({ floorColor }) => {
const floorColorHex = useMemo(() => new THREE.Color(floorColor), [floorColor]);
const zones = useMemo(() => [
{ pos: [-12, 0.01, -5] as const, size: [14, 0.1, 12] as const, color: TEAMS.miles.floorColor },
{ pos: [-12, 0.01, 9] as const, size: [14, 0.1, 8] as const, color: TEAMS.pmo.floorColor },
{ pos: [12, 0.01, -2] as const, size: [14, 0.1, 18] as const, color: TEAMS.alexa.floorColor },
], []);
return (
<group>
{/* Main floor */}
<mesh position={[0, -0.1, 0]} receiveShadow>
<boxGeometry args={[40, 0.2, 30]} />
<meshStandardMaterial color={floorColorHex} />
</mesh>
{/* Zone overlays */}
{zones.map((z, i) => (
<mesh key={i} position={[z.pos[0], z.pos[1], z.pos[2]]} receiveShadow>
<boxGeometry args={[z.size[0], z.size[1], z.size[2]]} />
<meshStandardMaterial color={z.color} transparent opacity={0.85} />
</mesh>
))}
{/* Grid */}
<gridHelper args={[40, 40, 0xa09880, 0xb0a890]} position={[0, 0.05, 0]} />
</group>
);
};
Create src/pixel-office/scene/BrandWall.tsx:
import React, { useMemo } from "react";
import * as THREE from "three";
interface BrandWallProps {
wallColor: string;
logoText: [string, string];
logoColors: [string, string];
}
function createLogoTexture(
logoText: [string, string],
logoColors: [string, string],
wallColor: string,
): THREE.CanvasTexture {
const canvas = document.createElement("canvas");
canvas.width = 2048;
canvas.height = 512;
const ctx = canvas.getContext("2d")!;
ctx.fillStyle = wallColor;
ctx.fillRect(0, 0, 2048, 512);
ctx.textAlign = "center";
ctx.fillStyle = logoColors[0];
ctx.font = "600 140px sans-serif";
ctx.fillText(logoText[0], 1024, 220);
ctx.fillStyle = logoColors[1];
ctx.font = "800 180px sans-serif";
ctx.fillText(logoText[1], 1024, 400);
const texture = new THREE.CanvasTexture(canvas);
texture.anisotropy = 4;
return texture;
}
export const BrandWall: React.FC<BrandWallProps> = ({ wallColor, logoText, logoColors }) => {
const texture = useMemo(
() => createLogoTexture(logoText, logoColors, wallColor),
[logoText, logoColors, wallColor],
);
const wallColorHex = useMemo(() => new THREE.Color(wallColor), [wallColor]);
return (
<group position={[0, 0, -14]}>
{/* Wall geometry */}
<mesh position={[0, 3.5, 0]} castShadow receiveShadow>
<boxGeometry args={[42, 7, 0.3]} />
<meshStandardMaterial color={wallColorHex} />
</mesh>
{/* Logo front face */}
<mesh position={[0, 3.5, 0.2]}>
<planeGeometry args={[38, 6]} />
<meshStandardMaterial
map={texture}
emissive={0xffffff}
emissiveMap={texture}
emissiveIntensity={0.7}
/>
</mesh>
{/* Logo back face */}
<mesh position={[0, 3.5, -0.2]} rotation={[0, Math.PI, 0]}>
<planeGeometry args={[38, 6]} />
<meshStandardMaterial
map={texture}
emissive={0xffffff}
emissiveMap={texture}
emissiveIntensity={0.7}
/>
</mesh>
</group>
);
};
cd ~/thalx-web-app/remotion && npx tsc --noEmit
cd ~/thalx-web-app/remotion
git add src/pixel-office/scene/Floor.tsx src/pixel-office/scene/BrandWall.tsx
git commit -m "feat(pixel-office): Floor + BrandWall R3F components"
Files:
~/thalx-web-app/remotion/src/pixel-office/scene/Furniture.tsx~/thalx-web-app/remotion/src/pixel-office/scene/Props.tsx~/thalx-web-app/remotion/src/pixel-office/scene/Whiteboard.tsxDone when:
npx tsc --noEmit passesCreate src/pixel-office/scene/Furniture.tsx. This includes: Desk, Chair, ConferenceTable, LowWall, ZoneSign, and a FurnitureLayout that places everything.
import React, { useMemo } from "react";
import * as THREE from "three";
import { TEAMS } from "../types";
import { DESK_POSITIONS } from "../presets";
const Voxel: React.FC<{
position: [number, number, number];
size: [number, number, number];
color: number | THREE.Color;
emissive?: number;
emissiveIntensity?: number;
}> = ({ position, size, color, emissive, emissiveIntensity }) => (
<mesh position={position} castShadow receiveShadow>
<boxGeometry args={size} />
<meshStandardMaterial
color={color}
roughness={0.8}
metalness={0.1}
emissive={emissive}
emissiveIntensity={emissiveIntensity ?? 0}
/>
</mesh>
);
export const Desk: React.FC<{ position: [number, number, number] }> = ({ position }) => (
<group position={position}>
<Voxel position={[0, 0.7, 0]} size={[1.4, 0.08, 0.7]} color={0x3a3a3a} />
<Voxel position={[-0.6, 0.35, -0.28]} size={[0.06, 0.7, 0.06]} color={0x2a2a2a} />
<Voxel position={[0.6, 0.35, -0.28]} size={[0.06, 0.7, 0.06]} color={0x2a2a2a} />
<Voxel position={[-0.6, 0.35, 0.28]} size={[0.06, 0.7, 0.06]} color={0x2a2a2a} />
<Voxel position={[0.6, 0.35, 0.28]} size={[0.06, 0.7, 0.06]} color={0x2a2a2a} />
<Voxel position={[0, 0.78, 0.05]} size={[0.5, 0.03, 0.35]} color={0x222222} />
<Voxel position={[0, 1.0, -0.12]} size={[0.48, 0.3, 0.02]} color={0x666666} emissive={0x666666} emissiveIntensity={0.3} />
</group>
);
export const Chair: React.FC<{ position: [number, number, number]; teamColor?: number }> = ({ position, teamColor }) => (
<group position={position}>
<Voxel position={[0, 0.45, 0]} size={[0.4, 0.06, 0.4]} color={0x2a2a2a} />
<Voxel position={[0, 0.75, -0.18]} size={[0.38, 0.5, 0.05]} color={teamColor || 0x2a2a2a} />
<Voxel position={[0, 0.22, 0]} size={[0.06, 0.44, 0.06]} color={0x1a1a1a} />
<Voxel position={[0, 0.02, 0]} size={[0.3, 0.04, 0.3]} color={0x1a1a1a} />
</group>
);
export const ConferenceTable: React.FC = () => (
<group>
<Voxel position={[0, 0.72, 0]} size={[4.5, 0.1, 2.2]} color={0x4a3828} />
<Voxel position={[-2, 0.72, 0]} size={[1.0, 0.1, 2.6]} color={0x4a3828} />
<Voxel position={[2, 0.72, 0]} size={[1.0, 0.1, 2.6]} color={0x4a3828} />
<Voxel position={[0, 0.36, 0]} size={[0.8, 0.72, 0.8]} color={0x3a2818} />
<Voxel position={[0, 0.82, 0]} size={[0.8, 0.02, 0.5]} color={0x3b82f6} emissive={0x3b82f6} emissiveIntensity={0.5} />
</group>
);
export const LowWall: React.FC<{ position: [number, number, number]; length: number }> = ({ position, length }) => (
<group position={position}>
<Voxel position={[0, 0.6, 0]} size={[0.15, 1.2, length]} color={0x908070} />
<Voxel position={[0, 1.22, 0]} size={[0.2, 0.06, length + 0.05]} color={0x999080} />
</group>
);
export const ZoneSign: React.FC<{ text: string; color: number; position: [number, number, number] }> = ({ text, color, position }) => {
const texture = useMemo(() => {
const canvas = document.createElement("canvas");
canvas.width = 512;
canvas.height = 128;
const ctx = canvas.getContext("2d")!;
ctx.fillStyle = "#" + new THREE.Color(color).getHexString();
ctx.font = "bold 48px sans-serif";
ctx.textAlign = "center";
ctx.fillText(text, 256, 80);
return new THREE.CanvasTexture(canvas);
}, [text, color]);
return (
<sprite position={position} scale={[4, 1, 1]}>
<spriteMaterial map={texture} transparent />
</sprite>
);
};
export const FurnitureLayout: React.FC = () => (
<group>
<ConferenceTable />
<LowWall position={[-5, 0, -3]} length={10} />
<LowWall position={[5, 0, -3]} length={10} />
<ZoneSign text={TEAMS.miles.name} color={TEAMS.miles.color} position={[-12, 5.5, -13]} />
<ZoneSign text={TEAMS.pmo.name} color={TEAMS.pmo.color} position={[-12, 5.5, 5]} />
<ZoneSign text={TEAMS.alexa.name} color={TEAMS.alexa.color} position={[12, 5.5, -13]} />
{/* Desks and chairs per zone */}
{Object.entries(DESK_POSITIONS).map(([team, config]) =>
config.positions.map((pos, i) => (
<React.Fragment key={`${team}-${i}`}>
<Desk position={pos} />
<Chair
position={[pos[0], pos[1], pos[2] + config.chairOffset]}
teamColor={TEAMS[team].color}
/>
</React.Fragment>
))
)}
</group>
);
Create src/pixel-office/scene/Props.tsx:
import React, { useMemo } from "react";
import * as THREE from "three";
const Voxel: React.FC<{
position: [number, number, number];
size: [number, number, number];
color: number;
emissive?: number;
emissiveIntensity?: number;
}> = ({ position, size, color, emissive, emissiveIntensity }) => (
<mesh position={position} castShadow receiveShadow>
<boxGeometry args={size} />
<meshStandardMaterial
color={color}
roughness={0.8}
metalness={0.1}
emissive={emissive}
emissiveIntensity={emissiveIntensity ?? 0}
/>
</mesh>
);
const Plant: React.FC<{ position: [number, number, number] }> = ({ position }) => (
<group position={position}>
<Voxel position={[0, 0.2, 0]} size={[0.4, 0.4, 0.4]} color={0x6b4226} />
<Voxel position={[0, 0.42, 0]} size={[0.35, 0.05, 0.35]} color={0x3a2a1a} />
<Voxel position={[0, 0.7, 0]} size={[0.5, 0.3, 0.5]} color={0x2d8a4e} />
<Voxel position={[0, 0.95, 0]} size={[0.35, 0.25, 0.35]} color={0x3aad5e} />
<Voxel position={[0, 1.12, 0]} size={[0.2, 0.15, 0.2]} color={0x4bc870} />
</group>
);
const CoffeeMachine: React.FC<{ position: [number, number, number] }> = ({ position }) => (
<group position={position}>
<Voxel position={[0, 0.5, 0]} size={[0.5, 1.0, 0.4]} color={0x444444} />
<Voxel position={[0, 1.05, 0]} size={[0.55, 0.1, 0.45]} color={0x555555} />
<Voxel position={[0.15, 0.8, 0.21]} size={[0.06, 0.06, 0.02]} color={0xff0000} emissive={0xff0000} emissiveIntensity={1.0} />
</group>
);
const ServerRack: React.FC<{ position: [number, number, number]; frame: number }> = ({ position, frame }) => {
const leds = useMemo(() =>
Array.from({ length: 6 }, (_, i) => ({
y: 0.3 + i * 0.35,
blinkOffset: Math.random() * Math.PI * 2,
})), []);
return (
<group position={position}>
<Voxel position={[0, 1.2, 0]} size={[0.7, 2.4, 0.5]} color={0x1a1a1a} />
{leds.map((led, i) => {
const blink = Math.sin(frame * 0.1 + led.blinkOffset) > 0.3;
return (
<React.Fragment key={i}>
<Voxel position={[0, led.y, 0.26]} size={[0.6, 0.25, 0.02]} color={0x222222} />
<Voxel
position={[0.25, led.y, 0.27]}
size={[0.04, 0.04, 0.02]}
color={0x00ff44}
emissive={0x00ff44}
emissiveIntensity={blink ? 0.8 : 0.1}
/>
</React.Fragment>
);
})}
</group>
);
};
const PLANT_POSITIONS: [number, number, number][] = [
[-18, 0, -12], [18, 0, -12], [-18, 0, 12], [18, 0, 12], [5, 0, 6], [-5, 0, 6],
];
export const OfficeProps: React.FC<{ frame: number }> = ({ frame }) => (
<group>
{PLANT_POSITIONS.map((pos, i) => (
<Plant key={i} position={pos} />
))}
<CoffeeMachine position={[16, 0, 8]} />
<ServerRack position={[18, 0, 6]} frame={frame} />
</group>
);
Create src/pixel-office/scene/Whiteboard.tsx:
import React from "react";
const Voxel: React.FC<{
position: [number, number, number];
size: [number, number, number];
color: number;
emissive?: number;
emissiveIntensity?: number;
}> = ({ position, size, color, emissive, emissiveIntensity }) => (
<mesh position={position} castShadow receiveShadow>
<boxGeometry args={size} />
<meshStandardMaterial
color={color}
roughness={0.8}
metalness={0.1}
emissive={emissive}
emissiveIntensity={emissiveIntensity ?? 0}
/>
</mesh>
);
export const Whiteboard: React.FC<{ position: [number, number, number] }> = ({ position }) => (
<group position={position}>
<Voxel position={[0, 1.0, 0]} size={[2.2, 1.6, 0.08]} color={0x1a1a1a} />
<Voxel position={[0, 1.02, 0.05]} size={[2.0, 1.4, 0.04]} color={0xf0f0ee} />
<Voxel position={[0, 0.26, 0.04]} size={[2.0, 0.08, 0.12]} color={0x2a2a2a} />
<Voxel position={[-0.4, 0.32, 0.08]} size={[0.08, 0.06, 0.08]} color={0x3b82f6} emissive={0x3b82f6} emissiveIntensity={0.8} />
<Voxel position={[0, 0.32, 0.08]} size={[0.08, 0.06, 0.08]} color={0x10b981} emissive={0x10b981} emissiveIntensity={0.8} />
<Voxel position={[0.4, 0.32, 0.08]} size={[0.08, 0.06, 0.08]} color={0xf59e0b} emissive={0xf59e0b} emissiveIntensity={0.8} />
</group>
);
cd ~/thalx-web-app/remotion && npx tsc --noEmit
cd ~/thalx-web-app/remotion
git add src/pixel-office/scene/Furniture.tsx src/pixel-office/scene/Props.tsx src/pixel-office/scene/Whiteboard.tsx
git commit -m "feat(pixel-office): Furniture, Props, Whiteboard components"
Files:
~/thalx-web-app/remotion/src/pixel-office/characters/VoxelCharacter.tsxDone when:
npx tsc --noEmit passesCreate src/pixel-office/characters/VoxelCharacter.tsx:
import React, { useMemo, useRef } from "react";
import * as THREE from "three";
import { useCurrentFrame, useVideoConfig } from "remotion";
import type { AgentDef } from "../types";
import { TEAMS } from "../types";
import { getAgentState, agentSeed } from "../presets";
interface VoxelCharacterProps {
agent: AgentDef;
position: [number, number, number];
rotation?: number;
}
const SKIN = 0xd4a574;
const PANTS = 0x2a2a2a;
const SHOES = 0x1a1a1a;
const HAIR_COLORS = [0x2a1a0a, 0x4a3020, 0x1a1a1a, 0x6a4030, 0x8a6040];
export const VoxelCharacter: React.FC<VoxelCharacterProps> = ({ agent, position, rotation = 0 }) => {
const frame = useCurrentFrame();
const { fps } = useVideoConfig();
const state = getAgentState(agent, frame, fps);
const seed = agentSeed(agent.id);
const scale = agent.isChief ? 1.2 : 1.0;
const s = scale;
const teamData = TEAMS[agent.team];
const shirtColor = agent.isChief
? teamData.color
: new THREE.Color(teamData.color).multiplyScalar(0.6).getHex();
const hairColor = HAIR_COLORS[seed % HAIR_COLORS.length];
// Animation values
const t = frame * 0.033 + seed * 0.7; // pseudo-time with offset per agent
let armLRotX = 0;
let armRRotX = 0;
let headRotX = 0;
let headRotY = 0;
let headRotZ = 0;
let bodyOffsetY = 0;
if (state === "idle") {
bodyOffsetY = Math.sin(t * 1.5) * 0.01;
headRotY = Math.sin(t * 0.4) * 0.15;
} else if (state === "working") {
armLRotX = Math.sin(t * 8) * 0.3 - 0.5;
armRRotX = Math.sin(t * 8 + Math.PI) * 0.3 - 0.5;
headRotX = -0.15;
} else if (state === "thinking") {
armRRotX = -0.8;
headRotZ = 0.1;
headRotX = -0.1 + Math.sin(t * 0.8) * 0.05;
}
// Label texture
const labelTexture = useMemo(() => {
const canvas = document.createElement("canvas");
canvas.width = 256;
canvas.height = 64;
const ctx = canvas.getContext("2d")!;
ctx.fillStyle = agent.isChief ? "#ffffff" : "#aaaaaa";
ctx.font = agent.isChief ? "bold 28px sans-serif" : "22px monospace";
ctx.textAlign = "center";
ctx.fillText(agent.label, 128, 40);
return new THREE.CanvasTexture(canvas);
}, [agent.label, agent.isChief]);
return (
<group position={position} rotation={[0, rotation, 0]}>
{/* Legs */}
<mesh position={[-0.15 * s, 0.35 * s, 0]} castShadow>
<boxGeometry args={[0.25 * s, 0.6 * s, 0.25 * s]} />
<meshStandardMaterial color={PANTS} roughness={0.8} />
</mesh>
<mesh position={[0.15 * s, 0.35 * s, 0]} castShadow>
<boxGeometry args={[0.25 * s, 0.6 * s, 0.25 * s]} />
<meshStandardMaterial color={PANTS} roughness={0.8} />
</mesh>
{/* Shoes */}
<mesh position={[-0.15 * s, 0.05 * s, 0.03 * s]} castShadow>
<boxGeometry args={[0.28 * s, 0.1 * s, 0.32 * s]} />
<meshStandardMaterial color={SHOES} roughness={0.9} />
</mesh>
<mesh position={[0.15 * s, 0.05 * s, 0.03 * s]} castShadow>
<boxGeometry args={[0.28 * s, 0.1 * s, 0.32 * s]} />
<meshStandardMaterial color={SHOES} roughness={0.9} />
</mesh>
{/* Body */}
<mesh position={[0, 0.95 * s + bodyOffsetY, 0]} castShadow>
<boxGeometry args={[0.55 * s, 0.7 * s, 0.3 * s]} />
<meshStandardMaterial color={shirtColor} roughness={0.8} />
</mesh>
{/* Left arm */}
<group position={[-0.4 * s, 0.95 * s, 0]} rotation={[armLRotX, 0, 0]}>
<mesh castShadow>
<boxGeometry args={[0.15 * s, 0.6 * s, 0.2 * s]} />
<meshStandardMaterial color={shirtColor} roughness={0.8} />
</mesh>
<mesh position={[0, -0.3 * s, 0]} castShadow>
<boxGeometry args={[0.13 * s, 0.15 * s, 0.15 * s]} />
<meshStandardMaterial color={SKIN} roughness={0.8} />
</mesh>
</group>
{/* Right arm */}
<group position={[0.4 * s, 0.95 * s, 0]} rotation={[armRRotX, 0, 0]}>
<mesh castShadow>
<boxGeometry args={[0.15 * s, 0.6 * s, 0.2 * s]} />
<meshStandardMaterial color={shirtColor} roughness={0.8} />
</mesh>
<mesh position={[0, -0.3 * s, 0]} castShadow>
<boxGeometry args={[0.13 * s, 0.15 * s, 0.15 * s]} />
<meshStandardMaterial color={SKIN} roughness={0.8} />
</mesh>
</group>
{/* Neck */}
<mesh position={[0, 1.35 * s, 0]} castShadow>
<boxGeometry args={[0.15 * s, 0.1 * s, 0.15 * s]} />
<meshStandardMaterial color={SKIN} roughness={0.8} />
</mesh>
{/* Head */}
<group position={[0, 1.6 * s, 0]} rotation={[headRotX, headRotY, headRotZ]}>
<mesh castShadow>
<boxGeometry args={[0.4 * s, 0.4 * s, 0.35 * s]} />
<meshStandardMaterial color={SKIN} roughness={0.8} />
</mesh>
{/* Eyes */}
<mesh position={[-0.1 * s, 0.02 * s, 0.18 * s]}>
<boxGeometry args={[0.06 * s, 0.06 * s, 0.02 * s]} />
<meshStandardMaterial color={0x222222} />
</mesh>
<mesh position={[0.1 * s, 0.02 * s, 0.18 * s]}>
<boxGeometry args={[0.06 * s, 0.06 * s, 0.02 * s]} />
<meshStandardMaterial color={0x222222} />
</mesh>
{/* Hair */}
<mesh position={[0, 0.23 * s, -0.02 * s]} castShadow>
<boxGeometry args={[0.42 * s, 0.12 * s, 0.38 * s]} />
<meshStandardMaterial color={hairColor} roughness={0.9} />
</mesh>
</group>
{/* Label */}
<sprite position={[0, 2.2 * s, 0]} scale={[2.5, 0.6, 1]}>
<spriteMaterial map={labelTexture} transparent />
</sprite>
</group>
);
};
cd ~/thalx-web-app/remotion && npx tsc --noEmit
cd ~/thalx-web-app/remotion
git add src/pixel-office/characters/VoxelCharacter.tsx
git commit -m "feat(pixel-office): VoxelCharacter with animations"
Files:
~/thalx-web-app/remotion/src/pixel-office/camera/CameraController.tsx~/thalx-web-app/remotion/src/pixel-office/camera/cameraPresets.tsDone when:
npx tsc --noEmit passesCreate src/pixel-office/camera/cameraPresets.ts:
export interface CameraFrame {
position: [number, number, number];
lookAt: [number, number, number];
fov: number;
}
type CameraPresetFn = (progress: number, speed: number, focusZone?: string) => CameraFrame;
const ZONE_CENTERS: Record<string, [number, number, number]> = {
miles: [-10, 2, -5],
pmo: [-10, 2, 8],
alexa: [11, 2, -3],
};
export const cameraPresets: Record<string, CameraPresetFn> = {
orbit360: (progress, speed) => {
const angle = progress * Math.PI * 2 * speed;
const radius = 38;
const y = 22 + Math.sin(progress * Math.PI * 2 * 0.5) * 3;
return {
position: [Math.cos(angle) * radius, y, Math.sin(angle) * radius],
lookAt: [0, 2, 0],
fov: 35,
};
},
dolly: (progress, speed) => {
const x = -30 + progress * 60 * speed;
const clampedX = Math.max(-30, Math.min(30, x));
return {
position: [clampedX, 22, 35],
lookAt: [0, 2, 0],
fov: 35,
};
},
zoneZoom: (progress, speed, focusZone = "miles") => {
const center = ZONE_CENTERS[focusZone] || ZONE_CENTERS.miles;
const t = progress * speed;
// Phase 1 (0-0.3): general view
// Phase 2 (0.3-0.7): zoom to zone
// Phase 3 (0.7-1): back to general
let position: [number, number, number];
let lookAt: [number, number, number];
if (t < 0.3) {
const p = t / 0.3;
position = [30, 25, 30];
lookAt = [0, 2, 0];
} else if (t < 0.7) {
const p = (t - 0.3) / 0.4;
const smooth = p * p * (3 - 2 * p); // smoothstep
position = [
30 + (center[0] + 8 - 30) * smooth,
25 + (12 - 25) * smooth,
30 + (center[2] + 8 - 30) * smooth,
];
lookAt = [
0 + (center[0] - 0) * smooth,
2,
0 + (center[2] - 0) * smooth,
];
} else {
const p = (t - 0.7) / 0.3;
const smooth = p * p * (3 - 2 * p);
position = [
center[0] + 8 + (30 - center[0] - 8) * smooth,
12 + (25 - 12) * smooth,
center[2] + 8 + (30 - center[2] - 8) * smooth,
];
lookAt = [
center[0] + (0 - center[0]) * smooth,
2,
center[2] + (0 - center[2]) * smooth,
];
}
return { position, lookAt, fov: 35 };
},
cenital: (progress, speed) => {
const angle = progress * Math.PI * 0.5 * speed;
return {
position: [Math.sin(angle) * 3, 45, Math.cos(angle) * 3],
lookAt: [0, 0, 0],
fov: 30,
};
},
static: () => ({
position: [30, 25, 30],
lookAt: [0, 2, 0],
fov: 35,
}),
};
Create src/pixel-office/camera/CameraController.tsx:
import React from "react";
import { useCurrentFrame, useVideoConfig } from "remotion";
import { useThree } from "@react-three/fiber";
import * as THREE from "three";
import type { CameraPreset } from "../types";
import { cameraPresets } from "./cameraPresets";
interface CameraControllerProps {
preset: CameraPreset;
speed: number;
focusZone?: string;
}
export const CameraController: React.FC<CameraControllerProps> = ({ preset, speed, focusZone }) => {
const frame = useCurrentFrame();
const { durationInFrames } = useVideoConfig();
const { camera } = useThree();
const progress = frame / durationInFrames;
const presetFn = cameraPresets[preset] || cameraPresets.static;
const { position, lookAt, fov } = presetFn(progress, speed, focusZone);
camera.position.set(...position);
camera.lookAt(new THREE.Vector3(...lookAt));
if ((camera as THREE.PerspectiveCamera).fov !== undefined) {
(camera as THREE.PerspectiveCamera).fov = fov;
(camera as THREE.PerspectiveCamera).updateProjectionMatrix();
}
return null;
};
cd ~/thalx-web-app/remotion && npx tsc --noEmit
cd ~/thalx-web-app/remotion
git add src/pixel-office/camera/
git commit -m "feat(pixel-office): CameraController + 5 camera presets"
Files:
~/thalx-web-app/remotion/src/pixel-office/lighting/OfficeLighting.tsxDone when:
ambientIntensitynpx tsc --noEmit passesCreate src/pixel-office/lighting/OfficeLighting.tsx:
import React from "react";
interface OfficeLightingProps {
ambientIntensity: number;
}
export const OfficeLighting: React.FC<OfficeLightingProps> = ({ ambientIntensity }) => (
<>
<ambientLight intensity={ambientIntensity} color={0xfff8ee} />
<directionalLight
position={[20, 30, 20]}
intensity={1.5}
color={0xfff5e0}
castShadow
shadow-mapSize-width={2048}
shadow-mapSize-height={2048}
shadow-camera-left={-30}
shadow-camera-right={30}
shadow-camera-top={30}
shadow-camera-bottom={-30}
/>
<directionalLight
position={[-15, 20, -10]}
intensity={0.8}
color={0xffe8cc}
/>
<pointLight
position={[0, 6, 0]}
intensity={1.2}
color={0xffeedd}
distance={30}
/>
</>
);
cd ~/thalx-web-app/remotion && npx tsc --noEmit
cd ~/thalx-web-app/remotion
git add src/pixel-office/lighting/OfficeLighting.tsx
git commit -m "feat(pixel-office): OfficeLighting component"
Files:
~/thalx-web-app/remotion/src/pixel-office/scene/OfficeScene.tsxDone when:
npx tsc --noEmit passesCreate src/pixel-office/scene/OfficeScene.tsx:
import React from "react";
import { useCurrentFrame } from "remotion";
import type { PixelOfficeProps, AgentDef } from "../types";
import { CHIEF_POSITIONS, SUB_POSITIONS } from "../presets";
import { Floor } from "./Floor";
import { BrandWall } from "./BrandWall";
import { FurnitureLayout } from "./Furniture";
import { OfficeProps } from "./Props";
import { Whiteboard } from "./Whiteboard";
import { VoxelCharacter } from "../characters/VoxelCharacter";
import { CameraController } from "../camera/CameraController";
import { OfficeLighting } from "../lighting/OfficeLighting";
export const OfficeScene: React.FC<PixelOfficeProps> = (props) => {
const frame = useCurrentFrame();
const { agents, scene, focusZone, wallColor, logoText, logoColors, floorColor, ambientIntensity, camera, cameraSpeed } = props;
// Separate chiefs and subagents
const chiefs = agents.filter((a) => a.isChief);
const subagents = agents.filter((a) => !a.isChief);
// Track subagent index per team
const subIndex: Record<string, number> = { miles: 0, pmo: 0, alexa: 0 };
return (
<>
<OfficeLighting ambientIntensity={ambientIntensity} />
<CameraController preset={camera} speed={cameraSpeed} focusZone={focusZone} />
<Floor floorColor={floorColor} />
<BrandWall wallColor={wallColor} logoText={logoText} logoColors={logoColors} />
<FurnitureLayout />
<OfficeProps frame={frame} />
<Whiteboard position={[-16, 0, -13.8]} />
{/* Place chiefs at conference table */}
{chiefs.map((agent) => {
const cp = CHIEF_POSITIONS[agent.team];
if (!cp) return null;
return (
<VoxelCharacter
key={agent.id}
agent={agent}
position={cp.pos}
rotation={cp.rot}
/>
);
})}
{/* Place subagents at desks */}
{subagents.map((agent) => {
const team = agent.team;
const idx = subIndex[team] ?? 0;
subIndex[team] = idx + 1;
const positions = SUB_POSITIONS[team];
if (!positions || idx >= positions.length) return null;
return (
<VoxelCharacter
key={agent.id}
agent={agent}
position={positions[idx]}
/>
);
})}
</>
);
};
cd ~/thalx-web-app/remotion && npx tsc --noEmit
cd ~/thalx-web-app/remotion
git add src/pixel-office/scene/OfficeScene.tsx
git commit -m "feat(pixel-office): OfficeScene assembles all components"
Files:
~/thalx-web-app/remotion/src/PixelOffice.tsx~/thalx-web-app/remotion/src/Root.tsxDone when:
npx tsc --noEmit passesnpx remotion render PixelOffice --frames=0 --image-format=png /tmp/pixel-office-test.png renders a single frame without errorsCreate src/PixelOffice.tsx:
import React from "react";
import { ThreeCanvas } from "@remotion/three";
import { useVideoConfig } from "remotion";
import type { PixelOfficeProps } from "./pixel-office/types";
import { OfficeScene } from "./pixel-office/scene/OfficeScene";
export const PixelOffice: React.FC<PixelOfficeProps> = (props) => {
const { width, height } = useVideoConfig();
return (
<ThreeCanvas
width={width}
height={height}
camera={{ fov: 35, near: 0.1, far: 200, position: [30, 25, 30] }}
style={{ backgroundColor: "#c8daf0" }}
shadows
>
<OfficeScene {...props} />
</ThreeCanvas>
);
};
Add imports at the top of src/Root.tsx:
import { PixelOffice } from "./PixelOffice";
import type { PixelOfficeProps } from "./pixel-office/types";
import { DEFAULT_PROPS } from "./pixel-office/presets";
Add as any cast:
const PixelOfficeComp = PixelOffice as any;
Add 3 compositions inside the <>...</> fragment, after the last existing </Composition>:
<Composition
id="PixelOffice"
component={PixelOfficeComp}
durationInFrames={600}
fps={FPS}
width={1920}
height={1080}
defaultProps={DEFAULT_PROPS satisfies PixelOfficeProps}
calculateMetadata={async ({ props }) => {
const p = props as unknown as PixelOfficeProps;
const w = p.format === "portrait" ? 1080 : p.format === "square" ? 1080 : 1920;
const h = p.format === "portrait" ? 1920 : p.format === "square" ? 1080 : 1080;
return {
durationInFrames: Math.round((p.duration || 20) * FPS),
width: w,
height: h,
};
}}
/>
cd ~/thalx-web-app/remotion && npx tsc --noEmit
cd ~/thalx-web-app/remotion
npx remotion render PixelOffice --frames=0 --image-format=png /tmp/pixel-office-test.png
Expected: renders a PNG of frame 0 without errors. Verify visually:
cp /tmp/pixel-office-test.png ~/playgrounds/pixel-office-test-frame.png
chmod 644 ~/playgrounds/pixel-office-test-frame.png
Check at: https://playgrounds.digitalhubassist.ai/pixel-office-test-frame.png
cd ~/thalx-web-app/remotion
git add src/PixelOffice.tsx src/Root.tsx
git commit -m "feat(pixel-office): PixelOffice composition + Root registration"
Files:
~/thalx-web-app/remotion/render_pixel_office.mjs~/thalx-web-app/remotion/render_pixel_office_all.mjsDone when:
render_pixel_office.mjs renders via Lambda and outputs JSONrender_pixel_office_all.mjs renders all 3 formatsCreate ~/thalx-web-app/remotion/render_pixel_office.mjs:
/**
* Render Pixel Office 3D video via AWS Lambda.
*
* Usage:
* node render_pixel_office.mjs '{"scene":"full","camera":"orbit360","duration":20,"format":"landscape"}'
*
* Outputs JSON to stdout: {"video_path":"/tmp/out.mp4","duration":20,"width":1920,"height":1080,"size_bytes":...}
*/
import {
renderMediaOnLambda,
getRenderProgress,
presignUrl,
} from "@remotion/lambda/client";
import fs from "fs";
import { execSync } from "child_process";
const FUNCTION_NAME =
process.env.REMOTION_FUNCTION_NAME ||
"remotion-render-4-0-434-mem2048mb-disk2048mb-240sec";
const SERVE_URL =
process.env.REMOTION_SERVE_URL ||
"https://remotionlambda-useast1-i9f1yk1a1a.s3.us-east-1.amazonaws.com/sites/thalx-remotion/index.html";
const REGION = process.env.AWS_REGION || "us-east-1";
const DEPLOY_DIR = "/home/clawd/playgrounds";
const MAX_RETRIES = 3;
const INITIAL_BACKOFF_MS = 5000;
async function sleep(ms) {
return new Promise((r) => setTimeout(r, ms));
}
async function renderWithRetry(params) {
for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {
try {
return await renderMediaOnLambda(params);
} catch (err) {
const isRateLimit =
err.message?.includes("Rate Exceeded") ||
err.message?.includes("TooManyRequestsException");
if (isRateLimit && attempt < MAX_RETRIES) {
const delay = INITIAL_BACKOFF_MS * attempt;
console.error(`[pixel-office] Rate limited (attempt ${attempt}), retrying in ${delay / 1000}s...`);
await sleep(delay);
continue;
}
throw err;
}
}
}
async function main() {
const arg = process.argv[2];
if (!arg) {
console.error("Usage: node render_pixel_office.mjs '<json>'");
process.exit(1);
}
const config = JSON.parse(arg);
// Resolve format → dimensions
const format = config.format || "landscape";
const width = format === "portrait" ? 1080 : format === "square" ? 1080 : 1920;
const height = format === "portrait" ? 1920 : format === "square" ? 1080 : 1080;
const outputPath = config.outputPath || `/tmp/pixel-office-${format}.mp4`;
const duration = config.duration || 20;
const thumbnail = config.thumbnail || false;
// Fetch real status if URL provided
let statusAgents = null;
if (config.statusUrl) {
try {
const resp = await fetch(config.statusUrl);
statusAgents = await resp.json();
console.error(`[pixel-office] Fetched real status from ${config.statusUrl}`);
} catch (e) {
console.error(`[pixel-office] Warning: could not fetch status: ${e.message}`);
}
}
// Build inputProps (everything except render-level params)
const { outputPath: _, statusUrl: __, thumbnail: ___, format: ____, ...inputProps } = config;
inputProps.format = format;
if (statusAgents && inputProps.agents) {
// Merge real states into agents
for (const agent of inputProps.agents) {
const real = statusAgents[agent.id];
if (real && real.state) {
agent.state = real.state;
}
}
}
console.error(`[pixel-office] Rendering "${format}" (${width}x${height}, ${duration}s)...`);
const { renderId, bucketName } = await renderWithRetry({
region: REGION,
functionName: FUNCTION_NAME,
serveUrl: SERVE_URL,
composition: "PixelOffice",
codec: "h264",
inputProps,
framesPerLambda: 20,
imageFormat: "jpeg",
maxRetries: 1,
privacy: "private",
overwrite: true,
downloadBehavior: { type: "play-in-browser" },
forceWidth: width,
forceHeight: height,
compositionDurationInFrames: Math.ceil(duration * 30),
});
console.error(`[pixel-office] Render started: ${renderId}`);
// Poll progress
let progress;
while (true) {
progress = await getRenderProgress({
renderId,
bucketName,
functionName: FUNCTION_NAME,
region: REGION,
});
if (progress.done) break;
if (progress.fatalErrorEncountered) {
console.error(`[pixel-office] FATAL: ${JSON.stringify(progress.errors).slice(0, 300)}`);
process.exit(1);
}
const pct = ((progress.overallProgress || 0) * 100).toFixed(0);
console.error(`[pixel-office] Progress: ${pct}%`);
await sleep(1500);
}
// Download
const downloadUrl = await presignUrl({
bucketName,
region: REGION,
objectKey: progress.outKey,
expiresInSeconds: 120,
checkIfObjectExists: true,
});
console.error(`[pixel-office] Downloading...`);
execSync(`curl -sL -o "${outputPath}" "${downloadUrl}"`);
// Extract thumbnail if requested
if (thumbnail) {
const thumbPath = outputPath.replace(".mp4", "-thumb.png");
const thumbFrame = Math.floor(duration * 30 * 0.3);
execSync(`ffmpeg -y -i "${outputPath}" -vf "select=eq(n\\,${thumbFrame})" -vsync vfr "${thumbPath}" 2>/dev/null`);
console.error(`[pixel-office] Thumbnail: ${thumbPath}`);
}
// Deploy to playgrounds
const deployName = `pixel-office-${format}.mp4`;
execSync(`cp "${outputPath}" "${DEPLOY_DIR}/${deployName}" && chmod 644 "${DEPLOY_DIR}/${deployName}"`);
console.error(`[pixel-office] Deployed: https://playgrounds.digitalhubassist.ai/${deployName}`);
const stat = fs.statSync(outputPath);
const result = {
video_path: outputPath,
deploy_url: `https://playgrounds.digitalhubassist.ai/${deployName}`,
duration,
width,
height,
size_bytes: stat.size,
};
console.log(JSON.stringify(result));
}
main().catch((err) => {
console.error(`[pixel-office] FATAL: ${err.message}`);
process.exit(1);
});
Create ~/thalx-web-app/remotion/render_pixel_office_all.mjs:
/**
* Render Pixel Office in all 3 formats (landscape, portrait, square).
* Usage: node render_pixel_office_all.mjs '{"scene":"full","camera":"orbit360","duration":15}'
*/
import { execSync } from "child_process";
const arg = process.argv[2];
if (!arg) {
console.error("Usage: node render_pixel_office_all.mjs '<json>'");
process.exit(1);
}
const base = JSON.parse(arg);
const formats = ["landscape", "portrait", "square"];
for (const format of formats) {
const props = { ...base, format };
console.error(`\n=== Rendering ${format} ===`);
try {
const result = execSync(
`node render_pixel_office.mjs '${JSON.stringify(props)}'`,
{ cwd: import.meta.dirname, encoding: "utf-8", stdio: ["pipe", "pipe", "inherit"] }
);
console.log(result.trim());
} catch (err) {
console.error(`[all] Failed ${format}: ${err.message}`);
}
}
console.error("\n=== All formats complete ===");
cd ~/thalx-web-app/remotion
npx remotion lambda sites create src/index.ts --site-name=thalx-remotion
Expected: outputs new site URL. Note the URL and update SERVE_URL in render_pixel_office.mjs if it changed.
cd ~/thalx-web-app/remotion
node render_pixel_office.mjs '{"scene":"full","camera":"static","duration":3,"format":"landscape","thumbnail":true}'
Expected: JSON output with video_path and deploy_url. Video at https://playgrounds.digitalhubassist.ai/pixel-office-landscape.mp4.
curl -s -o /dev/null -w "%{http_code}" https://playgrounds.digitalhubassist.ai/pixel-office-landscape.mp4
Expected: 200
cd ~/thalx-web-app/remotion
git add render_pixel_office.mjs render_pixel_office_all.mjs
git commit -m "feat(pixel-office): render scripts + Lambda deploy pipeline"
After all tasks are complete:
1. Run full 20s orbit render: node render_pixel_office.mjs '{"scene":"full","camera":"orbit360","duration":20,"format":"landscape"}'
2. Deploy video as the main pixel-office-cinema-loop: cp /tmp/pixel-office-landscape.mp4 ~/playgrounds/pixel-office-cinema-loop.mp4
3. Update Lambda site bundle if any fixes were needed