---
title: "Typographic Poster Set — exact type, then localised per market"
tier: hero
format: image
theme: typography | brand | localization
persona: brand studio, marketer, designer
duration: "~10–20 min"
budget_usd: "~$0.50–$2.00 (a poster is $0.063 at 1K, $0.084 at 2K)"
caps: ["grok-image-2", "grok-image-2-edit", "ffmpeg-export"]
skills: ["grok-image-2", "livepeer-ui-design", "finishing-quality"]
showcases: ["/chapters/typographic-poster-set-example.html"]
status: "live"
reliability: 4.4 # 5/5 at registration; typography verified character-exact at three difficulty levels
---

# Typographic Poster Set — agent playbook

> Paste this whole file (with the BRIEF filled in) into Claude cowork, chat, or Code.
> Livepeer Agent renders a poster whose **text is exactly what you specified**, then
> swaps the headline per market while the layout holds.

## What you'll get

- **A poster with correct type** — the strings you gave, character for character
- **A localised set** — the same design with the headline swapped per market
- **Platform crops** for the placements you name
- **A legibility check** on every render, so a bad one is caught before you ship it

## Why this playbook exists

Until now the standing rule here was: never trust AI to render text in an image;
put type on in post. Grok Imagine Image 2.0 changes that for typography
specifically — measured character-exact on a poster, on photoreal packaging with
small print, and across an infographic's labels
(`docs/caps/grok-image-2-typography-evaluation-2026-08-12.md`).

**It does not change it for charts.** In the same test every label was right and
the bars were wrong — 27% drawn at ~25%. If your piece encodes numbers as
lengths, this is the wrong tool and the agent must say so.

## Tell the agent about the poster

```yaml
poster_title:        # e.g. "Night Market"
headline:            # the EXACT string, as it must appear
subline:             # the EXACT second string (date, venue, strapline)
art_direction:       # 1–2 sentences: style, palette, mood
aspect_ratio: 3:4    # 3:4 | 1:1 | 4:3 | 16:9 | 9:16 …
resolution: 1k       # 1k ($0.063) | 2k ($0.084)
variants: 3          # how many to render before choosing (1–4)
markets:             # OPTIONAL. Localised headlines, e.g.
                     #   es: "MERCADO NOCTURNO"
                     #   fr: "MARCHÉ DE NUIT"
max_spend_usd: 5.00
output_slug:         # kebab-case filename prefix
```

## Conventions (how the agent must run)

1. **Refuse charts.** If the brief asks for a bar chart, pie chart, gauge or map,
   say plainly that this model draws the labels correctly and the geometry
   incorrectly, and offer `sensenova-u1-infographic` instead. Do not render it
   anyway.
2. **Quote the strings.** Always phrase the prompt as `reading exactly: <string>`.
   Loose direction produces invented copy that looks deliberate.
3. **Warn about the wait.** ~107s per render — slow for an image. Say so before
   rendering a set of four, and batch with `num_images` rather than looping.
4. **Check every render.** Read the text back off each image and compare it
   character by character to the brief. Report any mismatch rather than shipping
   it; this is cheap and it is the whole point of the playbook.
5. **Spend gate.** `$0.063 × variants` at 1K (× markets for the localised set).
   State it; refuse to exceed `max_spend_usd`.

## Steps

### 1 — Render the variants

```jsonc
create_media({
  action: "generate",
  model_override: "grok-image-2",
  prompt: "<art_direction>. Large headline reading exactly: <headline>. Below it in smaller type: <subline>.",
  // aspect_ratio / resolution / num_images go through run_capability inputs if you
  // need the full parameter surface
})
```

Read the text off each render. Show them with a ✓/✗ per string. **STOP** for a pick.

### 2 — Localise the winner

For each market, edit rather than regenerate — the layout stays put:

```jsonc
run_capability({
  capability: "grok-image-2-edit",
  prompt: "Replace the headline with: <market headline>. Keep the layout, type treatment and colours identical.",
  inputs: { image_urls: ["<winning poster>"] }
})
```

Check each localised render's text too — an accented or non-Latin headline is a
harder case than the original and deserves the same scrutiny.

### 3 — Crops

```jsonc
create_media({ action: "generate", model_override: "ffmpeg-export",
  inputs: { image_url: "<final>", width: 1080, height: 1920, mode: "cover" } })
```

## Deliver

- the poster set, with the **verified strings** listed next to each
- **cost**: variants + localisations + crops
- one honest line per render on whether the type came back exact, and what was
  wrong if it did not

## When to use something else

| If… | Use |
|---|---|
| the piece contains a chart or any number-as-geometry | `sensenova-u1-infographic`, or render deterministically |
| the text does not have to be exact | `flux-dev` / `flux-schnell` — ~50× faster |
| you want open-weight typography cheaper | `ideogram-v4` ($0.04/img) |
| the type must sit over video | `hyperframes-caption` / `hyperframes-render` |
