Skip to content
maskostudioDocs

All pages

  • Overview/docs/use-cases
  • Mobile app/docs/use-cases#mobile
  • Website/docs/use-cases#website
  • Desktop/docs/use-cases#desktop
  • AI agents/docs/use-cases#agents
  • Videos/docs/use-cases#videos
  • Brand/docs/use-cases#brand
  • Start here/docs/tutorials#start-here
  • All tutorials/docs/tutorials
  • Create your first mascot/docs/tutorials/first-mascot
  • Make your mascot move/docs/tutorials/first-animation
  • Make your mascot talk/docs/tutorials/make-it-talk
  • Put your mascot on your website/docs/tutorials/mascot-on-your-website
  • Show a mascot while your AI thinks/docs/tutorials/ai-loading-mascot
  • Give your AI agent a face/docs/tutorials/ai-agent-face
  • Animate the mascot you already have/docs/tutorials/animate-your-mascot
  • Quickstart: your first animation/docs/quickstart
  • API keys and workspaces/docs/authentication
  • How mascots work/docs/how-mascots-work
  • Credits and costs/docs/credits
  • From text/docs/create/from-text
  • From an image/docs/create/from-image
  • From a website/docs/create/from-website
  • From a brand book/docs/create/from-brand-book
  • Reference images/docs/manage/references
  • What you can generate/docs/generation
  • Pose-to-animation walkthrough/docs/generate/workflow
  • Images and poses/docs/generate/images
  • Animations/docs/generate/animations
  • Talking animations/docs/generate/talking
  • Logos, scenes and stickers/docs/generate/design-assets
  • Cursor follower/docs/generate/cursor-follower
  • Batch generation/docs/generate/batch
  • Canvases and releases/docs/canvas
  • Build a canvas/docs/canvas/build
  • Canvas templates/docs/canvas/templates
  • Generate canvas assets/docs/canvas/generate-all
  • Export a canvas/docs/canvas/export
  • Smooth mascot playback/docs/integrations/playback
  • Hosting and file formats/docs/generate/cdn
  • File sizes/docs/generate/file-sizes
  • Image and video exports/docs/manage/media-exports
  • Terminal quickstart/docs/terminal
  • Desktop commands/docs/terminal/commands
  • CLI browser login/docs/authentication/cli
  • Swift SDK/docs/sdk/swift
  • TypeScript and Electron SDK/docs/sdk/typescript
  • Jobs and polling/docs/manage/jobs
  • Webhooks/docs/manage/webhooks
  • Manage mascots and assets/docs/manage/collections
  • Connect an AI agent/docs/ai-agents
  • Masko in ChatGPT/docs/ai-tools/chatgpt
  • Masko for Cursor/docs/ai-tools/cursor
  • Masko for Muse/docs/ai-tools/muse
  • Masko skill for coding agents/docs/ai-tools/skills
  • All endpoints/docs/reference
  • Requests and responses/docs/reference/conventions
  • Errors and recovery/docs/reference/errors
  • Changelog/docs/reference/changelog
  • Migrate to mascots and canvases/docs/manage/studio-migration
Get an API key
Documentation

Show a mascot while your AI thinks

While it works
When the answer lands

Your mascot thinks while your AI request runs, stays patient on long answers and celebrates when the result lands, with no flicker between clips. You need a mascot: Create your first mascot.

25 min · 33 credits · Web, iOS, Android

Do it in Studio

  1. Open your mascot in Studio, go to the Animations tab, press Create Animation and choose New image + animation.
  2. Fill in one row of the table below, keep Loop on, Standard and 5s, and press Generate · 11 credits. Repeat for the other two.
  3. When all three are ready, press Get Links, turn on Enable asset hosting and copy each animation's transparent_video_mov and transparent_video_webm from Export JSON. Then go to step 4.
NameStarting imageMovement
Thinkinghand on chin, looking up thoughtfullythinking, tapping its chin, eyes drifting up and back
Waitingstanding relaxed, patient and calmgentle breathing, a small sway, a slow blink
Donearms raised in celebration, big smileone happy hop with arms up, landing back in place

Do it with the API

You need a write key from API keys in MASKO_API_KEY and 33 credits (GET /v1/credits shows your balance). Never put the key in browser or app code.

1. Find your mascot

Copy your mascot's id.

curl https://api.masko.ai/v1/mascots \
  -H "Authorization: Bearer $MASKO_API_KEY"

2. Generate the three animations

One batch call runs all three in parallel. Each request draws a pose from image_prompt and animates it with animation_prompt; set animation_model to standard and keep loop: true.

curl -X POST https://api.masko.ai/v1/mascots/MASCOT_ID/generate-batch \
  -H "Authorization: Bearer $MASKO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "requests": [
      {
        "type": "animation",
        "animation_model": "standard",
        "name": "Thinking",
        "image_prompt": "hand on chin, looking up thoughtfully",
        "animation_prompt": "thinking, tapping its chin, eyes drifting up and back",
        "duration": 5,
        "loop": true
      },
      {
        "type": "animation",
        "animation_model": "standard",
        "name": "Waiting",
        "image_prompt": "standing relaxed, patient and calm",
        "animation_prompt": "gentle breathing, a small sway, a slow blink",
        "duration": 5,
        "loop": true
      },
      {
        "type": "animation",
        "animation_model": "standard",
        "name": "Done",
        "image_prompt": "arms raised in celebration, big smile",
        "animation_prompt": "one happy hop with arms up, landing back in place",
        "duration": 5,
        "loop": true
      }
    ]
  }'

It returns 202 Accepted with one job per animation. A batch is not all-or-nothing: check that every entry in data.jobs has a job_id, and never resend the whole batch after an uncertain response. premium costs 31 credits per clip instead of 11.

3. Wait for the jobs

Long-poll each job until completed or failed, or use a webhook in production. A completed job carries urls.webm and urls.hevc.

# Holds the request open until the job finishes, up to 120 seconds.
# If the timeout comes first, you get the current status: ask again.
curl "https://api.masko.ai/v1/jobs/JOB_ID?wait=true&timeout=120" \
  -H "Authorization: Bearer $MASKO_API_KEY" \
  -H "Masko-API-Version: 2026-09-26"

4. Play the thinking loop

<!-- Safari and iOS play the .mov, other browsers the .webm -->
<video
  autoplay
  loop
  muted
  playsinline
  width="160"
  height="160"
  aria-hidden="true"
>
  <source
    src="https://assets.masko.ai/.../thinking-c3d4.mov"
    type='video/mp4; codecs="hvc1"'
  />
  <source
    src="https://assets.masko.ai/.../thinking-a1b2.webm"
    type="video/webm"
  />
</video>

The Android file is the stacked_video asset (GET /v1/mascots/:id/assets), or transparent_video_android in the hosted links.

5. Switch between thinking, waiting and done

Changing one video's source can blank the mascot while the next clip loads, so stack two players and show the hidden one only after it draws a real frame, in one swap with no fade.

// Markup: <div class="mascot"> holding two <video muted playsinline>
// CSS:    .mascot { position: relative; width: 160px; height: 160px }
//         .mascot video { position: absolute; inset: 0; width: 100%;
//                         height: 100%; opacity: 0 }
//         .mascot video.is-shown { opacity: 1 }
// No CSS transition on opacity: a crossfade would show two mascots.

// Fill these from each job's urls.webm and urls.hevc.
const CLIPS = {
  thinking: { webm: '.../thinking.webm', hevc: '.../thinking.mov', loop: true },
  waiting: { webm: '.../waiting.webm', hevc: '.../waiting.mov', loop: true },
  done: { webm: '.../done.webm', hevc: '.../done.mov', loop: false }
};

const [a, b] = document.querySelectorAll('.mascot video');
let shown = a;
let latest = 0;

export function show(state) {
  const request = ++latest;
  const next = shown === a ? b : a;
  const clip = CLIPS[state];
  next.loop = clip.loop;
  next.innerHTML =
    `<source src="${clip.hevc}" type='video/mp4; codecs="hvc1"'>` +
    `<source src="${clip.webm}" type="video/webm">`;
  next.load();
  // Wait for a real frame before showing it. Register before play().
  next.requestVideoFrameCallback(() => {
    if (request !== latest) return; // a newer state won
    next.classList.add('is-shown');
    shown.classList.remove('is-shown');
    shown.pause();
    shown = next;
  });
  next.play();
}

Thinking starts at once, Waiting takes over after 8 seconds, and Done plays once and holds its last frame. The latest counter keeps a late clip from replacing a newer one.

Where requestVideoFrameCallback is missing, keep the current clip instead of switching on a timer. Smooth mascot playback covers iOS, Android, cleanup and flicker checks.

What you get

  • Three animations, Thinking, Waiting and Done: five-second Standard clips with their pose images.
  • Every format: transparent WebM and HEVC .mov, stacked_video for Android and a plain MP4. Exports to Lottie, GIF and more are free.
  • Hosted links on https://assets.masko.ai. For phones, turn on free 360 px size variants.

Using an AI agent?

Paste this into your coding agent, in your app's project, connected to the Masko MCP server at https://masko.ai/api/mcp (setup for Cursor or ChatGPT) or with the Masko skill and an API key.

Add a loading mascot to this app with Masko, following
https://masko.ai/docs/tutorials/ai-loading-mascot
Use my mascot "MASCOT_NAME". Ask me if I have several.
Generate three Standard 5-second looping animations: Thinking, Waiting and Done.
Show me the cost first and wait for my OK. It should be 33 credits.
Never put my Masko API key in browser or app code.
Wait for the jobs, then use each job's urls.webm and urls.hevc.
Show Thinking while my AI request runs, Waiting after 8 seconds, and Done when
it returns, with two video players so the mascot never flickers.
Put the HEVC source first so Safari keeps the transparency.

Next