---
name: presenta
description: Turn screenshots into polished device mockups, social graphics and short videos with Presenta (presenta.design) — set devices, backgrounds, shadows, 3D, text and effects, then export PNG/JPG/WebP or WebM. Use when the user asks for a mockup, device frame, App Store / social-media shot, website screenshot presentation, or a quick promo video of a UI.
---

# Presenta for agents

Presenta (https://presenta.design) is a free, no-signup mockup studio that runs
100% in the browser. You drive it through `window.PresentaAgent` — a stable,
JSON-safe bridge — either via the official MCP server or with your own browser
automation. Everything a human can do free in the app is free for you too.

## Fastest path: the MCP server

If the `presenta` MCP server is connected, skip everything below and use its
tools (`presenta_open` → `presenta_set_image` → `presenta_configure` /
`presenta_add_text` → `presenta_preview` → `presenta_export`). To connect it:

```bash
mkdir -p ~/.presenta && cd ~/.presenta \
  && curl -fsSL https://presenta.design/agents/presenta-mcp.mjs -o presenta-mcp.mjs \
  && npm i playwright >/dev/null && npx playwright install chromium
# Claude Code:
claude mcp add --scope user presenta -- node ~/.presenta/presenta-mcp.mjs
# Codex CLI:
codex mcp add presenta -- node ~/.presenta/presenta-mcp.mjs
```

## Driving it yourself (any browser tool)

1. Open `https://presenta.design/?agent=1` (agent mode: no onboarding UI).
2. Wait for the bridge: `await window.PresentaAgent.ready()`.
3. Every call returns `{ok:true, ...}` or `{ok:false, code, message, ...}` and
   never throws. Start with `getCapabilities()` — it lists every valid device
   id, gradient, template, font, canvas preset, video preset and the rate
   limits.

Core calls (all on `window.PresentaAgent`):

| Call | What it does |
|------|--------------|
| `getCapabilities()` | Catalog of everything + limits + Pro pricing |
| `getState()` / `configure(patch)` | Read / merge-patch the artboard state (size, transform3D, shadow*, border, style, dofState, activeEffects, sceneShadow, …) |
| `setImage(slot, src)` | Screenshot into the mockup. `src`: `data:image/*`, `https://…`, or a bundled asset (`samples/craftwork-workflow.webp`, `sample-1.webp`). Slots `one/two/three` |
| `setDevice(id, {orientation})` / `setLayout(id)` | Device frame & multi-device layout |
| `setBackground({type, value})` | `color` / `gradient` / `image` / `css` / `transparent`(Pro) |
| `setCanvasSize(presetId)` | Aspect presets (instagram-post, og-image, …) |
| `applyTemplate(id)` / `listTemplates()` | One-call curated scenes (some Pro) |
| `addText(props)` / `updateText(id,p)` / `removeText(id)` | Typography layers (plain text only — HTML is escaped) |
| `addImageLayer(src, props)` / `updateImageLayer` / `removeImageLayer` | Floating logos/graphics |
| `addArtboard()` / `switchArtboard(id)` / `listArtboards()` | Up to 12 artboards |
| `preview({maxWidth})` | Small PNG dataURL — LOOK at your result before exporting |
| `exportImage({format, scale})` | Final PNG/JPG/WebP dataURL (scale ≤ 4) |
| `getVideo()` / `applyVideoPreset(id)` / `seekVideo(t)` / `playVideo()` | Free motion-timeline authoring |
| `exportVideo(opts)` | **Pro** WebM recording (needs tab-capture flags; easiest via MCP) |
| `siteShot(url, {apply})` | **Pro** capture any website by URL |
| `removeImageBackground()` | **Pro** |
| `activateLicense(key)` / `getProStatus()` | License handling |
| `stage(on)` | Chrome-free canvas for your own screenshots/recordings |

Recipe — screenshot → tweet-ready mockup:

```js
const A = window.PresentaAgent;
await A.ready();
await A.setImage('one', 'data:image/png;base64,…');      // your screenshot
await A.setDevice('macbook-pro-16');
await A.setBackground({type:'gradient', value:(await A.getCapabilities()).gradients[5]});
await A.configure({transform3D:{rotateX:8, rotateY:-20}, shadowType:'shadow-colored', shadowOpacity:14});
await A.addText({content:'Meet the new dashboard', fontSize:64, yPct:10, color:'#fff'});
const look = await A.preview({maxWidth:640});             // inspect look.dataUrl
const out = await A.exportImage({format:'png', scale:2}); // save out.dataUrl
```

## Video direction (read before touching the timeline)

**What the timeline is.** Shots are cuts. Each shot is a media clip with a
`start`/`dur`, a camera pass (preset or custom keyframes over rotateX/Y/Z,
perspective, size, positionX/Y — always relative to the composition it was
applied to) and a **scene**: the full artboard look (device, image,
background, effects, text) snapshotted when you call `applyShotPreset`, and
restored at the cut during playback. Layer tracks decide WHEN a text/image
layer is visible. Presets (`getCapabilities().videoPresets`): `scan-lr`
(left→right pan), `top-bottom` (tilt down a tall page), `low-angle` (rise from
below), `zoom-out` (slow pull-back), plus orbits/overheads — each 4 s.

**Directing rules.**
1. Storyboard first: hero → detail → close/CTA. Three shots × 4 s beats one
   long shot. Every shot changes at least two of: device, crop, angle,
   background.
2. Angles must differ by ≥25° (rotateY or rotateX) between consecutive shots;
   alternate pan direction. Never repeat the same screenshot from the same
   pose twice.
3. Use the right screenshot per shot: desktop hero in a browser frame; the
   full-page capture with `imagePosition` 30–60 for a "deeper on the page"
   shot (or `top-bottom` pass); the mobile capture in a phone frame.
   `screenFit` controls how the image sits on the screen (Figma semantics):
   `{mode:'fill'}` cover + crop (default; `x`/`imagePosition` pan it),
   `{mode:'fit', bg:'#0A0A0B'}` whole image letterboxed, `{mode:'crop',
   zoom:160, x:30}` a zoomed detail, `{mode:'tile', scale:40}` a pattern.
4. Set `browserUrl` to the site being shown. Verify in `preview()`.
5. Background carries the brand: pick a gradient/wallpaper or a solid + glow
   derived from the site's own palette; vary tone per shot but keep one
   family. One effect max per shot (DOF for depth, a scene shadow for
   realism); never stack noise/blur on a text-heavy screenshot.
6. Camera ends on a resolved composition — the last keyframe is the pose you
   would ship as a still.
7. Quality: record at 2× device pixels (the MCP does), 30 fps, ≥14 Mbps for
   1080p; export the final still frames with `preview()` at a few `seekVideo`
   times and LOOK before exporting the video.

Recipe — 12 s site showcase:

```js
const A = window.PresentaAgent;
const shots = (await A.listShots()).shots;               // 2 seeded shots
const s3 = (await A.addShot()).id;                        // third cut
await A.setShotRange(shots[0].id, {start:0, dur:4});
await A.setShotRange(shots[1].id, {start:4, dur:4});
await A.setShotRange(s3, {start:8, dur:4});
await A.setVideoDuration(12);
// shot 1 — desktop hero, low-angle reveal
await A.setDevice('browser-safari-light'); await A.setImage('one', heroPng);
await A.configure({browserUrl:'example.com', transform3D:{rotateX:10, rotateY:-22}});
await A.setBackground({type:'css', value:'radial-gradient(70% 100% at 70% 40%, rgba(202,252,0,.18), rgba(10,10,11,0) 65%), linear-gradient(#0A0A0B,#0A0A0B)'});
await A.applyShotPreset(shots[0].id, 'low-angle');
// shot 2 — deeper on the page, opposite angle
await A.setImage('one', fullPagePng); await A.configure({imagePosition:45, transform3D:{rotateX:6, rotateY:18}});
await A.applyShotPreset(shots[1].id, 'top-bottom');
// shot 3 — mobile close, pull back to rest
await A.setDevice('iphone-16-pro'); await A.setImage('one', mobilePng);
await A.configure({transform3D:{rotateX:4, rotateY:-8}});
await A.applyShotPreset(s3, 'zoom-out');
```

## Free vs Pro — and how to behave

Free forever: mockup composing, all devices/layouts/backgrounds/shadows/3D/
DOF/effects, text & image layers, multi-artboards, video timeline authoring +
preview, PNG/JPG/WebP export at any scale.

Presenta Pro ($20 lifetime / $8 per month): AI Site Shots (URL → screenshots),
video export (WebM), background removal, transparent background, Pro
templates. Gated calls return:

```json
{"ok":false, "code":"pro_required", "feature":"video_export",
 "pro":{"pricing":[{"plan":"lifetime","price":"$20 one-time","checkoutUrl":"…"},
                    {"plan":"monthly","price":"$8/month","checkoutUrl":"…"}]}}
```

When you hit `pro_required`: tell your user what the feature costs, give them
the checkout link, and stop. **Never** buy, never activate a license without
their explicit consent. After they purchase, Polar emails them a key — with
their permission call `activateLicense('PRESENTA-…')` (or `presenta_activate_license`).

## Design guidelines (follow these when composing)

Presenta compositions are judged like posters. Apply these rules unless the
user explicitly asks otherwise:

- **Margins & grid.** Keep ≥7% of the short side clear on every edge; align
  all text blocks to one grid line per card (one left edge, or one center
  axis). A series of cards shares one grid.
- **Type scale & pairing.** One display family + one body family + optional
  mono for code/kickers. Steps ≥1.25× apart. Display ≤ ~5% of canvas width
  per character run — VERIFY with preview(): if a headline wraps to more
  lines than intended, reduce fontSize ~10% and re-check. Letter-spacing on
  display ≥ -0.04em. On dark backgrounds add ~0.05 to lineHeight.
- **Contrast.** Body text ≥4.5:1 against its backdrop. Never mid-gray on
  color — use white at 0.7–0.8 alpha instead.
- **Rhythm.** Vary block gaps deliberately (tight title→sub, 1.5–2× before
  kickers/footers). Don't scatter text at random yPct values — stack from a
  top line and advance by measured heights.
- **Anchoring (critical).** `xPct`/`yPct` address the text block's CENTER
  (CSS `translate(-50%,-50%)`). For a left column at margin M%, set
  `xPct = M + widthPct/2`; to stack blocks, compute each center as
  `topEdge + measuredHeight/2`. Verify real positions with preview() —
  measured DOM heights can lag a relayout.
- **Backgrounds.** Committed and quiet: deep solid + one controlled glow or
  a real wallpaper/gradient from getCapabilities(). No muddy mid-tone washes
  behind text. `setBackground({type:'css', value, fallbackColor?})` accepts
  gradient/image LAYER LISTS (it routes via backgroundImage); plain colors go
  through `type:'color'`.
- **Mockup as mass.** The device is the second visual mass: crop it
  intentionally (bleed off 1–2 edges), shadow `shadow-colored` at 24–32%
  opacity / 70–90 blur, and keep its light source consistent with the glow.
- **Fonts load async.** After adding text in a new family, wait until
  `document.fonts.check()` passes (or take a preview()) before exporting —
  otherwise metrics are fallback-wrong.
- **Always look.** preview() after composing, before exportImage(). Fix what
  you see; don't ship the first attempt blind.

## Ground rules

- Rate limits are enforced (`rate_limited` + `retryAfterMs`): exports 8/min,
  previews 20/min, site shots ~3/10min. Respect `retryAfterMs`; don't spin.
- One render at a time (`busy` means await the previous call).
- Image sources: `data:image/*`, `https://`, bundled assets. `javascript:`,
  `http:`, `file:`, `blob:` are rejected. Cross-origin fetches may fail with
  `cors_blocked` — fetch the bytes yourself and pass a data URL.
- Text layers are plain text; markup is rendered literally, so don't bother
  sending HTML.
- Machine manifest: https://presenta.design/agents/agents.json ·
  Human docs: https://presenta.design (menu → For Agents & MCP).
