Hand-drawn diagrams from plain text. 57% fewer tokens than Mermaid.
Scrawl is a compact, line-oriented diagram format that renders to hand-drawn SVGs. Designed for LLM generation, documentation, and anywhere you want diagrams that feel human.
Version 0.5.0 adds chart mode, explicit sequence branching with fork / join, note callout leaders, and richer structured-workflow examples.
lr flowchart LR
push(Git Push)~blue->ci(Run Tests) push[Git Push]:::blue --> ci[Run Tests]
ci->build(Build)|pass ci -->|pass| build[Build Image]
ci=>fail(Notify)~red|fail ci -.->|fail| fail[Notify]:::red
build->gate{Approve?} build --> gate{Approve?}
gate->prod(Deploy)~green|yes gate -->|yes| prod[Deploy]:::green
gate=>fail|no gate -.->|no| fail
classDef blue fill:#3182ce33,stroke:#3182ce
~57 tokens classDef green fill:#38a16933,stroke:#38a169
classDef red fill:#e53e3e33,stroke:#e53e3e
~129 tokens
Same diagram. 56% fewer tokens. Colors are inline (~blue), not separate classDef blocks. Edge labels are inline (|pass), not -->|pass|. Dashed edges are =>, not -.->.
source
lr
push(Git Push)~blue->ci(Run Tests)
ci->build(Build Image)|pass
ci=>fail(Notify Team)~red|fail
build->staging(Deploy Staging)~orange->gate{Approve?}
gate->prod(Deploy Prod)~green|yes
gate=>fail|no
source
td
gw(API Gateway)~purple->{auth(Auth Service),users(User Service),orders(Order Service)}
users->db_u[(Users DB)]~orange
orders->db_o[(Orders DB)]~orange
orders->queue:Event Bus~blue|emit
queue=>notify(Notify Service)|subscribe
source
td
user((User))->login(Login Form)~blue->check{Valid?}
check->mfa(MFA Challenge)~orange|yes
check=>lock(Lock Account)~red|no
mfa->token(Issue JWT)~green|valid
mfa=>lock|invalid
source
lr
client((Client))->lb(Load Balancer)~blue->mw(Middleware)~purple->router{Router}
router->ctrl(Controller)~blue|matched
router=>error(Error Handler)~red|unmatched
ctrl->svc(Service Layer)->repo(Repository)->db[(Database)]~orange
| sketch | rough | clean |
|---|---|---|
| architect | blueprint |
|---|---|
source
wireframe
style rough
screen app:Dashboard 1360x940
header top:Product Header
text top_nav_overview:Overview
text top_nav_customers:Customers
text top_nav_billing:Billing
button invite:Invite
sidebar side_nav:Main Nav
list menu:Navigation
column content:Dashboard
row stats:Stats
card revenue:Revenue
card mrr:MRR
card churn:Churn
row body:Body
panel signup:Signup Flow
input name:Full Name
input email:Email
textarea notes:Notes
button create:Create User
panel preview:Preview
image hero:Wireframe Preview
text copy:Marketing Copynpm install -g scrawl-cli
echo 'lr\na(Start)~blue->b(End)~green' | scrawl > diagram.svgOr use as a library:
npm install scrawl-coretd # direction: td, lr, rl, dt
a:Label->b:Label # edge with labels
a(Rounded) # shape: () rounded
a((Circle)) # shape: (()) circle
a{Diamond} # shape: {} diamond
a[(Cylinder)] # shape: [()] cylinder
a->b|edge label # edge label after pipe
a=>b # dashed edge
a..>b # dotted edge
a<->b # bidirectional
a->{b,c,d} # fan-out
a~blue # color: red blue green yellow purple orange pink gray teal cyan
[Group Name: a b c] # group nodes
# this is a comment
Use wireframe as the first line to switch from graph layout to UI sketch layout.
wireframe
screen landing:Landing Page 1440x960
header top:Header
text brand:Acme
button cta:Get Started
column hero:Hero
text headline:Big Promise
row actions:Actions
button primary:Start Trial
button secondary:Talk to SalesSupported first-pass wireframe components:
screenheadersidebarrowcolumnpanelcardbuttoninputtextareaimagetextlist
Wireframe flows can also take explicit route turns when auto-routing is not enough:
wireframe
screen desk:Desk 1280x900
card start:Start
modal confirm:Confirm
flow start -> confirm route=right,down,left | guided
flow confirm -> start turns=up left left
flow review -> publish route=left*2,down:140,right | long detourRoute actions are absolute orthogonal directions: up, down, left, right.
left*2repeats the default step twicedown:140uses an explicit pixel distance- plain
left/downstill use the default step
Use sequence to render long ordered step lists as chained blocks. Add wrap=N to bend the sequence into serpentine rows instead of one long strip.
sequence wrap=4 snake=horizontal rowgap=90 colgap=28
style architect
brief:Brief->draft:Draft->review:Review->revise:Revise->approve:Approve->package:Package->publish:Publish->measure:MeasureYou can still declare one step per line, but graph-style chains like A->B->C->D work directly. With wrap=4, the first four steps flow left-to-right, the next four flow right-to-left, and row transitions connect vertically at the edge.
Header options:
wrap=Nlimits how many steps go on one row before the serpentine turnsnake=horizontal|verticalchooses whether the snake advances by rows or by columnsrowgap=Nincreases or tightens spacing between rowscolgap=Nincreases or tightens spacing between columns
Transition labels also work inline on chained sequence edges:
sequence wrap=3
triage->debug|investigate->fix|patch->shipUse phase and lane to mark semantic sections. They render as labeled group regions behind the relevant steps:
sequence wrap=3 snake=vertical
phase setup:Setup
A->B->C
lane review:Review Lane
C->D->EUse notes for annotations that should stay attached to a step without becoming part of the main chain:
sequence wrap=3
A->B->C
note right of B:Wait for reviewer
note over C:Deploy windowSequence notes now avoid overlapping nearby step boxes, which matters most in wrapped fork/join flows.
Use fork and join when the process fans out into parallel review lanes and later reconverges:
sequence wrap=3 snake=horizontal rowgap=100 colgap=26
phase intake:Intake and triage
intake:Intake->draft:Draft
fork draft -> legal:Legal Review, security:Security Review
lane release:Release lane
join legal, security -> approve:Approve
approve->ship:Ship
note right of approve:Final sign-off\nand release window
note over security:Parallel checks stay visibleUse break when you want to force a new row before the next step:
sequence wrap=4
triage:Triage->debug:Debug->fix:Fix
break
verify:Verify->ship:ShipUse chart as the first line to render lightweight hand-drawn charts without dropping to coordinates.
Supported chart kinds:
barlineareascatterpie
Bar, line, and area charts use numeric series values:
chart
style blueprint
kind bar
title Revenue by Quarter
xlabel Quarter
ylabel Revenue
categories Q1, Q2, Q3, Q4
series Revenue: 12, 18, 15, 22
series Plan: 10, 14, 16, 20Supported chart kinds:
- Axis and combo:
bar,line,area,scatter,combo,waterfall,dot,box,tornado,likert - Slice and radial:
pie,donut,radar,radial-bar,gauge - Matrix and hierarchy:
heatmap,treemap,sunburst - Flow and stage:
sankey,funnel
Common chart controls:
legend right|top|bottom|nonegrid none|x|y|bothpoints show|hide|autolabels show|hide|autocurve linear|smooth|stepstack grouped|stacked|percentxticks N,yticks N,y2ticks Nxmin,xmax,ymin,ymax,y2min,y2maxref x|y|y2 ...annotate x,y: Label
Series lines can carry per-series options:
series Revenue [type=bar color=#2563eb]: 12, 18, 24
series Conversion [type=line axis=right color=#16a34a curve=smooth labels=show]: 2.1, 2.8, 3.4Example combo chart with a secondary axis, target line, and annotation:
chart
style blueprint
kind combo
title Revenue vs Conversion
xlabel Month
ylabel Revenue
legend top
grid both
labels auto
y2ticks 4
categories Jan, Feb, Mar, Apr
ref y 20 label=Target color=#ef4444
annotate Mar,24:Peak color=#0f172a
series Revenue [type=bar color=#2563eb]: 12, 18, 24, 28
series Conversion [type=line axis=right color=#16a34a curve=smooth labels=show]: 2.1, 2.8, 3.4, 3.9Stacked and percent-stacked bars/areas use stack stacked or stack percent:
chart
style architect
kind bar
stack percent
title Revenue Mix by Quarter
ylabel Revenue Share
legend top
grid y
categories Q1, Q2, Q3, Q4
series Product: 12, 16, 18, 22
series Services: 8, 9, 11, 14
series Support: 4, 5, 6, 7Scatter charts use x,y point pairs separated by semicolons:
chart
kind scatter
title Activation vs Retention
xlabel Activation
ylabel Retention
series Cohort A: 12,34; 18,29; 24,41; 30,48
series Cohort B: 10,22; 16,26; 20,24; 28,33Pie and donut charts use categories plus one series of slice values, or multiple one-value series:
chart
style rough
kind donut
title Revenue Mix
legend right
categories Product, Services, Support, Training
series Mix: 40, 30, 20, 10Special data forms:
- Heatmap cells:
cell Row,Column: 91 - Sankey flows:
flow leads -> demo: 48 - Treemap / sunburst hierarchy:
item Product/API: 32 - Gauge thresholds:
threshold 85 #f59e0b Watch
Gauge charts render on a centered upper arc so threshold bands and the needle stay aligned inside the frame.
One input, five visual styles — same diagram (a(Start)~blue->b{Check}->c(Done)~green / b=>d(Error)~red):
| sketch (default) | rough | clean |
|---|---|---|
| wabi-sabi | art brut | geometric |
| architect | blueprint |
|---|---|
| drafting | engineering |
import { renderDiagram } from 'scrawl-core'
const svg = renderDiagram('lr\na->b->c', { style: 'architect' })scrawl render diagram.scrawl --style architect > diagram.svgEach preset controls roughness, bowing, stroke width variation, arrowhead style, text wobble, edge curvature, double-line mode, and corner overshoot. Per-element seed derivation ensures every shape has unique character while remaining deterministic.
| Package | Description |
|---|---|
| Package | Install |
| --------- | --------- |
scrawl-core |
npm i scrawl-core |
scrawl-cli |
npm i -g scrawl-cli |
remark-scrawl |
npm i remark-scrawl |
scrawl-web |
— |
scrawl-vscode |
— |
Markdown docs — remark-scrawl plugin renders code blocks as inline SVGs:
```scrawl
td
a(Start)->b{Check}->c(Done)~green
b=>d(Error)~red
```LLM prompts — 57% fewer tokens means cheaper, faster, and fits more context.
CI pipelines — deterministic output: same input always produces the same SVG. Diffable in version control.
VS Code — live preview as you type.
| Diagram | scrawl | Mermaid | Savings |
|---|---|---|---|
| CI/CD Pipeline | ~57t | ~129t | 56% |
| Microservices | ~77t | ~153t | 50% |
| User Auth Flow | ~51t | ~133t | 62% |
| Git Branching | ~55t | ~162t | 66% |
| React Component Tree | ~50t | ~146t | 66% |
| Database Schema | ~64t | ~167t | 62% |
| Network Topology | ~66t | ~152t | 57% |
| Bug Triage | ~70t | ~134t | 48% |
| API Lifecycle | ~82t | ~139t | 41% |
| How Scrawl Works | ~60t | ~164t | 63% |
| Average | 57% |
- Parse — zero-dependency recursive descent parser converts text to a typed IR
- Layout — dagre computes node positions and edge routing
- Render — rough.js draws hand-drawn shapes, edges with Bezier smoothing, and text with per-character wobble
- Seed — djb2 hash of source content ensures deterministic output
Contributions welcome. This is a pnpm monorepo with Turborepo:
pnpm install
pnpm turbo build
pnpm turbo testMIT