Engineering

How we built real-time chart generation

Dev Patel·Staff Engineer··7 min read

Turning a sentence like "show me monthly revenue as a bar chart" into a clean, accurate visualization is harder than it looks. The model has to understand intent, the data has to be shaped correctly, and the chart has to render fast enough to feel live. Here is how Artisser's chart pipeline works, and the decisions that made it quick and reliable.

Intent first, pixels later

The biggest early lesson was to separate what the user wants from how it gets drawn. Instead of asking a model to emit an image of a chart, we ask it to emit a structured spec — chart type, axes, series, labels, and formatting. That spec is a small, validated JSON object. Drawing happens afterward in a deterministic renderer. This split is the whole trick: the model handles ambiguity, the renderer handles correctness.

A typed spec as the contract

Because the renderer only ever sees a typed spec, we can guarantee a lot of properties the model alone could not. We validate the spec against a schema before anything is drawn, which catches malformed output early and lets us repair it without bothering the user.

  • Every numeric axis gets sensible bounds, even when the model omits them.
  • Color palettes are pulled from a fixed brand set, so charts always look on-brand.
  • Unsupported combinations (a pie chart with a time axis, say) are rejected and re-asked.
  • Accessibility metadata — labels and roles — is attached automatically.

Streaming the spec for responsiveness

We do not wait for the full spec before showing anything. The model streams tokens, and we parse the partial JSON as it arrives. As soon as the chart type and first series are known, we draw a skeleton; as more data streams in, the chart fills out. To the user it feels like the chart is being assembled live, which is far better than staring at a blank panel for two seconds.

Rendering on a worker thread

Drawing happens off the main thread. The renderer runs in a worker, takes the validated spec, and produces both an interactive canvas and an exportable asset. Keeping it off the UI thread means the rest of the app stays smooth even when a chart has thousands of points. Export is essentially free, because the same renderer that draws the live preview also emits PNG and SVG.

Repair loops instead of failures

When validation fails, we do not show an error. We feed the specific problem back to the model — "the y-axis values are non-numeric" — and let it correct itself. Most issues resolve in a single repair pass, invisibly. This closed loop is why the pipeline feels robust: the user sees a working chart, not a stack trace.

What we would tell other teams

If you are building anything that turns language into structured output, resist the urge to make the model do the final rendering. Have it produce a spec, validate that spec hard, and render deterministically. You get speed, consistency, and a debugging story that does not depend on staring at generated pixels. For charts, that architecture is what makes real-time generation feel less like magic and more like infrastructure.

Turn your next idea into something real

One prompt becomes images, video, code, and charts. Start free — no credit card required.

Start free