Final render — held

Every frame exists: four stills, one text card and the eight-second live-motion clip are all generated and attached. The final render is blocked on a product fix — confirm refuses this deck because two of its slides are blank on purpose, and the deployed build has no way to say so. That bug, and two others we hit, are written up below. 35 seeds spent so far (~$2.10); the render itself is 1 more.

Spent so far35 seeds · $2.10
Runtimepending render
Shots5 (1 live)
Night Shift, ep. 1. One anime character, four stills and one eight-second live-motion clip. Every prompt that produced them — including the one that failed — is printed in full below.
Key takeaways
  • Beauty is not the lever. Memorability is intrinsic to an image and largely independent of rated beauty (Isola et al., CVPR 2011). Beautiful anime is the default output of current models, so optimising for it optimises the thing everyone already has.
  • Put the aesthetic somewhere it does not belong. The craft move is to spend a fantasy-and-romance visual language on an ordinary, universally recognised setting — a fuel-station night shift and the small arithmetic people do at the pump.
  • Design the character for regeneration, not for a portrait. Fine facial detail drifts between generations; coarse high-contrast anchors do not. One oversized jacket, one hanging earphone, one green-white light — those come back. The precise eyelid does not.
  • Consistency comes from the reference parameter, not from prose. referenceAssetId holds the character. Re-describing her in every prompt does not.
  • You cannot ask a diffusion model for illegible text. "Blurred, unreadable figures" still draws figures, and they come back as garbage. The only reliable move is to remove the text-bearing surface from the frame. Blur is a request; absence is a composition. This cost us 5 seeds to learn.
  • Separate stopping from sharing. They are different mechanisms with different evidence, and no peer-reviewed study has manipulated a short-form first frame on a live feed and measured retention. Every composition rule here is an inference stack, and is labelled as one.
  • Planned 31 seeds, spent 35 — the overrun is one discarded image. 21 seeds without the live clip; 16 for three stills plus card plus render. The music was free.

Published 2026-09-23. Written as a build log rather than a retrospective: the method first, then every attempt that was actually made, including the one that was thrown away.

Before you copy anything below

Image generation is non-deterministic. The same prompt returns a different frame every time. Run the prompts on this page and you will get your Mira, not ours — a different face, a different jacket drape, a different night. That is not a failure of the prompt; it is what these models are. What reproduces is the method — one reference frame, everything else generated from it — not the pixels.

Concretely: the asset IDs printed below are ours and live in our library. They will not resolve for you. In steps 2–4 you must pass your own first-frame assetId, the one your own step 1 returned.

The idea — why anime, and why a fuel station

Ask any current image model for an anime still and it will hand you a good one on the first try. Cel shading, rim light, a hand-painted background, a face with the right proportions. That is not a hard request any more; it is close to a default mode.

Which is precisely the problem with it.

In 2011, Isola, Xiao, Torralba and Oliva ran a large-scale study on what makes an image memorable and found something uncomfortable for anyone optimising a frame: memorability is an intrinsic, stable property of an image, consistent across viewers who have never met — the split-half rank correlation across independent groups lands around ρ = 0.75 — and it is largely independent of rated aesthetic beauty. A gorgeous photograph and a memorable one are not the same category. People agree with each other about which images they will remember, and their agreement does not track how pretty they find them.

0.75
rank correlation between independent groups of viewers on which images they remember — memorability is intrinsic to the image, and it is not the same thing as beauty.

Put those two facts together and the strategy inverts. If a beautiful anime frame is the model's default output, then a beautiful anime frame is what every other person prompting that model is also holding. Beauty has been commoditised; it is table stakes, not a differentiator. The thing that survives is distinctiveness — and distinctiveness is not a rendering setting, it is a decision about subject.

So: take the aesthetic that is normally reserved for fantasy, school romance and magical realism, and spend it on something nobody would ever animate. Not a castle. Not a rooftop confession. A night-shift fuel station, and the small arithmetic people do at the pump.

Everyone has stood at one. Everyone has watched the two numbers on the display race each other — litres on one side, money on the other — and quietly decided which one they were going to stop at. That moment is universally recognised and almost never depicted, which is exactly the shape you want: maximum recognition, minimum precedent.

The price sign is scenery, not a thesis

A tall illuminated price sign is in every shot, and its number changes from episode to episode. That is a compositional choice and it should stay one.

The temptation is to let the sign become the argument — to make a reel about fuel prices. Don't. Two reasons, one aesthetic and one mechanical.

The aesthetic reason: as soon as the number is the point, the character stops being a person and becomes a device for delivering the point. The thing that makes this work is that Mira is not commenting on anything. She is tired, the shift is eleven hours, and there is a sign behind her. Treated as setting, the sign carries recognition and cost-of-living weight without asking anyone to agree with anything.

The mechanical reason is more boring and more important. Platforms restrict how far political content travels to accounts that do not already follow you. Meta has said so explicitly for Instagram's recommendation surfaces, and equivalent limits exist elsewhere. Reach to non-followers is the entire mechanism a series like this depends on — an episode that only reaches your existing followers is a diary entry. Coding the content as political is the one self-inflicted way to switch that mechanism off. Keep the sign as scenery and the door stays open.

What the evidence does and does not say

This is the section where most "hook science" articles quietly fall apart, so here is the honest version, split into two piles that are routinely mixed up.

Stopping and sharing are different mechanisms

Stopping is the first three seconds: whether a thumb keeps moving. Meta measures this natively as skip rate, with a 3-second threshold. It is a perceptual and attentional question, and there is real vision-science literature adjacent to it.

Sharing is a social act that happens after stopping and has almost nothing in common with it. A reel can be extremely good at stopping people and never be forwarded once. Treating the two as one funnel is the most common error in this genre.

What is supportable, and what it actually supports

These are findings from the vision-science literature. They are about static images and lab conditions, not feeds — which is a real limitation, stated again below.

What sharing actually runs on — named as mechanisms, not findings

There is no clean experimental literature that tells you why one short-form video gets forwarded and a similar one does not. What there is, is a set of mechanisms that are well described in adjacent work and that practitioners can observe. Naming them honestly:

The honest limit

Say this plainly, because the credibility of everything above depends on it: no peer-reviewed study has manipulated the first frame of a short-form video on a live feed and measured retention. Not one. Every composition rule in this article is an inference stack — lab findings about static images and gaze, extended to a moving feed nobody has run the experiment on.

That is a reason to treat these as priors to test, not laws to follow. It is also a reason the measurement section at the end of this article exists.

What is not evidence, and gets cited constantly

For completeness, the claims you will find in almost every competing article on this subject, and should not repeat:

The cast

Three elements, and one of them is a sign.

Mira — the constant

She is the same in every episode: an oversized company windbreaker with sleeves past her hands, one earphone in and one hanging loose, under green-white fluorescent canopy light. Hair tied up loosely, strands escaping.

That description is not a mood board. It is an engineering spec, and it is the single most useful craft idea in this article.

A character in an AI reel has to survive regeneration. You generate her once, and then every subsequent shot is the model reconstructing her from that reference. Reconstruction is lossy, and it is lossy unevenly: fine facial geometry — the exact eyelid, the precise nose bridge, the particular jaw — drifts noticeably between generations. Coarse, high-contrast, structurally distinctive anchors do not. A silhouette reproduces. A single garment with an unmistakable shape reproduces. A prop reproduces. A light with a strong colour cast reproduces.

So build the character out of the things that reproduce:

AnchorTypeWhy it survives
Windbreaker two sizes too big, sleeves past the handsSilhouetteChanges her outline, not her face. Outline is coarse and high-contrast — the most reliably reproduced feature there is.
One earphone in, one hanging loosePropA small asymmetry that reads instantly and is easy for the model to place. Asymmetry is more identifiable than symmetry.
Green-white fluorescent canopy light from aboveLightA colour cast is a global property of the frame. It survives almost any reconstruction and unifies every shot into one world.
Loosely tied hair with escaping strandsSilhouetteCoarse shape at the top of the head, visible even from behind.

Four anchors. Note what is not on the list: her face. If you design a character whose identity lives in her face, you will spend seeds fighting drift on every shot. If you design one whose identity lives in a jacket, an earphone and a light, she comes back.

The rotating guest — one per episode

Mira is constant; the customer changes. This is what gives the series a shape without needing a plot:

Each is a complete story in one frame, and each is an identity marker for somebody watching.

The sign — the third character

Tall, illuminated, always in frame, always out of focus in episode one — unreadable bokeh behind her head. Its number changes each episode. Over a run, the sign is the only thing in the series that visibly moves, and the audience notices it without being told to. That is the series mechanic, and it costs nothing to run.

The plan

Five shots. One of them buys generated motion; the rest are stills with camera motion over them. One end card with exact typography, which is free.

#ShotHow the image is madeMotionCaption
1Mira behind the counter, medium close-up, canopy light from above, sign unreadable behind heradd_slide_image — no reference (this is the reference)Live Motion, 8s — the clip carries all the movementnone (empty string)
2Seen from behind, wide. Empty forecourt at 3am, one car at the far pump, the sign looming over bothadd_slide_image — referenced to shot 1push_in≤ 4 words
3The pump itself — a gloved hand on the nozzle, kiosk glow behind. Planned as an over-the-shoulder shot with the display in frame; that version had to be thrown away. See below.add_slide_image — referenced to shot 1push_in≤ 4 words
4Pulling the shutter down, bag over shoulder, walking past a parked car without stopping, sign glowing behind heradd_slide_image — referenced to shot 1push_in≤ 4 words
5End card — one line, exact typographyadd_text_card — freestaticthe line itself

Two structural notes before the prompts.

Why shot 1 carries no caption. The evidence says text competes with the face rather than adding to it, and that the first frame should stay clean for roughly eight tenths of a second. The composer draws captions per slide; there is no per-slide caption delay. So the reliable way to get a clean opening beat is to give slide 0 an empty caption and let the first words land on shot 2. That is a real constraint, honestly stated, rather than a parameter that does not exist.

Why shot 1 is the live one. Looming captures attention and it is the hook shot that needs to earn the stop. Since a live slide takes no camera move from the renderer, the push-in has to be written into the live prompt — which is what the prompt below does.

The steps, with the exact prompts

These are the calls that actually ran, with the prompts as they were sent. Tool and parameter names are the Poppify MCP as deployed. If you are driving it from an AI assistant, the assistant makes these calls; if you are reproducing the method elsewhere, the shape is what matters — one reference generation, N referenced generations, a free text card, one paid motion clip, one render.

Read the yellow note near the top of this page before copying any of it: the asset IDs are ours, they will not resolve for you, and your frames will not look like ours.

Step 0 — open the session

Carry the brief in verbatim. userWords is not decoration: the server parses it, and constraints that only live in your paraphrase die there.

start_session_from_topic({
  topic:     "Night Shift — a quiet anime series set at a roadside fuel station",
  userWords: "<the user's brief, verbatim>",
  shape:     "story",
  audience:  "people who have worked a night shift, or stood at a pump doing the arithmetic",
  benefit:   "recognition — someone else noticed the thing you do at the pump",
  hook:      "an anime aesthetic spent on the least glamorous place in town",
  aspectRatio: "9:16",
  apiKey:    "<wallet key>"
})

Step 1 — the reference still (5 seeds)

This one image decides the entire series, so it is generated alone and looked at before anything else is bought. Note what the prompt does and does not contain: it describes light, garment, prop, mood and framing in detail, and it forbids text four different ways.

add_slide_image({
  apiKey: "<wallet key>",
  sessionId: "<id>",
  ignoreSessionPhotos: true,
  prompt:
    "Anime still, night, cel-shaded with hand-painted background. A young woman
     behind the counter of a small roadside fuel-station kiosk, lit almost
     entirely by green-white fluorescent canopy light falling from above. She
     wears a company windbreaker two sizes too big, sleeves past her hands; one
     earphone in, the other hanging loose against her collar. Hair tied up
     loosely, strands escaping. Tired, calm, unbothered — not sad, not smiling.
     Medium close-up, eye level, shallow depth of field. Far behind her through
     the kiosk window, completely out of focus, the soft glow of a tall
     illuminated price sign — unreadable bokeh. Limited palette: green-white
     fluorescent, deep night blue, warm amber highlight. Hard rim light on her
     shoulder. Film grain. No text, no letters, no numbers anywhere in the
     image. Vertical 9:16."
})
// → vasset_1790231401621_36a37641-…   kept, on-model first try

Three lines in that prompt are doing disproportionate work. "Tired, calm, unbothered — not sad, not smiling" is there because a model asked for "tired" will reliably give you melancholy, and melancholy is a different character. The named palette holds the world together across four separate generations. And "unreadable bokeh" on the price sign is a bet that turns out to be a bad one — see shot 3.

Shot 1 — reference still · generated

Exists. vasset_1790231401621_36a37641-… — kept on the first attempt. Image drops in here.

Shot 1. Every other shot in the reel is generated from this frame by reference, which is why it is bought and inspected on its own.

Step 2 — shots 2 and 4 (5 seeds each)

Each of these passes the shot-1 asset as referenceAssetId. The prompt then describes the change, not the character — identity comes from the reference. The opening clause is deliberately repetitive; that repetition is doing the work.

// Shot 2 — the forecourt, wide
add_slide_image({
  apiKey: "<wallet key>", sessionId: "<id>",
  referenceAssetId: "<YOUR shot-1 assetId>",
  prompt:
    "Same anime style, same cel-shading and hand-painted background as image 1,
     same night, same green-white fluorescent palette. WIDE establishing shot of
     the whole fuel station forecourt at 3am, seen from across the empty road.
     Bright canopy lights flooding an empty concrete apron; a single small car at
     the far pump, tiny in frame; the lit kiosk window glowing at the back where
     the woman from image 1 sits, small and distant. Tall illuminated price sign
     on a pole at the left edge, its numbers soft and out of focus. Wet asphalt
     reflecting the canopy light. Deep night blue sky, no stars. Empty, quiet,
     enormous. No text, no letters, no legible numbers anywhere. Vertical 9:16."
})
// → kept

// Shot 4 — walking away at dawn
add_slide_image({
  apiKey: "<wallet key>", sessionId: "<id>",
  referenceAssetId: "<YOUR shot-1 assetId>",
  prompt:
    "Same young woman from image 1 — same oversized green company windbreaker,
     same loose tied-up hair, same earphone cord — now outside, seen from behind
     and slightly to the side, walking away from the fuel station at dawn. Small
     canvas bag over one shoulder, hands in pockets, sleeves over her hands. She
     walks past a small parked car in the foreground without turning her head
     towards it. Early dawn light: the night blue lifting to cold pale grey at
     the horizon, the canopy lights behind her still on and now weaker than the
     sky. The tall price sign glows on its pole behind her, its figures soft and
     unreadable. Long empty road ahead. Same anime cel-shaded style and
     hand-painted background as image 1. Quiet, ordinary, not tragic. No text,
     letters or legible numbers anywhere. Vertical 9:16."
})
// → vasset_1790231626828_e04dbc03-…   kept

Shot 4 is the one that carries the episode, and it does it with a single physical detail: she walks past the parked car without turning her head towards it. That is the whole ending. Nothing is stated; the geometry states it.

Note also that shot 4 changes the time of day. The reference holds the character; it does not hold the lighting, which means an episode can move from 3am to dawn without losing her.

Step 2b — shot 3, which failed, and the rule it produced

Shot 3 was planned as an over-the-shoulder view at the pump, with the pump's display panel as the bright centre of the frame. The prompt asked for the display's figures to be "blurred and unreadable".

What came back had crisp, confident, entirely legible text on the panel. It read:

Pront 4520 · Diay's 6000 · 2200

Five seeds, unusable. And note that the prompt had also said "no text anywhere in the image" — the instruction was there and lost the argument, because the composition had asked for a screen and a screen is a thing with writing on it.

Here is the rule, and it is the most valuable thing in this guide:

You cannot ask a diffusion model for illegible text. Asking for "blurred", "soft", "out of focus" or "unreadable" figures still asks for figures, and the model will draw them — sharply, and wrongly. You must remove the text-bearing surface from the frame: turn the display away, crop it out, put it behind something. Blur is a request. Absence is a composition.

The redo does exactly that. It keeps the pump and throws away the panel — and it says so four times, in four different vocabularies, because one negation is not enough when the composition is pulling the other way:

// Shot 3, second attempt — the display removed from the frame entirely
add_slide_image({
  apiKey: "<wallet key>", sessionId: "<id>",
  referenceAssetId: "<YOUR shot-1 assetId>",
  prompt:
    "Same anime style, same cel-shading, same night and green-white fluorescent
     palette as image 1. Tight low-angle shot of a gloved hand gripping a fuel
     nozzle in the pump socket of a car, seen from the side. The nozzle, the hose
     and the car's paintwork fill the frame. The pump's display housing is turned
     completely away from the camera and is not visible at all — there is NO
     screen, NO panel, NO dial and NO signage anywhere in the frame. Behind, far
     out of focus, the warm glow of the lit kiosk window. Shallow depth of field,
     intimate, cold night air. Absolutely no text, no letters, no numbers, no
     symbols, no lettering of any kind anywhere in the image. Vertical 9:16."
})
// → vasset_1790231684591_fa3bf463-…   kept

The redo is a better shot than the original plan, which is worth noticing. The failure forced a tighter, more intimate frame — a hand and a nozzle instead of a wide view with a screen in it — and removed the one element that would have made the number the subject. The constraint improved the composition.

If you genuinely need a number on screen — a price, a litre count, a total — the answer is not a better prompt. It is add_text_card, or an overlay you control. Generated typography is never going to be the right tool for a string that has to be correct.

Shots 2–4 — referenced stills · generated

All three exist and are attached. Images drop in here.

Shots 2–4. The test to apply: does the silhouette match across all four frames? The face drifts a little. The jacket, the hair and the light do not — which is the whole argument for designing a character out of coarse anchors.
The discarded frame — 5 seeds

The first shot-3 attempt, with "Pront 4520 / Diay's 6000 / 2200" written across the pump display. Drops in here.

What "blurred and unreadable" actually produces. Printed here on purpose. A guide that shows only the takes that worked is the thing that makes people distrust these tools.

Step 3 — attach the stills to slides (free)

update_slides({
  sessionId: "<id>",
  ops: [
    { action: "set_image", slideIndex: 0, imageUrl: "<shot 1 url>" },
    { action: "set_image", slideIndex: 1, imageUrl: "<shot 2 url>" },
    { action: "set_image", slideIndex: 2, imageUrl: "<shot 3 url>" },
    { action: "set_image", slideIndex: 3, imageUrl: "<shot 4 url>" },
    { action: "set_text",  slideIndex: 0, newText: "" }
  ]
})

The empty set_text on slide 0 is the clean-opening-frame decision, made explicit. An empty string is the suppression signal; it is not the same as leaving the field alone.

It is also, on the currently deployed build, the thing that stops this deck rendering — because nothing distinguishes a caption left blank on purpose from one nobody got round to writing. That is the third bug below, and it is the reason the slot at the top of this page is still empty.

Step 4 — the end card (free, and exact)

This is the one line of language in the reel, and it is the reason the whole thing holds together. It goes on a text card, never in an image prompt.

add_text_card({
  apiKey: "<wallet key>",
  sessionId: "<id>",
  slideIndex: 4,
  eyebrow: "THE NIGHT SHIFT",
  text:    "Eleven hours selling something I walk home without.",
  footer:  "EP. 1",
  theme:   "midnight",
  align:   "left"
})
// → typeset.lines: ["Eleven hours selling", "something I walk", "home without."]
//   fontSize 107px at 1080x1920.   0 seeds.

Passing sessionId and slideIndex in the same call attaches the card to that slide directly, which is better than copying the returned URL into a separate update_slides call — the copy is where it breaks.

The response tells you where the lines broke. typeset.lines came back as three lines — "Eleven hours selling" / "something I walk" / "home without." — at 107px on a 1080×1920 card. That is how you check a line break without rendering anything: the break after "walk" lands the sentence's turn on its own line, which is what you want, and if it had not you would re-run the card for free until it did. Compare this with an image model, where you would be paying 5 seeds per attempt to inspect typography you cannot control.

eyebrow and footer are doing series work rather than episode work. "THE NIGHT SHIFT" above and "EP. 1" below turn a closing line into a serial frame, at no cost, and the number is the only thing that changes next week.

Shot 5 — end card · rendered

Exists. Typeset server-side, three lines, 107px. Zero seeds. Drops in here.

Shot 5. Typography rendered by the server, not generated. Exact on the first try, at zero cost — which is also why there is no reason to ever risk it in an image prompt.

Step 5 — the live-motion shot (10 seeds)

Two calls: set the slide's motion mode, then animate it. Preview the assembled prompt for free with dryRun before paying.

update_slides({
  sessionId: "<id>",
  action: "set_motion_mode",
  slideIndex: 0,
  motionMode: "live",
  liveDurationSeconds: 8,
  liveAction:
    "She blinks slowly once and exhales; her breath fogs faintly in the cold air.
     A moth crosses the fluorescent tube behind her. Almost imperceptible
     push-in. She does not speak and does not smile."
})

animate_slide({ sessionId: "<id>", slideIndex: 0, dryRun: true })   // free preview

animate_slide({ sessionId: "<id>", slideIndex: 0, apiKey: "<wallet key>" })  // 10 seeds
// → lmasset_1790231784536_20c613ad-…   31 seconds to generate
// → "Requested provider 'seedance' can't do audio; used 'veo' instead"

Four things are happening in that liveAction, and each is deliberate.

"She does not speak and does not smile" is the fifth thing, added after the fact and worth keeping. Image-to-video models have a strong prior towards making faces perform — a smile, a mouth moving as if mid-sentence. On a character whose entire register is unbothered, a smile is not a small error. Negations of this kind are cheap to add and occasionally save the whole clip.

The engine is resolved per request, and it will substitute

Our session had defaultLiveProvider set to Seedance. The server ran Veo 3.1 Lite instead, and said so:

"Requested provider 'seedance' can't do audio; used 'veo' instead"

That is correct behaviour and worth understanding before it surprises you. Live-motion providers differ in what they support — duration buckets, end-frame interpolation, native audio — and the server routes to the cheapest registered engine that can actually do what the call asked for. The clip took 31 seconds to generate.

The practical consequence: never promise a specific model, to a client or in a caption. Ask for the capability. The engine underneath you is a runtime decision, and it changes.

Step 5b — the soundtrack (free)

Before paying 5 seeds to generate music, search the library. It costs nothing:

get_music_library({ ... })
// → "Comfortable Melancholy" — score 61
//   sparse piano, cello, 70bpm, nordic noir, 30s.   0 seeds.

A score of 61 is a strong match, and sparse piano at 70bpm against a fluorescent-lit night shift is not a compromise — it is the track you would have asked for. The free library beat paying for generation, which is the usual outcome and the reason the search is the first move rather than the fallback.

This is a small saving in isolation. Across a weekly series it is 5 seeds an episode that never needed spending.

Shot 1 — live-motion clip · generated

Exists. lmasset_1790231784536_20c613ad-… — eight seconds, Veo 3.1 Lite, 31 seconds to generate. Drops in here.

The live shot. What to check: is she the same person she was in the still, and does the push read as a push rather than a zoom?

Step 6 — render and collect

get_result({ sessionId: "<id>" })   // pre-confirm: exact seed breakdown, free

confirm({ sessionId: "<id>", apiKey: "<wallet key>" })   // 1 seed, starts the render

get_result({ sessionId: "<id>" })   // poll every 20–30s → complete, with videoUrl

Call get_result before confirm as well as after. Pre-confirm it returns the exact price the render will charge, which is the last cheap moment to notice that something is configured wrong.

This is where this build currently stops. confirm refuses the deck, for a reason that turns out to be a bug rather than a mistake on our side. See below.

Three ways this build broke

All three are worth printing. The first will happen to you. The second and third are ours to fix, and describing them is more useful than pretending the run was clean.

1. Garbled text on a surface we asked to be blurry

Covered above: 5 seeds, one discarded frame, and the rule that came out of it — blur is a request, absence is a composition. This is the one a reader following this method will hit, because the instinct to solve unwanted text with "make it unreadable" is very strong and completely wrong.

2. animate_slide refused an image that was already on the slide

The live-motion call came back with:

unattached_generated_image — slide 0 is still using a different image,
most likely the original upload

Slide 0 was the generated frame. Nothing was wrong with the deck.

The cause is a spelling mismatch inside the product. The slide stores its media as a gs:// reference, while the guard that checks "did you remember to attach the image you paid for?" compares against signed https:// URLs. Two spellings of the same asset, so the comparison fails and the guard fires.

Two things to take from it. First, the guard exists for a good reason and fired in the right direction — it refused before charging, and the failure it is designed to catch (paying 5 seeds for an image, forgetting to attach it, and then paying 10 more to animate the wrong picture) is a genuinely expensive one. A guard that is wrong but free is far better than a guard that is right but late.

Second, the documented escape works: re-call with useSlideImageAsIs: true, which means "yes, I know, animate what is on the slide."

animate_slide({
  sessionId: "<id>", slideIndex: 0,
  apiKey: "<wallet key>",
  useSlideImageAsIs: true
})

Use it when you have actually checked the slide. It is an override, not a workaround.

3. confirm refuses a deck whose blanks are deliberate

This is the one holding the render, and it is the most interesting of the three because the product is enforcing a rule that is usually right.

confirm returns deck_half_written — a guard against shipping a reel where somebody forgot to write half the captions. Reasonable. But this deck has two slides that are blank on purpose:

Both blanks are decisions. The deployed build cannot tell them apart from an oversight, because an empty string and a lone space are both trimmed back to empty — so there is currently no way to express "blank on purpose" at all. The fix exists; it has not shipped yet, and we are deliberately holding the deploy rather than rushing it to make an article look finished.

Hence the empty slot at the top of this page. Everything else — four stills, one text card, one live clip, one soundtrack — is done and attached.

The build log

A guide that shows only the successful take is the thing that makes people distrust these tools. Here is every attempt in this build, in order, with what it cost.

StepAttemptSeedsOutcome
Shot 1 — Mira reference15Kept. On-model first try.
Shot 2 — forecourt, wide15Kept.
Shot 3 — over the shoulder at the pump15Discarded. Legible garbage on the pump display — "Pront 4520 / Diay's 6000 / 2200" — where the prompt had asked for blurred, unreadable figures.
Shot 3 — redo, display removed from frame25Kept. Tighter and better than the original plan.
Shot 4 — walking away at dawn15Kept.
End card — add_text_card10Kept. Typeset to three lines at 107px.
Soundtrack — get_music_library10Kept. "Comfortable Melancholy", library match at score 61. No generation needed.
Live Motion on shot 1110Kept. 31s to generate. Routed to Veo 3.1 Lite after the requested provider was found not to support audio.
Render (confirm)—pendingBlocked on deck_half_written. Two slides are blank on purpose; the deployed build has no way to say so.
Total so far935~$2.10 against 31 seeds planned. The whole overrun is one discarded image.

That is a 13% overrun on a five-shot reel, caused entirely by one wrong instinct about how to hide a number. It is worth stating in the open, because the alternative — publishing the four frames that worked and quietly not mentioning the fifth — is how "AI makes this effortless" gets manufactured, and it is why nobody's cost estimates in this category are trustworthy.

For calibration: the wider generative-video market runs at three to five attempts per usable clip. Six kept frames out of seven attempts is a very good day, and it is a good day mostly because a reference frame does the heavy lifting.

What it costs

ItemSeeds (planned)Seeds (actual)Cost at $0.06/seed
Mira reference still (shot 1)55$0.30
Three referenced shots (2, 3, 4)1520$1.20
End card (add_text_card)00$0.00
Soundtrack (library match)00$0.00
Live Motion on shot 1 (8s)1010$0.60
Render1pending$0.06
Total3135 + render$2.10 so far

The single line that differs is shot 3, bought twice.

Two cheaper versions of the same plan:

Budget for one discard. Six kept frames out of seven attempts was a good run, and a good run still came in over the plan.

Every subsequent episode reuses Mira's reference still, so the 5 seeds for shot 1 are a one-time cost across the whole series rather than a per-episode one. Same for the soundtrack.

New accounts get 50 free seeds on signup, which covered this build — one discard included — with 15 to spare.

Five gotchas that cost money

1. Never put digits or words in an image prompt

Image models garble typography. Not occasionally — reliably. Prices, signs, labels, install commands, brand names: they come back as plausible-looking nonsense, and you pay 5 seeds to find out. Use add_text_card, which renders the literal string server-side with real typography, is exact on the first try, and costs nothing. (Poppify's server now refuses a text-primary image prompt before charging, which is a sign of how often this happened.)

The stronger version of this rule, learned the expensive way in this build: do not ask for illegible text either. "Blurred", "soft", "out of focus", "unreadable" — all of them still ask for figures, and the model draws them. Take the text-bearing surface out of the frame instead. Blur is a request; absence is a composition.

2. A live slide gets no camera move from the renderer

This is the one that surprises people. On a slide with a live-motion clip, the renderer applies no FFmpeg camera move — no push_in, nothing. The reason is that the clip already contains its own camera movement, and layering a zoompan on top of an image that is already moving over-zooms it and discards the composition the clip was generated with. So a camera move you want on a live slide has to be written into the live prompt: "the camera pushes in almost imperceptibly." Setting a videoEffect on that slide does nothing.

3. Generate shot 1 alone, and look at it

Shot 1 is the reference for the entire series. If the jacket is wrong, every referenced shot inherits the wrong jacket — and you will have paid 15 seeds to propagate the error before noticing. Buy one image, look at it, then buy the rest. Cheap discipline: 5 seeds risked instead of 20.

The same logic applies one level down, and we did not follow it: shots 2, 3 and 4 were fired together. Had shot 3 been inspected before shot 4 was bought, nothing would have changed in this case — but the habit that saves you is checking each frame against the reference before committing to the next.

4. Never name an anime studio or an existing IP in a prompt

Name a studio, a franchise or a known character and the video model's content filter will refuse the generation as third-party IP — and you pay for the attempt. Describe attributes instead, which is both permitted and more precise: cel-shaded, hard rim light, hand-painted backgrounds, limited-frame motion, limited palette. That is what you actually wanted from the studio name anyway.

5. Character consistency comes from the reference parameter, not from words

The most common expensive mistake is re-describing the character in every prompt and hoping. It does not work; each generation re-interprets the description slightly and the drift compounds across four shots. Pass referenceAssetId (or referenceImageUrl, or referenceSlide to point at a slide in the session) and describe only the change. The identity comes from the pixels, not the adjectives.

How to measure it

If the composition rules above are inferences, the only thing that turns them into knowledge is measurement. Here is what is actually measurable and how not to fool yourself with it.

Skip rate is a real metric, and it is not what people think

Skip rate is a native Instagram metric. It appeared in-app around August 2025 and became available through the Graph API on 4 December 2025. It is defined as the percentage of views that were skipped within the first 3 seconds, with views as the denominator.

Two things it is not, both of which get substituted for it constantly:

Those three numbers have three different denominators, and the value swings dramatically depending on which one you picked. Conflating them produces a chart that moves for reasons that have nothing to do with your content.

Skip rate rises with reach

This is the subtle one. As a reel is pushed to a wider and less-qualified audience, a larger share of its viewers are people with no particular reason to care — so skip rate goes up as reach goes up. A post that reached 400 people and one that reached 40,000 are not comparable on this metric, and the 40,000 one will look worse while performing better.

Compare posts at similar view counts, or do not compare them.

There are no public benchmarks

Nobody has published a credible distribution of skip rate by niche, format or follower count. Any article that gives you a "good skip rate is under X%" number is making it up. Your own back catalogue is the only baseline that exists.

Sample size, honestly

Short-form performance is extremely noisy, and small-sample comparisons are where people convince themselves of things that are not there.

Views per variantWhat you can actually conclude
Below ~30Nothing. This is noise. Do not draw a conclusion from it, and especially do not change your format because of it.
~62Enough to resolve a difference of roughly ±10 percentage points.
~199Enough to show that a 20% rate genuinely beats a 10% rate.

Which means: a series is a measurement instrument and a single reel is not. Run the same character, the same setting and the same shot grammar across enough episodes to accumulate views, change one thing at a time, and compare within a reach band. That is slow. It is also the only version of this that produces knowledge rather than anecdote.

The series format

One episode per week. Mira is constant; the guest rotates; the number on the sign changes. Five shots each, 21 or 31 seeds each plus whatever gets discarded, and the reference still and the soundtrack are already paid for.

The format does three things at once. It amortises the character design across every future episode. It gives returning viewers a reason to return that is not the algorithm's job. And it accumulates the sample size that the measurement section says you need — which is the real argument for a series over a one-off, and the one nobody makes.

Frequently asked

How do you make an AI anime video?
Generate one reference still of your character, then generate every other shot from that reference rather than re-describing the character in words. Put literal text on a text card instead of in an image prompt, because image models garble typography. Buy generated motion on one shot only — the one where the subject has to actually move. In Poppify the call order is start_session_from_topic → add_slide_image for the reference still → add_slide_image with referenceAssetId for the remaining shots → update_slides set_image to attach them → add_text_card for the end card → update_slides set_motion_mode plus animate_slide for the live shot → confirm → get_result. The five-shot plan prices at 31 seeds, about $1.86; the build documented here came to 35 because one image was discarded and regenerated. Image generation is non-deterministic, so the same prompts will give you a different character — the method reproduces, the pixels do not.

What is the best AI anime generator?
For a single beautiful anime frame, most current image models produce one on the first try — anime is close to a default mode for them, which is exactly why a beautiful anime frame is a weak differentiator. The harder problem is a sequence: the same character across four shots, exact typography, and one shot with real motion. That is a composition problem rather than a generation problem, so the tool that matters is the one that holds a character reference across calls and prices per finished render rather than per generated second.

How do you keep an AI character consistent across shots?
Two things. First, pass the reference parameter — referenceImageUrl or referenceAssetId — rather than re-describing the character in prose; re-description drifts every time. Second, design the character out of coarse, high-contrast anchors that survive regeneration: a distinctive silhouette, one garment, one prop, one light source. Fine facial detail drifts between generations. A jacket two sizes too big, one hanging earphone and green-white fluorescent light reproduce reliably; the exact shape of an eyelid does not.

Does a beautiful anime frame make a reel memorable?
Not by itself. Isola, Xiao, Torralba and Oliva (CVPR 2011) found that image memorability is intrinsic and consistent across viewers, and that it is largely independent of rated aesthetic beauty. Since a gorgeous, genre-conforming anime frame is the default output of these models, optimising for beauty optimises the thing everyone already has. Distinctiveness is the lever — which in practice means applying an aesthetic normally reserved for fantasy or romance to an ordinary, universally recognised setting.


This post is part of our research series on the social media tooling landscape. The evidence claims in it are deliberately narrow: where a mechanism is named rather than measured, the text says so.

Related reading