AI spark

Pro

A brand-quality animated spark mark. Four tapered rays and four lighter diagonals breathe at rest, ripple while thinking, pulse like a heartbeat while writing, then gather and settle when done. Every status change blends from the pose the mark is already in.

Live · keyboard ready
AI ready
Ready
Ready
12
16
20
24
32
48
  • Showing that an assistant is thinking or writing, inline above a streaming reply.
  • A send or generate button that turns into a working indicator inside a composer.
  • An image or canvas tool that needs a calm, branded working mark over the canvas while a render runs.
  • Empty states and onboarding where a 64 to 96px hero mark gives the AI feature an identity.
  • Determinate progress with a known percentage; use a progress bar or the generation stats component instead.
  • Multi step agent runs where each step needs its own state and detail; use agent-status or agent-run.
  • Plain text loading lines with no mark; use text-shimmer.

Installation

Pro source and install commands unlock with a Pro plan.

example.tsx
import { AiSpark } from "@/components/arc/ai-spark/ai-spark"; export function ReplyStatus({ phase }: { phase: "thinking" | "generating" | "done" }) {  return <AiSpark variant="pill" status={phase} />;}

Assistant avatar

example.tsx
<AiSpark variant="avatar" size={32} status={phase} announce={false} />

Inline in a sentence

example.tsx
<p>  <AiSpark size="1em" tone="inherit" status="generating" announce={false} /> Drafting a reply to Maya Chen</p>

Custom progress label

example.tsx
<AiSpark variant="pill" status="thinking" label="Searching 4 sources" />

Hero mark in an empty state

example.tsx
<AiSpark size={96} status="idle" />

AiSpark

An animated spark mark that shows an AI at work: four tapered primary rays and four lighter diagonal rays around a soft core. Each status is its own motion, and every change blends from the current pose, so states never hard cut. Use variant="pill" for a status label or variant="avatar" for a round tile.

PropTypeDefaultDescription
status"idle" | "thinking" | "generating" | "done" | "error"–What the AI is doing. Leave it unset and the spark plays through idle, thinking, generating and done on its own.
sizenumber | string24 (32 for the avatar, 16 in the pill)Size in px, or any CSS length such as "1em" to match surrounding text. The mark itself, the avatar tile, or the mark inside the pill. Proportions are hinted optically: small marks get heavier rays and under 18px the diagonals become dots.
tone"accent" | "foreground" | "muted" | "inherit""accent"Semantic color of the mark; the diagonals and core use a lighter tone of the same color. Error always eases to a muted danger tone.
colorstring–Any CSS color, overriding tone.
variant"mark" | "pill" | "avatar""mark"mark renders the spark alone. pill adds a crossfading status label (Ready, Thinking, Writing, Done, Couldn't finish) in a quiet bordered capsule. avatar sets the mark in a round, lightly tinted tile for message avatars and icon buttons.
labelstring–Pill text, overriding the built-in label for the current status, for example "Reading 12 files".
speednumber1Multiplies every loop and spin speed.
twinklebooleantrueA very slow breathing twinkle while idle. Turn it off for a fully still idle mark.
pausedbooleanfalseFreezes the animation in place; resuming continues from the same phase.
announcebooleantrueAnnounce status changes through a polite live region. Turn it off for decorative copies so only one spark speaks.
aria-labelstring–Accessible name. Defaults to a phrase for the status, such as "AI is thinking".
classNamestring–Class on the root span.
styleCSSProperties–Inline styles on the root span.
None
The spark is a status indicator and takes no focus. Put controls such as Stop next to it.
  • The root is a polite live region (role="status"): a status change is announced once, as "AI is thinking" for the mark or as the label text for the pill.
  • aria-busy is set while thinking or generating.
  • Status is never color alone: each state has its own shape and motion, error shortens the rays and dims the diagonals, and the pill adds a text label.
  • Set announce={false} on decorative copies (lists, size ladders) so screen readers hear one voice. They then render as role="img" with an accessible name.
  • The SVG is aria-hidden; the name comes from the label or a visually hidden phrase.
  • One requestAnimationFrame loop per spark writes eight small ray paths, a core radius and two group transforms. React never re-renders per frame and nothing triggers layout.
  • Every status is a periodic function of time and of position around the ring, so idle, thinking and generating loop with no seam.
  • Idle: a 6.4s breath of the whole mark while one diagonal at a time softly reaches out. Thinking: one crest ripples around the ring every 1.9s, with small per-ray phase offsets, while the mark turns at 26 degrees per second. Generating: a lub dub heartbeat every 1.04s, axes firing in sequence and diagonals answering, 46 degrees per second, with two particles orbiting just outside the rays (40px and up).
  • Done: the rays gather toward the center in 170ms and ease back to rest over 510ms with no overshoot, while the spin glides into the home angle on an ease-out whose speed only falls.
  • Error: one small decaying shake over 460ms, shorter rays, dimmer diagonals and a muted danger tone over 320ms.
  • Status changes blend over 420 to 560ms from wherever the mark is; an interrupted blend becomes the new starting point, and the gather and shake gestures always finish smoothly even if the status changes again. Pill labels crossfade (100ms out, 160ms in, 2px rise) while the pill eases to its new width.
  • Reduced motion: no spin, loops, gather, shake or particles. Each status shows a still pose with a 200ms blend between poses and the color change.
  • The loop pauses when the spark is off screen or the tab is hidden, and stops entirely once a resting state has settled.
  • The mark has a fixed footprint at any status, so it never shifts surrounding layout.
  • Crisp from 12px to 120px and beyond: geometry lives in a 100 unit viewBox, ray weight is hinted on a log scale by rendered size, and CSS lengths are measured so hinting still applies.
  • The pill keeps a 28px height and only animates its label width.
  • Eight SVG paths, three circles and one rAF loop per spark; resting states stop the loop entirely, and off screen sparks do not tick.
  • No canvas, no filters, no blur, no gradients; depth comes from two tones of one color.
  • The pill label shimmer runs only while thinking or generating.

Notes for AI

Give your coding assistant the Markdown reference instead of screenshots.

  • Map your stream lifecycle to status: request sent or tool running -> thinking, first token until the last -> generating, finished -> done, failed or aborted -> error. Return to idle when the composer is ready again.
  • Use variant="pill" with label for specific progress ("Searching 4 sources", "Drawing"); keep labels to one or two words.
  • Use size="1em" and tone="inherit" to drop the mark inline into a sentence or a button so it matches the text.
  • Use one spark per active generation. For many rows, render a static mark (status="idle", twinkle={false}) and animate only the active row.
  • Use variant="avatar" next to assistant messages or inside an icon button; size sets the tile and the mark is 56% of it.
  • Leaving status unset makes it self-play, which is only meant for demos and marketing pages.
/components/ai-spark/markdown

The full library index for assistants is at /llms.txt.