Updated June 11, 2026

How to make a Drafty canvas live

Claude writes the report once. A plain cron keeps it current, readers act on it with a click — and no model runs in either loop.

A Drafty canvas is usually a snapshot — Claude makes it once, you share the link, and it shows what was true the moment it was published. For a plan, a draft, a design, that's exactly right.

But some canvases want to be alive. A metrics dashboard, a funnel, a status board — the link should always show current numbers, not the ones from last Tuesday. Drafty calls these live canvases, and being live turns out to mean two things: the page keeps itself current, and the people reading it can act on it — right there, with a click.

Snapshot
1,284
signups
as of last Tuesday
Live
1,902
signups
updated 2 min ago
Same link, two jobs. A snapshot freezes the numbers the moment it's published; a live canvas always shows what's true right now.

The obvious way to keep it current is to have your agent re-run the report and re-publish. That works. But if you reach for /loop or a scheduled Claude run to do it, you're paying a model to do a job that needs no thinking.

Here's the honest frame: Claude is the author — it writes the query and the layout once. A plain cron is the engine — it runs that script on a timer, forever. The refresh itself is mechanical, so there's no reason for Claude to be in the loop at run time.

A refresh is mechanical

Writing the report is the smart part. Deciding what to pull, how to aggregate it, what the layout should be, which numbers are bot noise and which are real — that's judgement, and it's exactly what Claude is good at. You do it once.

Running the report is not judgement. It's the same three steps every time: query the data, render the HTML, push the new version. A script does that identically, every time. Nothing about it benefits from a language model.

So the moment you've nailed the report, take Claude out of the loop.

Don't pay a model to run a cron

It's tempting to keep a /loop going, or schedule a claude -p job, to do the refresh. Don't. Both re-invoke the model on every run — they spin up a session, burn tokens, and need something keeping them alive. For a job that's pure execution, that's the wrong tool and a real cost.

A plain operating-system cron has none of that. It runs a shell command on a timer and goes back to sleep. No model, no session, no credits.

A model in the loop
/loop or claude -p — spins up a session and burns tokens on every single run.
$ / run
A plain cron
Runs one shell command on a timer, then goes back to sleep. No model, no session.
free
Both keep the link current — but only one needs a language model running to do it. For pure execution, the cron is the right tool.

Author once, run forever

The split is the whole idea:

You loop Claude back in when the definition changes — not for refreshes.

Claude — the author
Writes the query and the layout once: what to pull, how to render it, which numbers are noise. The smart part — runs only when the report needs to change.
A plain cron — the engine
Runs the script on a timer — pull, render, push. The mechanical part — no model, no session, no credits. Forever.
Claude writes the loop once; a dumb local cron runs it forever.

How the loop actually works

The script does three things, in order: pull the data, render a self-contained HTML canvas, and push it to the same link. The one refinement worth adding is to only push when the numbers actually moved — hash the rendered output (ignoring the "generated at" line), compare it to the last run, and skip the push if nothing changed.

And refreshes don't clutter history. On a live canvas a data refresh is a tick: it updates the page (anyone watching sees it reload in place) but creates no version. History shows the changes someone authored — pushes, edits, restores — plus one daily snapshot as a safety net, with a green heartbeat line telling you the canvas is live and when it last ticked. The skip-if-unchanged guard still earns its keep: it saves the pointless work of pushing identical bytes on a tight schedule.

every 5 min
1,8841,902
signups
query → renderquery → renderdata moved — pushedno change — skipped
No model in the loop — just query → render → push. An unchanged run skips the push, so a tight schedule never spams version history.

Introducing live canvases

New in Drafty
Live canvases
Tell Claude to keep a canvas live. It writes the refresh, runs it on a timer, and the page stays current — readers can even act on it — with no model in the loop.

Everything above is the idea. Live canvases are that idea, built in — the refresh engine (the plugin calls it drafty-cron) ships in the Drafty plugin, so the Claude you're already working with knows how to do it.

You don't write the script or touch a scheduler. You ask, in the same chat you're already in, and Claude wields the rest: it authors the refresh, installs the timer, and reports back. The "only re-publish when the numbers moved" guard is baked in, so a tight schedule never spams your version history.

Keep the metrics canvas refreshed every 5 minutes.
On it. It'll refresh every 5 minutes and only re-publish when the numbers change.
what's refreshing right now?
Just one: metrics, every 5 min — last run pushed 2 minutes ago.
make it every 15 instead, and stop the funnel one
Done — metrics is on 15 minutes now, and the funnel refresh is removed.
You never touch a terminal. The only one using the CLI is the Claude agent you're already chatting with — it wields the drafty-cron skill for you.

Managing it is just more conversation — "what's refreshing right now?", "make it every fifteen minutes", "stop updating the funnel one." Each maps to the skill behind the scenes; you never see a command. The CLI has exactly one user, and it isn't you — it's the agent.

What actually leaves your machine

There's a quieter benefit to keeping the engine local: the only thing that ever leaves is the finished page.

Stays on your machine
  • BigQuery access
  • Credentials & API keys
  • The SQL query
  • Raw rows
Goes to the link
1,902
signups
Only the finished page is published. The data access behind it — credentials, query, raw rows — never leaves your machine.

The script runs on your machine, with your credentials, your query, your raw rows — and none of that is uploaded. What gets pushed to the link is the rendered HTML: the same numbers a person would see, and nothing behind them. The canvas isn't a live wire into your warehouse; it's a static snapshot. Opening the link runs no query and touches no database, so a viewer sees exactly the figures you chose to publish — with no way to pivot, drill, or reach anything you didn't render.

That's the real payoff of Claude authoring the report: it decides what to roll up and what to leave out, so the sensitive rows stay in the query while only the aggregate ships. And if even the finished numbers shouldn't be public, the canvas can be private — owner-only or invite-only — so the link reaches the people you choose and no one else.

Run it as often as you want

Because there's no model in the loop, each run is essentially free — a cheap query and a file write. So don't be precious about the cadence: every five minutes is fine. The only real costs are the data query (keep it cheap) and version history (handled by the skip-if-unchanged guard).

Match the interval to how fast the data moves. A dashboard that changes a few times a day doesn't need a five-minute cron — but it costs nothing to have one.

Let readers act on it

A dashboard you can only look at leaves the last step manual: you see the new signup, the fresh feedback note, the failing check — and then go do something about it somewhere else, or tell someone to. A live canvas closes that step on the page itself.

Hover any repeating item (tap on your phone) and hit ✓ Done. The item dims immediately, a small mark is stored against that data row — attributed, undoable — and on the next tick the refresh script reads the marks back and drops the row. Triaged, on the page, by the person reading it.

live canvas⟳ next tick
✓ Done
marked done — dropped on the next tick
Acting on the report is pure data too — no agent wakes up, no tokens burn, and the mark survives every refresh.

Notice what didn't happen: no agent woke up, no tokens burned. Marking is pure data, the same way the refresh is pure execution — the no-model rule holds on both sides of the page. For the script author it's one extra line: read the done marks back (drafty marks ls) and exclude those rows before rendering. Each item just needs a stable data-key from your data source's row id, so marks survive every re-render.

And when the feedback is about the report rather than a row — "exclude bot traffic", "add a week-over-week column" — that's a change to the definition, which is Claude's half of the split. Open the dock and hit Tell Claude: your instruction becomes a canvas-level thread that lands in the inbox your agent already watches, and the next authored push carries the change. That's the "loop Claude back in" moment made concrete — readers handle the rows, Claude handles the report, the cron handles everything in between.

One honest caveat

A local cron runs on your machine, which means it runs while your machine is awake. It survives reboots and logging out; it doesn't run while the laptop is powered off, and if it sleeps through a scheduled run it catches up once on wake.

For most dashboards — something you glance at through the day — that's exactly enough. If you need it fresh while the laptop's closed in a bag, that's the one case for hosting the loop in the cloud instead: same script, different runner.

Get started

Pick a canvas that should never be stale — a dashboard, a funnel, a status board — and ask Claude to keep it live. From then on the link updates itself, the people reading it act on it right there, and Claude comes back only when the report itself needs to change — one Tell Claude away.

Related help