A vector editor with an API

Most vector editors have no API. The ones that do have a scripting layer: a second implementation, maintained by a different team, exposing a different subset of the verbs, on a different release cycle. It drifts, then it rots, then it gets deprecated. This has happened to almost every scripting layer ever shipped, and it happened for a structural reason — the moment your API is a separate implementation of the product, it is guaranteed to disagree with the product.

justdraw.fyi has one implementation, and everything is a client of it.

keyboard ─┐
mouse ────┤
CLI ──────┤──> one command ──> the editor ──> your drawing
agent ────┘

Press Ctrl+G and the keyboard sends group.create. Click the group button and the toolbar sends group.create. Run jd group create and the CLI sends group.create. Have a language model decide two shapes belong together and it sends group.create. Four seats, one steering wheel. The API cannot drift from the product, because it is the product.

From a shell

p=$(jd proj create logo)              # prints the project id
a=$(jd draw "$p" rect 0 0 120 80)     # every command prints the new id
b=$(jd draw "$p" circle 60 40 25)
jd style "$b" fill=#5b7cfa
g=$(jd group create "$a" "$b")
jd align vcenter "$g" "$wordmark"
jd export "$p" logo.svg

Ids pipe from one command into the next, which is the oldest idea in Unix and the reason a shell pipeline works at all. An element id works anywhere a project id is expected — the server finds the project it belongs to.

Over HTTP

curl -sX POST https://app.justdraw.fyi/api/v1/agent \
  -H 'Content-Type: application/json' \
  -H 'Origin: https://app.justdraw.fyi' \
  -d '{"cmd":"element.draw","project":"<uuid>",
       "args":{"shape":"rect","x1":0,"y1":0,"x2":40,"y2":30,"fill":"#5b7cfa"}}'

No API key. No account. A project is created with one unauthenticated POST, and the UUID it returns is the capability — whoever holds it can edit that drawing.

The whole command surface — draw, style, move, clone, delete, group, ungroup, align, distribute, order, export — is on the agent protocol page.

The constraint, stated plainly

A drawing command needs the project open in a browser. With no tab, you get a 409.

That looks like a bug and it is the central design decision. The editor in the browser already knows what “align these three shapes” means: it has the geometry, the layers, the groups, the undo stack, the renderer. If the server computed alignment too, there would be two implementations of alignment, they would disagree, and the day they disagreed is the day a customer found out.

So the server doesn’t compute. It relays the command down the WebSocket the open editor holds, the editor does what it does when you press the align button, and the answer comes back. Which also means that when your script recolours a shape, whoever is looking at that tab watches it turn blue.

Commands that don’t touch the geometry don’t need a tab: proj.create, proj.list, proj.show, proj.rename, proj.destroy, and doc.export to YAML are answered from the database. You can mint a project and dump it to YAML from a cron job with no browser anywhere.

When this is the wrong tool

If you need to generate ten thousand SVGs headlessly with no human anywhere, the browser-as-engine constraint is a tax you shouldn’t pay — use a rendering library. This is built for drawings a person is going to look at and keep working on, with a machine helping.

Make a drawing and point something at it.

justdraw.fyi is a vector editor that opens in a tab. No account, nothing to install, and every drawing is a link you can send to anyone.

← All posts