Pixel Office Remotion Composition — Implementation Plan

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:

WaveTasksDepende deParallelizable
01, 2Sí (deps + types/presets)
13, 4, 5Wave 0Sí (componentes 3D independientes)
26, 7Wave 0Sí (camera + lighting independientes)
38Wave 1, 2No (ensambla todo)
49, 10Wave 3No (composición + render + deploy)

Task 1: Install dependencies and create types _(Wave 0)_

Files:

Done when:

cd ~/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"

Task 2: Default agents and presets _(Wave 0)_

Files:

Done when:

Create 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"

Task 3: Floor and BrandWall components _(Wave 1)_

Files:

Done when:

Create 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"

Task 4: Furniture, Props, and Whiteboard _(Wave 1)_

Files:

Done when:

Create 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"

Task 5: VoxelCharacter and animations _(Wave 1)_

Files:

Done when:

Create 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"

Task 6: Camera controller and presets _(Wave 2)_

Files:

Done when:

Create 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"

Task 7: Office lighting _(Wave 2)_

Files:

Done when:

Create 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"

Task 8: OfficeScene assembly _(Wave 3)_

Files:

Done when:

Create 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"

Task 9: PixelOffice composition + Root registration _(Wave 4)_

Files:

Done when:

Create 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"

Task 10: Render scripts + Lambda deploy _(Wave 4)_

Files:

Done when:

Create ~/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"

Post-Implementation

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