# Project export: Mirage

This document was generated by HackStack to give an AI agent context about a hackathon project. Sections are labeled with their provenance; content marked as truncated was cut to keep this document small.

## Project metadata

- Hackathon: TreeHacks 2026
- Tagline: Robots and self-driving cars need training data. Mirage synthesizes 3D world-model training data covering hard-to-capture scenes - like driving in a hailstorm.
- Devpost: https://devpost.com/software/synsplatt
- GitHub: https://github.com/kyan-yang/treehacks-2026
- Demo: https://treehacks-2026-nine.vercel.app/
- Result: winner ([Modal] Inference Track (Inference Track Grand Prize: $5K in Modal Credits per person, paid visit (flight & hotel) to SF or NY Office with lunch with Modal team,. Inference Track Runner Up Prize: $1K in Modal credits, airpods for each team member.))
- Team: 5 GitHub contributor(s) — Shrey Birmiwal (53 commits), Claude Opus 4.6 (39 commits), adiprasadd (14 commits), Kevin Thomas (10 commits), Kyan Yang (9 commits)

## Devpost submission (written by the team)

### Overview

Motivation The future of AI-like robots and self-driving cars requires lots and lots of data. Current datasets are extensive, but they cover common senarios, like driving in a sunny or cold day. They overrepresent positive cases and underrepresent edge cases and failure cases, where things could actually go wrong. This leads us to believe that simulations of world models will be key to simulating edge cases, like driving when a tree hits the ground, to train more robust and safe models. Conviction is furthered by Waymo beginning similar research, seen here What did we do? Users will create prompts, such as "Generate a road that had a tree break and fall down on", a unique scenario that would be unlikely to be present in current datasets, but very valid and important to train a model on. An LLM hosted on Google Cloud will expand the user prompt Veo3 model hosted on Google Cloud will generate a video of the simulated road Modal hosting multiple h100 GPUs will run an open source world model / gaussian splatting algorithm to create a 3d representation of this world Future work (half completed at hackathon) Physics + simulations in 3D generated space Segmentation of objects in 3d representation space (enabling the moving of objects in the space, identification, and training)

## README (from the GitHub repository)

# Mirage

Won 1st place at TreeHacks Modal prize 2026!

Robots and self-driving cars need training data. SynSplatt synthesize 3D world-model training data covering hard-to-capture scenes - like driving in a hailstorm.

 - https://github.com/kyan-yang/Mirage
 - Try it at vercel

Created at TreeHacks 2026 by Shrey Birmiwal, Kyan Yang, Kevin Thomas, Adi Prasad

## Motivation

 - The future of AI-like robots and self-driving cars requires lots and lots of data.
 - Current datasets are extensive, but they cover common senarios, like driving in a sunny or cold day.
 - They **overrepresent positive cases and underrepresent edge cases** and failure cases, where things could actually go wrong.
 - This leads us to believe that simulations of world models will be key to simulating edge cases, like driving when a tree hits the ground, to train more robust and safe models.
 - Conviction is furthered by Waymo beginning similar research, [seen here](https://x.com/Waymo/status/2019804616746029508?s=20)

## What did we do?

 - Users will create prompts, such as **"Generate a road that had a tree break and fall down on"**, a unique scenario that would be unlikely to be present in current datasets, but very valid and important to train a model on.
 - An LLM hosted on Google Cloud will expand the user prompt
 - Veo3 model hosted on Google Cloud will generate a video of the simulated road
 - Modal hosting multiple h100 GPUs will run an open source world model / gaussian splatting algorithm to create a 3d representation of this world

## Future work (half completed at hackathon)

 - Physics + simulations in 3D generated space
 - Segmentation of objects in 3d representation space (enabling the moving of objects in the space, identification, and training)


## Detected evidence (automated analysis)

Indexed codebase: 37 recognized source files, 275 KB.
- CSS (language) — detected in the code
- HTML (language) — detected in the code
- JavaScript (language) — detected in the code
- Python (language) — detected in the code
- React (technology) — detected in the code
- TypeScript (language) — detected in the code
- AI coding agent: Claude Code — evidence: commit authorship or trailers
- AI coding agent: Codex — evidence: config files committed to the repository

## Codebase structure (from repository index)

### Files (102 of 102)

```
.DS_Store
.gitignore
AGENTS.md
archive/flashworld.py
archive/modal_hunyuanworld.py
archive/test-modal.py
google-maps-data/fetch_streetview.py
google-maps-data/scenes/manual-tree-hacks/IMG_5881.HEIC
google-maps-data/scenes/manual-tree-hacks/IMG_5882.HEIC
google-maps-data/scenes/manual-tree-hacks/IMG_5883.HEIC
google-maps-data/scenes/manual-tree-hacks/IMG_5884.HEIC
google-maps-data/scenes/manual-tree-hacks/IMG_5885.HEIC
google-maps-data/scenes/manual-tree-hacks/IMG_5886.HEIC
google-maps-data/scenes/manual-tree-hacks/IMG_5887.HEIC
google-maps-data/scenes/manual-tree-hacks/IMG_5888.HEIC
google-maps-data/scenes/manual-tree-hacks/IMG_5889.HEIC
google-maps-data/scenes/ouroom/IMG_5891.HEIC
google-maps-data/scenes/ouroom/IMG_5892.HEIC
google-maps-data/scenes/ouroom/IMG_5893.HEIC
google-maps-data/scenes/ouroom/IMG_5894.HEIC
google-maps-data/scenes/ouroom/IMG_5895.HEIC
google-maps-data/scenes/ouroom/IMG_5896.HEIC
google-maps-data/scenes/ouroom/IMG_5897.HEIC
google-maps-data/scenes/ouroom/IMG_5898.HEIC
google-maps-data/scenes/ouroom/IMG_5899.HEIC
google-maps-data/scenes/ouroom/IMG_5900.HEIC
google-maps-data/scenes/ouroom/IMG_5901.HEIC
google-maps-data/scenes/ouroom/IMG_5902.HEIC
google-maps-data/scenes/ouroom/IMG_5903.HEIC
google-maps-data/scenes/ouroom/IMG_5904.HEIC
google-maps-data/scenes/ouroom/IMG_5905.HEIC
google-maps-data/scenes/ouroom/IMG_5906.HEIC
google-maps-data/scenes/ouroom/IMG_5907.HEIC
google-maps-data/scenes/ouroom/IMG_5908.HEIC
google-maps-data/scenes/ouroom/IMG_5909.HEIC
google-maps-data/scenes/ouroom/IMG_5910.HEIC
google-maps-data/scenes/ouroom/IMG_5911.HEIC
google-maps-data/scenes/ouroom/IMG_5912.HEIC
google-maps-data/scenes/ouroom/IMG_5913.HEIC
google-maps-data/scenes/ouroom/IMG_5914.HEIC
google-maps-data/scenes/ouroom/IMG_5915.HEIC
google-maps-data/scenes/ouroom/IMG_5916.HEIC
google-maps-data/scenes/ouroom/IMG_5917.HEIC
google-maps-data/scenes/ouroom/IMG_5918.HEIC
google-maps-data/scenes/ouroom/IMG_5919.HEIC
google-maps-data/scenes/ouroom/IMG_5920.HEIC
google-maps-data/scenes/ouroom/IMG_5921.HEIC
google-maps-data/scenes/ouroom/IMG_5922.HEIC
google-maps-data/scenes/robot/IMG_0455.heic
google-maps-data/scenes/robot/IMG_0458.heic
google-maps-data/scenes/robot/IMG_0459 copy.heic
google-maps-data/scenes/robot/IMG_0459.heic
google-maps-data/scenes/robot/IMG_0460.heic
google-maps-data/scenes/robot/IMG_0461.heic
google-maps-data/scenes/robot/IMG_0462.heic
google-maps-data/scenes/robot/IMG_0463.heic
google-maps-data/scenes/robot/IMG_0464.heic
google-maps-data/scenes/robot/IMG_0465.heic
google-maps-data/scenes/stanford_main_quad/camera_meta.json
LICENSE
modal/world-mirror.py
oldREADME.md
README.md
scripts/world_mirror_refine.sh
v2/browse.py
v2/fetch_streetview.py
v2/modal_app.py
v3/.DS_Store
v3/.gitignore
v3/frontend/.DS_Store
v3/frontend/.gitignore
v3/frontend/index.html
v3/frontend/package.json
v3/frontend/public/.DS_Store
v3/frontend/scripts/upload-models.mjs
v3/frontend/src/App.css
v3/frontend/src/App.tsx
v3/frontend/src/components/DebugPanel.tsx
v3/frontend/src/components/Gallery.tsx
v3/frontend/src/components/GalleryModal.tsx
v3/frontend/src/components/GenerateButton.tsx
v3/frontend/src/components/HomePage.tsx
v3/frontend/src/components/ImageUpload.tsx
v3/frontend/src/components/ProgressSteps.tsx
v3/frontend/src/components/PromptInput.tsx
v3/frontend/src/components/ResultViewer.tsx
v3/frontend/src/components/SplatViewer.tsx
v3/frontend/src/components/TabSelector.tsx
v3/frontend/src/components/VideoPreview.tsx
v3/frontend/src/galleryStore.ts
v3/frontend/src/main.tsx
v3/frontend/src/models.ts
v3/frontend/src/vite-env.d.ts
v3/frontend/tsconfig.json
v3/frontend/tsconfig.tsbuildinfo
v3/frontend/vite.config.ts
v3/modal_app.py
v3/readme.md
v3/scripts/batch-generate-previews.js
v3/scripts/generate-preview.js
v3/scripts/package.json
v3/scripts/README.md
```

### Dependencies

- v3/frontend/package.json: @sparkjsdev/spark@^0.1.10, @types/react@^19.0.0, @types/react-dom@^19.0.0, @types/three@^0.182.0, @vercel/blob@^2.2.0, @vitejs/plugin-react@^4.3.4, react@^19.0.0, react-dom@^19.0.0, three@^0.182.0, typescript@~5.7.0, vite@^6.0.0
- v3/scripts/package.json: puppeteer@^22.0.0

### Recent commits (newest first)

- Update README.md
- Update GitHub link in README
- fix: portal gallery modal to body and fix home page z-index/pointer-events
- Update README.md
- Merge pull request #4 from kyan-yang/kyan/cool-bg
- cool bg
- Merge remote-tracking branch 'origin/main' into kyan/cool-bg
- style: rename syn_splatt to Mirage across frontend
- Merge remote-tracking branch 'origin/main' into kyan/cool-bg
- feat: add Mess model to gallery with preview image
- fix tab green underline
- Strongly emphasize scene must be completely static
- Improve video generation prompts for better camera movement
- feat: serve PLY models from Vercel Blob storage
- Update default prompts to relevant driving and robot scenarios
- updated readme
- readme
- edits
- frontend updates
- Restructure UI into separate Create and Gallery tabs

## Key source files (fetched from GitHub, selected and truncated for size)

### oldREADME.md

```markdown
# HunyuanWorld on Modal

## Model choice for agents/segmentation

If you need a persistent 3D world representation (mesh/assets you can inspect, segment, and attach agents to), start with **HunyuanWorld-1.0**.

Use **HunyuanWorld-1.5 (WorldPlay)** when you primarily want interactive world-model video generation. It is stronger for real-time playable dynamics, but its default outputs are not as directly agent-friendly as an explicit 3D scene asset pipeline.

## Files in this repo

- `/Users/shreybirmiwal/treehacks-2026/modal_hunyuanworld.py`: Modal app for generating worlds with HunyuanWorld-1.0 and serving artifacts.

## Prereqs

1. Install Modal locally:

```bash
pip install modal
python3 -m modal setup
```

2. Create a Modal secret named `huggingface-token`:

```bash
modal secret create huggingface-token HUGGINGFACE_TOKEN=hf_xxx
```

## Environment variables (.env)

`.env` files are **not committed** (they are in `.gitignore`). You need to create them locally where required.

**v3 frontend** (`v3/frontend/`):

- Create `v3/frontend/.env.development` and/or `v3/frontend/.env.production` with:

  ```bash
  VITE_API_URL=https://your-modal-app--viewer.modal.run
  ```

  Replace the URL with your deployed Modal viewer endpoint. The frontend uses this to talk to the backend API.

**Other env usage:** Backend/Modal code reads API keys from the environment (e.g. `GEMINI_API_KEY`, `HUGGINGFACE_TOKEN`). Use Modal secrets or your shell env; no `.env` in the repo.

## Run a generation job

Text-to-world:

```bash
modal run /Users/shreybirmiwal/treehacks-2026/modal_hunyuanworld.py::main --prompt "an alpine valley at sunrise" --classes outdoor
```

Image-to-world:

```bash
modal run /Users/shreybirmiwal/treehacks-2026/modal_hunyuanworld.py::main --image-path /absolute/path/to/input.png --classes outdoor --labels-fg1 rocks,trees --labels-fg2 mountains,clouds
```

## Deploy artifact viewer

```bash
modal deploy /Users/shreybirmiwal/treehacks-2026/modal_hunyuanworld.py
```

The app exposes:

- `GET /` list run IDs
- `GET /runs/{run_id}` list files for a run
- `GET /runs/{run_id}/file?path=modelviewer.html` open viewer
- `GET /runs/{run_id}/file?path=<artifact-path>` download any artifact

## Notes

- This setup uses `A100-80GB` for headroom. You can downgrade later if memory allows.
- First build can take a while due to model dependencies.
- If you need a world-state API for autonomous agents, the next step is adding a post-process stage that normalizes generated artifacts into a scene graph (entities, transforms, semantic tags).

```

### AGENTS.md

```markdown
## Notes
Dont view the contents of `archive/`. That code is old and outdated versions.

## Code Organization Principles
- Prefer fewer, semantically distinct top-level folders (target ~5–12)
- Enforce consistent conventions (same type of code always in the same place)
- Minimize duplication and ambiguity (one utils/, one api/ per domain)
- Keep each unit self-contained
- **IMPORTANT**: Prefer logical, clean, modular, robust designs. Avoid overengineered flows.

## Abstraction Judgment
- **Measure abstraction by what it costs to not have it, not by line count.** A 3-line wrapper that two apps import is justified if the alternative is duplicated `useEffect` + `useState` blocks in both. "Remove it for simplicity" is wrong if removing it moves complexity into every consumer.
- **An abstraction is premature when it encodes an assumption you haven't validated.** A hook that dictates data access patterns before the UI exists is risky — not because it's code, but because it might be the wrong API. Thin wrappers over storage reads are low-risk; opinionated data-shaping hooks are higher-risk.
- **The trigger for adding abstraction is multiple consumers with diverging needs, not future speculation.** Two apps (popup, panel) reading different slices of the same storage already justifies per-slice access. "We might need this later" does not.
- **Never delete an abstraction only to reintroduce it one phase later.** If the roadmap already contains the feature that requires the abstraction (e.g., live pricing creates independent refresh lifecycles), keep the abstraction. Churn is not simplicity.

## Workflow
- **IMPORTANT**: Use parallel subagents to do your work for you. Avoid polluting your own context
- **IMPORTANT**: NEVER GUESS API ENDPOINTS. Check docs and github repos first.

## Writing Style for Plans & Docs
- **No code blobs in plans.** Communicate ideas with word specificity, not code.
- Describe what each function/module receives, does, and returns in prose.
- Name concrete files, functions, and types for consistency, but do not write their implementations.
- Diagrams (architecture, file trees) are fine — they are visual aids, not code.

# folder structure:
- google maps data: has scripts to take a google maps location and extract 360 panoramic pictures from it with degrees / etc
- /modal: world-mirror.py generates a world model ply file but has messy code
- /v2: same thing as /modal but cleaner code and has a dashboard to see all results easier
- /v3: has a new pipeling: you type ina prompt, "tree falled on road", gemini creates a video, then it goes through world model to create a world model then displays it on a splat viewer


How to run v2 on a set of images or video?
```bash
modal run v2/modal_app.py --input-dir <PATH TO IMAGE> 
```

How to run v3 on a set of images or video?
```bash
modal run v3/modal_app.py --input-dir <PATH TO IMAGES>
modal run v3/modal_app.py --video-path <PATH TO VIDEO>
```
```

### v3/scripts/package.json

```
{
  "name": "preview-generator",
  "version": "1.0.0",
  "description": "Generate preview videos from PLY files",
  "main": "generate-preview.js",
  "scripts": {
    "preview": "node generate-preview.js",
    "batch": "node batch-generate-previews.js"
  },
  "dependencies": {
    "puppeteer": "^22.0.0"
  },
  "engines": {
    "node": ">=16"
  }
}

```

### v3/frontend/package.json

```
{
  "name": "scenario-gen-frontend",
  "private": true,
  "version": "0.0.1",
  "type": "module",
  "scripts": {
    "dev": "vite",
    "build": "tsc -b && vite build",
    "preview": "vite preview"
  },
  "dependencies": {
    "@sparkjsdev/spark": "^0.1.10",
    "@types/three": "^0.182.0",
    "react": "^19.0.0",
    "react-dom": "^19.0.0",
    "three": "^0.182.0"
  },
  "devDependencies": {
    "@types/react": "^19.0.0",
    "@types/react-dom": "^19.0.0",
    "@vercel/blob": "^2.2.0",
    "@vitejs/plugin-react": "^4.3.4",
    "typescript": "~5.7.0",
    "vite": "^6.0.0"
  }
}

```

### v3/frontend/src/main.tsx

```typescript
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import App from "./App";
import "./App.css";

createRoot(document.getElementById("root")!).render(
  <StrictMode>
    <App />
  </StrictMode>
);

```

### v3/frontend/src/App.tsx

```typescript
import { useState, useCallback, useRef, useEffect } from "react";
import TabSelector from "./components/TabSelector";
import PromptInput from "./components/PromptInput";
import GenerateButton from "./components/GenerateButton";
import ProgressSteps from "./components/ProgressSteps";
import type { StepLabel } from "./components/ProgressSteps";
import VideoPreview from "./components/VideoPreview";
import DebugPanel from "./components/DebugPanel";
import ResultViewer from "./components/ResultViewer";
import HomePage from "./components/HomePage";

const API_URL = import.meta.env.VITE_API_URL || "";
const CACHE_KEY = "scenario-gen-cache";

export type Category = "autonomous" | "humanoid";
export type StepState = "pending" | "active" | "done" | "error";

const TEXT_STEP_LABELS: StepLabel[] = [
  { key: "expand", label: "Expanding prompt with Gemini" },
  { key: "video", label: "Generating video with Veo 3.1" },
  { key: "world", label: "Building 3D world with HunyuanWorld-Mirror" },
];

const UPLOAD_STEP_LABELS: StepLabel[] = [
  { key: "upload", label: "Processing uploaded files" },
  { key: "world", label: "Building 3D world with HunyuanWorld-Mirror" },
];

export interface StepInfo {
  state: StepState;
  detail: string;
}

export interface DebugData {
  originalPrompt: string;
  expandedPrompt: string;
  videoUrl: string;
  files: string[];
  runId: string;
}

export interface ResultData {
  runId: string;
  gaussiansPly: string;
}

interface CachedState {
  steps: Record<string, StepInfo>;
  showProgress: boolean;
  debugData: DebugData;
  hasDebugData: boolean;
  result: ResultData | null;
  videoCollapsed: boolean;
}

function loadCache(): CachedState | null {
  try {
    const raw = localStorage.getItem(CACHE_KEY);
    if (!raw) return null;
    return JSON.parse(raw) as CachedState;
  } catch {
    return null;
  }
}

function saveCache(state: CachedState) {
  try {
    localStorage.setItem(CACHE_KEY, JSON.stringify(state));
  } catch {
    // localStorage full or unavailable — ignore
  }
}

export default function App() {
  const cached = useRef(loadCache());

  const [view, setView] = useState<"home" | "generate">("home");
  const [category, setCategory] = useState<Category>("autonomous");
  const [prompt, setPrompt] = useState("");
  const [uploadFiles, setUploadFiles] = useState<File[]>([]);
  const [debugMode, setDebugMode] = useState(false);
  const [generating, setGenerating] = useState(false);

  const [steps, setSteps] = useState<Record<string, StepInfo>>(
    cached.current?.steps ?? {
      expand: { state: "pending", detail: "" },
      video: { state: "pending", detail: "" },
      world: { state: "pending", detail: "" },
    }
  );
  const [showProgress, setShowProgress] = useState(cached.current?.showProgress ?? false);

  const [debugData, setDebugData] = useState<DebugData>(
    cached.current?.debugData ?? {
      originalPrompt: "",
      expandedPrompt: "",
      videoUrl: "",
      files: [],
      runId: "",
    }
  );
  const [hasDebugData, setHasDebugData] = useState(cached.current?.hasDebugData ?? false);

  const [result, setResult] = useState<ResultData | null>(cached.current?.result ?? null);
  const [streamError, setStreamError] = useState<string | null>(null);
  const [videoCollapsed, setVideoCollapsed] = useState(cached.current?.videoCollapsed ?? false);

  // Abort controller for cancelling in-flight generation
  const abortRef = useRef<AbortController | null>(null);

  // Persist key state to localStorage whenever it changes
  useEffect(() => {
    saveCache({ steps, showProgress, debugData, hasDebugData, result, videoCollapsed });
  }, [steps, showProgress, debugData, hasDebugData, result, videoCollapsed]);

  const setStepState = useCallback(
    (step: string, state: StepState, detail?: string) => {
      setSteps((prev) => ({
        ...prev,
        [step]: { state, detail: detail ?? prev[step].detail },
      }));
    },
    []
  );

  const generate = useCallback(async () => {
    // Smart routing: if files exist, use upload route; otherwise use text route
    const hasFiles = uploadFiles.length > 0;
    if (!hasFiles && !prompt.trim()) return;
    if (generating) return;

    // Switch to generate view
    setView("generate");

    // Cancel any previous request
    abortRef.current?.abort();
    const controller = new AbortController();
    abortRef.current = controller;

    setGenerating(true);
    setShowProgress(true);
    setResult(null);
    setStreamError(null);
    setHasDebugData(false);
    setVideoCollapsed(false);

    // Clear previous debug data (including video URL) so the old video doesn't linger
    const freshDebug: DebugData = {
      originalPrompt: "",
      expandedPrompt: "",
      videoUrl: "",
      files: [],
      runId: "",
    };

    if (hasFiles) {
      // Image-to-3D route (ignore prompt)
      setSteps({
        upload: { state: "active", detail: "" },
        world: { state: "pending", detail: "" },
      });
      setDebugData(freshDebug);
    } else {
      // Text-to-3D route
      setSteps({
        expand: { state: "pending", detail: "" },
        video: { state: "pending", detail: "" },
        world: { state: "pending", detail: "" },
      });
      setDebugData({ ...freshDebug, originalPrompt: prompt });
    }

    try {
      let resp: Response;

      if (hasFiles) {
        const formData = new FormData();
        uploadFiles.forEach((f) => formData.append("files", f));
        resp = await fetch(`${API_URL}/upload`, {
          method: "POST",
          body: formData,
          signal: controller.signal,
        });
      } else {
        resp = await fetch(`${API_URL}/generate`, {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify({ prompt, category }),
          signal: controller.signal,
        });
      }

      if (!resp.ok) {
        throw new Error(
          `Server error: ${resp.status} ${resp.statusText}`
        );
     
[truncated — 8162 more characters]
```

### archive/test-modal.py

```python
import modal

app = modal.App("example-get-started")


@app.function()
def square(x):
    print("This code is running on a remote worker!")
    return x**2


@app.local_entrypoint()
def main():
    print("the square is", square.remote(42))

```

### scripts/world_mirror_refine.sh

```shell
#!/usr/bin/env bash
set -euo pipefail

if [[ $# -lt 1 ]]; then
  echo "Usage: $0 <run_id> [extra modal args]" >&2
  echo "Usage: $0 --run-id <run_id> [extra modal args]" >&2
  echo "Example: $0 abc123 --output-subdir refined_v2 --data-factor 1" >&2
  echo "Example: $0 --run-id abc123 --output-subdir refined_v2 --data-factor 1" >&2
  exit 1
fi

RUN_ID=""
if [[ "${1:-}" == "--run-id" ]]; then
  if [[ $# -lt 2 ]]; then
    echo "Error: --run-id requires a value." >&2
    exit 1
  fi
  RUN_ID="$2"
  shift 2
elif [[ "${1:-}" == --run-id=* ]]; then
  RUN_ID="${1#--run-id=}"
  shift
else
  RUN_ID="$1"
  shift || true
fi

if [[ -z "$RUN_ID" || "$RUN_ID" == -* ]]; then
  echo "Error: missing run_id. Pass '<run_id>' or '--run-id <run_id>'." >&2
  exit 1
fi

REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
SCRIPT_PATH="$REPO_ROOT/modal/world-mirror.py"

python3 -m modal run "$SCRIPT_PATH"::refine --run-id "$RUN_ID" "$@"

```

### v2/browse.py

```python
"""
Debug browser — generates a local HTML page showing all files from a run.
Shows input images, depth maps, normals, videos, PLY downloads, etc.

Usage:
    # Called automatically after generation, or standalone:
    python v2/browse.py --run-id abc123 --viewer-url https://treehacks-26--world-mirror-v2-viewer.modal.run
"""

import json
import os
import tempfile
import webbrowser
from pathlib import Path
from urllib.parse import quote


def _file_url(base: str, run_id: str, path: str) -> str:
    return f"{base}/runs/{run_id}/file?path={quote(path, safe='')}"


def _categorize_files(files: list[str]) -> dict[str, list[str]]:
    categories = {
        "input_images": [],
        "generated_images": [],
        "depth_maps": [],
        "normal_maps": [],
        "videos": [],
        "splats": [],
        "colmap": [],
        "other": [],
    }
    for f in files:
        fl = f.lower()
        if fl.endswith((".jpg", ".jpeg", ".png", ".webp")):
            if "/depth/" in fl:
                categories["depth_maps"].append(f)
            elif "/normal/" in fl:
                categories["normal_maps"].append(f)
            elif "/images/" in fl or "/images_resized/" in fl:
                categories["generated_images"].append(f)
            elif f.startswith("inputs/") and "/" not in f[len("inputs/"):]:
                categories["input_images"].append(f)
            else:
                categories["other"].append(f)
        elif fl.endswith((".mp4", ".avi", ".mov", ".webm")):
            categories["videos"].append(f)
        elif fl.endswith((".ply", ".splat", ".ksplat")):
            categories["splats"].append(f)
        elif "/sparse/" in fl:
            categories["colmap"].append(f)
        elif fl.endswith(".npy"):
            pass  # skip binary numpy files
        else:
            categories["other"].append(f)
    return categories


def generate_browse_html(run_id: str, base_url: str, files: list[str]) -> str:
    cats = _categorize_files(files)

    def img_grid(file_list: list[str], max_show: int = 50) -> str:
        if not file_list:
            return "<p style='color:#666'>None</p>"
        html = '<div class="grid">'
        for f in file_list[:max_show]:
            url = _file_url(base_url, run_id, f)
            label = f.split("/")[-1]
            html += f'''<div class="card">
                <a href="{url}" target="_blank"><img src="{url}" loading="lazy" /></a>
                <div class="label">{label}</div>
            </div>'''
        if len(file_list) > max_show:
            html += f'<p style="color:#888">...and {len(file_list) - max_show} more</p>'
        html += "</div>"
        return html

    def file_list_html(file_list: list[str]) -> str:
        if not file_list:
            return "<p style='color:#666'>None</p>"
        html = "<ul>"
        for f in file_list:
            url = _file_url(base_url, run_id, f)
            size_hint = ""
            if f.endswith(".ply"):
                size_hint = " (PLY)"
            html += f'<li><a href="{url}" target="_blank">{f}</a>{size_hint}</li>'
        html += "</ul>"
        return html

    def video_embeds(file_list: list[str]) -> str:
        if not file_list:
            return "<p style='color:#666'>None</p>"
        html = '<div class="video-grid">'
        for f in file_list:
            url = _file_url(base_url, run_id, f)
            label = f.split("/")[-1]
            html += f'''<div class="video-card">
                <video controls preload="metadata" src="{url}"></video>
                <div class="label">{label}</div>
            </div>'''
        html += "</div>"
        return html

    viewer_url = f"{base_url}/runs/{run_id}/splat-viewer"
    all_files_url = f"{base_url}/runs/{run_id}"

    return f"""<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<title>Debug: {run_id}</title>
<style>
  * {{ margin:0; padding:0; box-sizing:border-box; }}
  body {{ background:#111; color:#eee; font-family:-apple-system,system-ui,sans-serif; padding:20px; max-width:1400px; margin:0 auto; }}
  h1 {{ color:#4a9eff; margin-bottom:8px; }}
  h2 {{ color:#ccc; margin:24px 0 12px; border-bottom:1px solid #333; padding-bottom:6px; }}
  a {{ color:#5db3ff; text-decoration:none; }}
  a:hover {{ text-decoration:underline; }}
  .meta {{ color:#888; margin-bottom:16px; }}
  .links {{ display:flex; gap:16px; margin:12px 0; flex-wrap:wrap; }}
  .links a {{ background:#222; border:1px solid #444; padding:8px 16px; border-radius:6px; }}
  .links a:hover {{ background:#333; }}
  .grid {{ display:grid; grid-template-columns:repeat(auto-fill, minmax(160px, 1fr)); gap:8px; }}
  .card {{ background:#1a1a1a; border:1px solid #333; border-radius:6px; overflow:hidden; }}
  .card img {{ width:100%; height:140px; object-fit:cover; display:block; }}
  .card .label {{ padding:4px 6px; font-size:11px; color:#888; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }}
  .video-grid {{ display:grid; grid-template-columns:repeat(auto-fill, minmax(320px, 1fr)); gap:12px; }}
  .video-card {{ background:#1a1a1a; border:1px solid #333; border-radius:6px; overflow:hidden; }}
  .video-card video {{ width:100%; display:block; }}
  .video-card .label {{ padding:4px 8px; font-size:12px; color:#888; }}
  ul {{ list-style:none; }}
  ul li {{ padding:4px 0; }}
  ul li a {{ font-family:monospace; font-size:13px; }}
  .stats {{ display:grid; grid-template-columns:repeat(auto-fit, minmax(150px, 1fr)); gap:8px; margin:12px 0; }}
  .stat {{ background:#1a1a1a; border:1px solid #333; border-radius:6px; padding:12px; text-align:center; }}
  .stat .num {{ font-size:24px; color:#4a9eff; font-weight:bold; }}
  .stat .lbl {{ font-size:12px; color:#888; margin-top:4px; }}
</style>
</head>
<body>
<h1>Run: {run_id}</h1>
<div class="meta">{len(files)} total files</div>

<div class="links">
  <a href="{viewer_url}" target="_blank">Open Splat Viewer</a>
  <a href="{viewer_url}?full=true" target="_blank">Full Quality Viewer</a>
  <a 
[truncated — 2207 more characters]
```

### archive/flashworld.py

```python
from __future__ import annotations

import json
import os
import subprocess
import uuid
import base64
import io
from pathlib import Path
from typing import Any

import modal
from fastapi import FastAPI, HTTPException
from fastapi.responses import FileResponse, JSONResponse

APP_NAME = "flashworld-modal"
REPO_URL = "https://github.com/imlixinyang/FlashWorld.git"
REPO_DIR = Path("/opt/FlashWorld")
RUNS_DIR = Path("/data/runs")

app = modal.App(APP_NAME)
flash_volume = modal.Volume.from_name("flashworld-artifacts", create_if_missing=True)

# FlashWorld needs CUDA devel image for compiling gsplat and spz from source.
# Every dependency is pinned exactly as the upstream repo requires.
image = (
    modal.Image.from_registry(
        "nvidia/cuda:12.4.1-devel-ubuntu22.04",
        add_python="3.10",
    )
    .apt_install(
        "git",
        "wget",
        "cmake",
        "build-essential",
        "clang",
        "ninja-build",
        "ffmpeg",
        "libgl1",
        "libglib2.0-0",
        "pkg-config",
        "libavformat-dev",
        "libavcodec-dev",
        "libavdevice-dev",
        "libavutil-dev",
        "libavfilter-dev",
        "libswscale-dev",
        "libswresample-dev",
        "zlib1g-dev",
    )
    # Step 1: PyTorch stack pinned to upstream requirements.txt
    .pip_install(
        "torch==2.6.0",
        "torchvision==0.21.0",
        extra_index_url="https://download.pytorch.org/whl/cu124",
    )
    # Step 2: Clone repo first so we have the models/ package available
    .run_commands(
        f"git clone --depth 1 {REPO_URL} {REPO_DIR}",
    )
    # Step 3: All pinned pip dependencies from requirements.txt
    .pip_install(
        "triton==3.2.0",
        "transformers==4.57.0",
        "omegaconf==2.3.0",
        "ninja==1.13.0",
        "numpy==2.2.6",
        "einops==0.8.1",
        "moviepy==1.0.3",
        "opencv-python==4.12.0.88",
        "av==15.1.0",
        "plyfile==1.1.2",
        "ftfy==6.3.1",
        "accelerate==1.10.1",
        "nanobind==2.9.2",
        "uvicorn",
        "jaxtyping",
        "rich",
        "pandas",
        "Pillow",
        "imageio",
        "tqdm",
        "safetensors",
        "huggingface_hub",
        "gradio",
        "fastapi",
        "python-multipart",
    )
    # Step 4: Ensure build tools are available (needed for --no-build-isolation)
    .pip_install("wheel", "setuptools")
    # Step 5: Compile gsplat from the exact pinned commit (needs CUDA toolkit + torch)
    .run_commands(
        "TORCH_CUDA_ARCH_LIST='8.0;8.6;8.9;9.0+PTX' "
        "CUDA_HOME=/usr/local/cuda "
        "pip install --no-cache-dir --no-build-isolation "
        "git+https://github.com/nerfstudio-project/gsplat.git@32f2a54d21c7ecb135320bb02b136b7407ae5712",
        gpu="A100",
    )
    # Step 5: Install diffusers from exact pinned commit
    .pip_install(
        "git+https://github.com/huggingface/diffusers.git@447e8322f76efea55d4769cd67c372edbf0715b8",
    )
    # Step 6: Compile spz (Niantic gaussian splat codec) from exact pinned commit
    .run_commands(
        "pip install --no-cache-dir git+https://github.com/nianticlabs/spz.git@a4fc69e7948c7152e807e6501d73ddc9c149ce37",
    )
    # Step 7: Download the FlashWorld model checkpoint from HuggingFace
    .run_commands(
        "pip install --no-cache-dir huggingface_hub[cli]",
        "python3 -c \""
        "from huggingface_hub import hf_hub_download; "
        "hf_hub_download(repo_id='imlixinyang/FlashWorld', filename='model.ckpt')"
        "\"",
    )
)


class CommandError(RuntimeError):
    pass


def _run(command: list[str], cwd: Path | None = None) -> str:
    result = subprocess.run(
        command,
        cwd=str(cwd) if cwd else None,
        capture_output=True,
        text=True,
        check=False,
    )
    if result.returncode != 0:
        raise CommandError(
            f"Command failed: {' '.join(command)}\n"
            f"exit_code={result.returncode}\n"
            f"stdout:\n{result.stdout}\n"
            f"stderr:\n{result.stderr}"
        )
    return result.stdout


def _list_files(root: Path) -> list[str]:
    if not root.exists():
        return []
    return [str(p.relative_to(root)) for p in sorted(root.rglob("*")) if p.is_file()]


@app.function(
    image=image,
    gpu="A100-80GB",
    timeout=60 * 30,
    volumes={"/data": flash_volume},
)
def generate(
    text_prompt: str = "",
    image_b64: str | None = None,
    run_id: str | None = None,
    n_frame: int = 16,
    image_height: int = 480,
    image_width: int = 704,
    export_video: bool = True,
    export_spz: bool = True,
    export_ply: bool = False,
    video_fps: int = 15,
    offload_t5: bool = False,
) -> dict[str, Any]:
    """Generate a 3D scene from a text prompt and/or image using FlashWorld."""
    import sys
    sys.path.insert(0, str(REPO_DIR))

    import torch
    import numpy as np
    import time as _time
    from PIL import Image as PILImage
    from huggingface_hub.constants import HUGGINGFACE_HUB_CACHE

    # These imports come from the FlashWorld repo
    from utils import export_gaussians, sample_from_dense_cameras, normalize_cameras, create_raymaps
    from app import GenerationSystem

    run_id = run_id or uuid.uuid4().hex[:12]
    run_dir = RUNS_DIR / run_id
    run_dir.mkdir(parents=True, exist_ok=True)

    # Locate the model checkpoint
    ckpt_path = os.path.join(
        HUGGINGFACE_HUB_CACHE,
        "models--imlixinyang--FlashWorld",
        "snapshots",
        "6a8e88c6f88678ac098e4c82675f0aee555d6e5d",
        "model.ckpt",
    )
    if not os.path.exists(ckpt_path):
        from huggingface_hub import hf_hub_download
        hf_hub_download(repo_id="imlixinyang/FlashWorld", filename="model.ckpt")

    # Initialize the generation system
    device = torch.device("cuda")
    print("Initializing GenerationSystem...")
    generation_system = GenerationSystem(
        ckpt_path=ckpt_path,
        device=device,
        offload_t5=offload_t5,
    )
   
[truncated — 5157 more characters]
```

[26 more indexed source files omitted to keep this export small. The full file list is in the Codebase structure section above.]