> ## Documentation Index
> Fetch the complete documentation index at: https://docs.viggle.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Preprocessing Guide

> Pre-create characters and scenes for faster, reusable rendering

Pre-create characters and scenes to cut render latency by \~3x. Assets are stored and reusable across unlimited renders.

<Tip>Character preprocessing costs 1 credit. Scene creation is free. Rendered video costs 1 credit/second.</Tip>

## Prerequisites

* A Viggle API key ([get one here](https://portal.viggle.ai/keys))
* A reference image (PNG or JPG)
* A driving video (MP4)

## Workflow

<Steps>
  <Step title="Create a character" icon="user">
    Upload a reference image to extract a reusable character embedding. Free -- \~5s to process.

    <CodeGroup>
      ```python Python theme={null}
      import requests, time
      API_KEY, BASE = "YOUR_API_KEY", "https://apis.viggle.ai"
      headers = {"Authorization": f"Bearer {API_KEY}"}

      resp = requests.post(f"{BASE}/api/characters/preprocess", headers=headers,
          files={"image": open("character.png", "rb")},
          data={"name": "My Character"})
      character = resp.json()
      character_id = character["id"]
      ```

      ```javascript JavaScript theme={null}
      const API_KEY = "YOUR_API_KEY", BASE = "https://apis.viggle.ai";
      const headers = { Authorization: `Bearer ${API_KEY}` };
      const form = new FormData();
      form.append("name", "My Character");
      form.append("image", imageFile);
      const resp = await fetch(`${BASE}/api/characters/preprocess`, {
        method: "POST", headers, body: form
      }).then(r => r.json());
      const characterId = resp.id;
      ```

      ```bash cURL theme={null}
      curl -X POST "https://apis.viggle.ai/api/characters/preprocess" \
        -H "Authorization: Bearer YOUR_API_KEY" \
        -F "name=My Character" -F "image=@character.png"
      ```
    </CodeGroup>

    Poll until ready:

    <CodeGroup>
      ```python Python theme={null}
      while True:
          status = requests.get(f"{BASE}/api/characters/{character_id}", headers=headers).json()
          if status["status"] == "ready": break
          elif status["status"] == "failed": raise Exception(status.get("error_message"))
          time.sleep(5)
      ```

      ```javascript JavaScript theme={null}
      const sleep = (ms) => new Promise(r => setTimeout(r, ms));
      while (true) {
        const status = await fetch(`${BASE}/api/characters/${characterId}`, { headers })
          .then(r => r.json());
        if (status.status === "ready") break;
        if (status.status === "failed") throw new Error(status.error_message);
        await sleep(5000);
      }
      ```

      ```bash cURL theme={null}
      # Poll until status is "ready"
      curl "https://apis.viggle.ai/api/characters/CHAR_ID" \
        -H "Authorization: Bearer YOUR_API_KEY"
      # Repeat every 5 seconds until: "status": "ready"
      ```
    </CodeGroup>
  </Step>

  <Step title="Create a scene" icon="film">
    Upload a driving video to extract motion data. Free -- takes \~8x the video length (motion extraction + background inpainting + upload).

    <Tip>No driving video? [Import a template from viggle.ai](/guides/import-templates) instead.</Tip>

    <CodeGroup>
      ```python Python theme={null}
      resp = requests.post(f"{BASE}/api/scenes/preprocess", headers=headers,
          files={"video": open("dance.mp4", "rb")},
          data={"name": "Dance Sequence"})
      scene = resp.json()
      scene_id = scene["id"]
      ```

      ```javascript JavaScript theme={null}
      const sceneForm = new FormData();
      sceneForm.append("name", "Dance Sequence");
      sceneForm.append("video", videoFile);
      const sceneResp = await fetch(`${BASE}/api/scenes/preprocess`, {
        method: "POST", headers, body: sceneForm
      }).then(r => r.json());
      const sceneId = sceneResp.id;
      ```

      ```bash cURL theme={null}
      curl -X POST "https://apis.viggle.ai/api/scenes/preprocess" \
        -H "Authorization: Bearer YOUR_API_KEY" \
        -F "name=Dance Sequence" -F "video=@dance.mp4"
      ```
    </CodeGroup>

    Poll until ready:

    <CodeGroup>
      ```python Python theme={null}
      while True:
          status = requests.get(f"{BASE}/api/scenes/{scene_id}", headers=headers).json()
          if status["status"] == "ready": break
          elif status["status"] == "failed": raise Exception(status.get("error_message"))
          time.sleep(5)
      ```

      ```javascript JavaScript theme={null}
      while (true) {
        const status = await fetch(`${BASE}/api/scenes/${sceneId}`, { headers })
          .then(r => r.json());
        if (status.status === "ready") break;
        if (status.status === "failed") throw new Error(status.error_message);
        await sleep(5000);
      }
      ```

      ```bash cURL theme={null}
      # Poll until status is "ready"
      curl "https://apis.viggle.ai/api/scenes/SCENE_ID" \
        -H "Authorization: Bearer YOUR_API_KEY"
      # Repeat every 5 seconds until: "status": "ready"
      ```
    </CodeGroup>
  </Step>

  <Step title="Render" icon="play">
    Combine character + scene to generate video.

    <CodeGroup>
      ```python Python theme={null}
      job = requests.post(f"{BASE}/api/render", headers=headers, data={
          "character_id": character_id,
          "scene_id": scene_id,
      }).json()
      print(f"Job: {job['job_id']}")
      ```

      ```javascript JavaScript theme={null}
      const renderForm = new URLSearchParams();
      renderForm.append("character_id", characterId);
      renderForm.append("scene_id", sceneId);
      const job = await fetch(`${BASE}/api/render`, {
        method: "POST", headers, body: renderForm
      }).then(r => r.json());
      ```

      ```bash cURL theme={null}
      curl -X POST "https://apis.viggle.ai/api/render" \
        -H "Authorization: Bearer YOUR_API_KEY" \
        -d "character_id=CHAR_ID" -d "scene_id=SCENE_ID"
      ```
    </CodeGroup>
  </Step>

  <Step title="Poll and download" icon="download">
    Poll every 3-5 seconds until complete, then use `cdn_url` to download the final video.

    <CodeGroup>
      ```python Python theme={null}
      while True:
          status = requests.get(f"{BASE}/api/render/{job['job_id']}").json()
          if status["status"] == "complete":
              cdn_url = status["cdn_url"]
              break
          elif status["status"] == "failed": raise Exception(status.get("error_message"))
          time.sleep(3)

      video = requests.get(cdn_url)
      open("output.mp4", "wb").write(video.content)
      ```

      ```javascript JavaScript theme={null}
      const sleep = (ms) => new Promise(r => setTimeout(r, ms));
      let cdnUrl;
      while (true) {
        const s = await fetch(`${BASE}/api/render/${job.job_id}`).then(r => r.json());
        if (s.status === "complete") { cdnUrl = s.cdn_url; break; }
        if (s.status === "failed") throw new Error(s.error_message);
        await sleep(3000);
      }
      const vid = await fetch(cdnUrl);
      require("fs").writeFileSync("output.mp4", Buffer.from(await vid.arrayBuffer()));
      ```

      ```bash cURL theme={null}
      # Poll until complete
      curl "https://apis.viggle.ai/api/render/JOB_ID"
      # Download using the cdn_url from the response
      curl -o output.mp4 "CDN_URL_FROM_RESPONSE"
      ```
    </CodeGroup>
  </Step>
</Steps>

## Reusing assets

<AccordionGroup>
  <Accordion title="Render same character with different scenes">
    <CodeGroup>
      ```python Python theme={null}
      job2 = requests.post(f"{BASE}/api/render", headers=headers, data={
          "character_id": character_id,
          "scene_id": another_scene_id,
      }).json()
      ```

      ```javascript JavaScript theme={null}
      const form2 = new URLSearchParams();
      form2.append("character_id", characterId);
      form2.append("scene_id", anotherSceneId);
      const job2 = await fetch(`${BASE}/api/render`, {
        method: "POST", headers, body: form2
      }).then(r => r.json());
      ```

      ```bash cURL theme={null}
      curl -X POST "https://apis.viggle.ai/api/render" \
        -H "Authorization: Bearer YOUR_API_KEY" \
        -d "character_id=CHAR_ID" -d "scene_id=ANOTHER_SCENE_ID"
      ```
    </CodeGroup>
  </Accordion>

  <Accordion title="Render different character with same scene">
    <CodeGroup>
      ```python Python theme={null}
      job3 = requests.post(f"{BASE}/api/render", headers=headers, data={
          "character_id": another_character_id,
          "scene_id": scene_id,
      }).json()
      ```

      ```javascript JavaScript theme={null}
      const form3 = new URLSearchParams();
      form3.append("character_id", anotherCharacterId);
      form3.append("scene_id", sceneId);
      const job3 = await fetch(`${BASE}/api/render`, {
        method: "POST", headers, body: form3
      }).then(r => r.json());
      ```

      ```bash cURL theme={null}
      curl -X POST "https://apis.viggle.ai/api/render" \
        -H "Authorization: Bearer YOUR_API_KEY" \
        -d "character_id=ANOTHER_CHAR_ID" -d "scene_id=SCENE_ID"
      ```
    </CodeGroup>
  </Accordion>

  <Accordion title="List your assets">
    <CodeGroup>
      ```python Python theme={null}
      characters = requests.get(f"{BASE}/api/characters", headers=headers).json()
      scenes = requests.get(f"{BASE}/api/scenes", headers=headers).json()
      ```

      ```javascript JavaScript theme={null}
      const characters = await fetch(`${BASE}/api/characters`, { headers })
        .then(r => r.json());
      const scenes = await fetch(`${BASE}/api/scenes`, { headers })
        .then(r => r.json());
      ```

      ```bash cURL theme={null}
      curl "https://apis.viggle.ai/api/characters" \
        -H "Authorization: Bearer YOUR_API_KEY"
      curl "https://apis.viggle.ai/api/scenes" \
        -H "Authorization: Bearer YOUR_API_KEY"
      ```
    </CodeGroup>
  </Accordion>
</AccordionGroup>

## What's next?

<CardGroup cols={2}>
  <Card title="On-Demand Rendering" icon="zap" href="/guides/on-demand">
    Render without preprocessing -- one API call
  </Card>

  <Card title="Import Templates" icon="download" href="/guides/import-templates">
    Use pre-made templates from viggle.ai
  </Card>

  <Card title="Render Options" icon="gauge" href="/render-options">
    Background mode and more
  </Card>
</CardGroup>
