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

# How it works

From source image to looping mp4 — the architecture in one page.

# How it works

Motioness is an **edge-hosted image-to-motion proxy**. One URL replaces an `<img src>` and the response is either a poster jpg (immediate) or a looping mp4 (generated once, cached forever).

## Request flow

```
Browser ──→ /v1/{projectKey}/{src}.{fmt}
              │
              ▼
       Cloudflare Worker
              │
   ┌──────────┴──────────┐
   │ Already cached?     │── yes ──→ Stream from edge cache (~5ms)
   │                     │
   └──────────┬──────────┘
              │ no
              ▼
     ensureAsset(srcUrl, opts)
              │
   ┌──────────┴──────────┐
   │ Asset row in D1?    │── yes (pending) ──→ 202 + Retry-After
   │                     │── yes (ready)   ──→ stream from R2
   └──────────┬──────────┘
              │ no
              ▼
       Insert asset row, enqueue:
         1. Claude vision  → motion prompt
         2. Replicate i2v  → mp4 (single OR two-stage loop)
         3. R2 write       → durable bytes
         4. Webhook fire   → asset.completed
              │
              ▼
        Return 202 to browser
```

## The three pipelines

The `mode` parameter (or project default) picks one of three pipelines:

| Mode | Pipeline | Use case |
|---|---|---|
| `loop` | Two-stage stitch — generate forward + return-to-start, merge in fixed timeline | Backgrounds, hero accents, ambient motion |
| `animate-in` | Single i2v call, played once on entry | Reveal animations, build-ups, story beats |
| `animate-on-hover` | Same as `animate-in` but client-paused until hover | Interactive cards, gallery items |

`loop` is two Replicate calls; `animate-in` and `animate-on-hover` are one. See [motion modes](/concepts/motion-modes).

## Tier-based model selection

The image-to-video model is picked from the project owner's plan, not from the request:

| Tier | Model | Quality | Loop-capable |
|---|---|---|---|
| Free | `wan-fast` | low | no — uses crossfade |
| Pro ($19/mo) | `seedance` | standard | yes (native) |
| Team ($79/mo) | `p-video` | high | yes (native) |

Customers can't request a higher-tier model than their plan. Quality scales with the plan.

## Edge cache + content-addressing

Asset IDs are content-addressed: same `(src, motion, mode, dur, prompt, seed, idem, aspect)` produces the same `asset_id`, which produces the same R2 path, which produces the same cached URL.

```
asset_id = sha256(src_url || motion || mode || dur_seconds || prompt || seed || idem || aspect)[0:16]
```

This means:
- Two browsers hitting the same proxy URL never re-generate.
- Adding `?idem=variant-a` and `?idem=variant-b` produces two distinct assets from the same source.
- Removing query params doesn't break cache — non-content params (`wait_ms`, `sig`, `exp`) are stripped before hashing.

## Storage

| Tier | Backend | What's there |
|---|---|---|
| 1 | Edge cache | mp4 + jpg responses, ~5ms hot reads |
| 2 | KV (`CACHE`) | `asset:{id}` index pointing at R2 path — skips D1 on cache miss |
| 3 | R2 (`R2_ASSETS`) | Durable mp4s (`assets/{id}.mp4`), posters (`posters/{srcHash}.jpg`) |
| 4 | D1 (`DB`) | Rows for `assets`, `generations`, `asset_events`, `webhook_deliveries`, `projects`, `users` |

## Reliability surface

Three independent recovery mechanisms keep generation honest:

1. **Per-stage retry envelope** — transient errors (Replicate 5xx/429, R2 5xx, timeouts) retry up to 3 attempts per stage with `[0s, 1s, 4s]` backoff.
2. **Replicate webhook + cron reconciler** — if the webhook never lands, a cron job runs every minute and queries Replicate directly to finalize stuck assets after 5 minutes. After 10 minutes it marks them `failed` with `pipeline_stalled`.
3. **Standard Webhooks delivery** — outbound webhooks retry on `30s, 2m, 10m, 30m, 2h` and dead-letter after 5 attempts.

You can re-enqueue a dead delivery from the dashboard or via [`POST /api/projects/:id/webhooks/deliveries/:deliveryId/redeliver`](/api/webhooks/redeliver).

## Brand context

When configured, a project's brand context (colors, fonts, tagline, content inventory) is injected into the vision prompt. The vision model gets:

```
Brand: Acme — fast SaaS observability
Tone: technical, confident
Colors: #0d9373 (primary), #f59e0b (accent), dark scheme
Fonts: Inter / IBM Plex Mono
```

This biases motion choices toward what fits the brand — subtle for editorial sites, kinetic for consumer brands, etc. See [brand context](/concepts/brand-context) for how to populate it (manual fields, brand-detect, or deep-crawl).

## What you don't have to think about

- **Prompt engineering** — the vision model writes the prompt for you.
- **Model selection** — tier picks it; `quality=` is a hint when you want explicit control.
- **Aspect handling** — `aspect=preserve` (default) matches the source.
- **CDN / cache invalidation** — content-addressed URLs never need invalidation.
- **Format negotiation** — request `.mp4` for video, `.jpg` for poster fallback.

## What you should know

- First request to an mp4 returns `202` while generation runs. Use `?wait_ms=8000` to long-poll.
- Free-tier mp4s use `crossfade` loop mode (visible seam); Pro+ mp4s loop natively.
- The proxy is **read-only** for the source URL — Motioness never modifies your source images.
- Generation costs apply to the **project owner**, not the embedding site.