picjs Guide
This guide takes you from first diagram to advanced features. Each section builds on the previous.
Getting Started
Browser Setup
Add picjs to your HTML page.
<script src="https://cdn.jsdelivr.net/npm/@strike48/picjs@latest/dist/picjs.umd.js"></script>
Or install as a package:
$ npm i @strike48/picjs
Or self-host by copying picjs.umd.js from the dist folder.
Automatic Code Block Processing
picjs can automatically find and render diagram code blocks, similar to Mermaid:
<pre><code class="language-picjs">
box "Hello"
arrow
box "World"
</code></pre>
<script>
// Process all picjs code blocks on page load
picjs.processCodeBlocks();
</script>
The selector defaults to code.picjs, pre > code.language-picjs. Pass a custom selector if needed:
picjs.processCodeBlocks('pre.diagram > code');
Direct API
For programmatic use:
const result = picjs.picjs('box "Hello"; arrow; box "World"');
console.log(result.svg); // SVG markup string
console.log(result.width); // Width in pixels
console.log(result.height); // Height in pixels
console.log(result.isError); // true if parsing failed
Options:
picjs.picjs(source, {
cssClass: 'my-diagram', // Add CSS class to SVG element
darkMode: true, // Invert colors for dark backgrounds
plaintextErrors: true // Return errors as plain text, not HTML
});
Tutorial
1. Hello World
The simplest diagram:
box "Hello, World!"
box "Hello, World!"
This creates a single box with text centered inside.
2. Multiple Objects
Chain objects with semicolons or newlines:
box "Hello,"
box "World!"
box "Hello,"
box "World!"
By default, objects flow to the right. Each new object appears to the right of the previous one.
3. Shapes
picjs provides these shape primitives:
Block shapes (have width, height, can contain text):
box— Rectanglecircle— Circleellipse— Ellipse (wider than tall by default)oval— Pill shape (rounded ends)cylinder— Database symboldiamond— Decision diamondfile— Document with folded corner
define show { [ down; $1 $2; $3 ]; move right 50% }
down
[
right
show(box, "a box", "box \"a box\"")
show(circle, "a circle", "circle \"a circle\"")
show(ellipse, "a ellipse", "ellipse \"a ellipse\"")
show(oval, "a oval", "oval \"a oval\"")
]
move down .3
[
right
show(cylinder, "a cylinder", "cylinder \"a cylinder\"")
show(diamond, "a diamond", "diamond \"a diamond\"")
show(file, "a file", "file \"a file\"")
]
Line shapes (connect points):
line— Plain linearrow— Line with arrowheadspline— Curved line through pointsarc— Circular arc
S: box wid 2 ht 2 invis
line from S.nw to S.ne
spline color red from S.ne to \
S.ne+(.5,-.5) to \
S.se - (.5,-.5) to \
S.se
arrow color green from S.se to S.sw
arc color blue from S.sw to S.nw
line dotted from S.n to S.s color pink
line thin dashed from S.sw to S.ne
S: box wid 2 ht 2 invis
line from S.nw to S.ne
spline color red from S.ne to \
S.ne+(.5,-.5) to \
S.se - (.5,-.5) to \
S.se
arrow color green from S.se to S.sw
arc color blue from S.sw to S.nw
line dotted from S.n to S.s color pink
line thin dashed from S.sw to S.ne
Other:
dot— Small filled circle (for marking points)move— Invisible spacertext— Text without a surrounding shape
4. Connecting with Lines
Use arrow and line to connect shapes:
box "A"
arrow
box "B"
arrow
box "C"
box "A"
arrow
box "B"
arrow
box "C"
Arrow variants:
arrowor->— Arrow pointing in travel direction<-— Arrow pointing backward<->— Arrows on both endsline— No arrowheads
5. Direction
The default direction is right. Change it with direction statements:
right
box "A"
arrow
box "B"
down
arrow
box "C"
left
arrow
box "D"
up
arrow
right
box "A"
arrow
box "B"
down
arrow
box "C"
left
arrow
box "D"
up
arrow
Directions: right, down, left, up
6. Positioning
Relative positioning with compass points
Every object has named points:
S: box wid 1.4 ht 1.4 fill 0xf0f0f0 color lightgrey thin
dot at S.s color green
text ".s .south" ".bot .bottom" with .n at last dot.s
dot at S.c same
text ".c .center" with .t at last dot.s
dot at S.n same
text ".n .north" ".t .top" with .s at last dot.n
dot at S.ne same
text ".ne" with .sw at last dot.ne
dot at S.e same
text ".e" ".east" ".right" with .w at last dot.e
dot at S.se same
text ".se" with .nw at last dot.se
dot at S.sw same
text ".sw" with .ne at last dot.sw
dot at S.w same
text ".w" ".west" ".left" with .e at last dot.w
dot at S.nw same
text ".nw" with .se at last dot.nw
S: box wid 1.4 ht 1.4 fill 0xf0f0f0 color lightgrey thin
dot at S.s color green
text ".s .south" ".bot .bottom" with .n at last dot.s
dot at S.c same
text ".c .center" with .t at last dot.s
dot at S.n same
text ".n .north" ".t .top" with .s at last dot.n
dot at S.ne same
text ".ne" with .sw at last dot.ne
dot at S.e same
text ".e" ".east" ".right" with .w at last dot.e
dot at S.se same
text ".se" with .nw at last dot.se
dot at S.sw same
text ".sw" with .ne at last dot.sw
dot at S.w same
text ".w" ".west" ".left" with .e at last dot.w
dot at S.nw same
text ".nw" with .se at last dot.nw
Lines also have .start and .end.
Reference these points to connect objects precisely:
A: box "A"
B: box "B" at 1 right of A
arrow from A.e to B.w
A: box "A"
B: box "B" at 1 right of A
arrow from A.e to B.w
Absolute positioning
Use at with coordinates:
box "Origin" at 0,0
box "Right" at 1,0
box "Up" at 0,1
box "Origin" at 0,0
box "Right" at 1,0
box "Up" at 0,1
Relative to other objects
A: box "A"
box "B" at 1 right of A
box "C" at 0.5 below A
box "D" at 1 ne of A
A: box "A"
box "B" at 1 right of A
box "C" at 0.5 below A
box "D" at 1 ne of A
7. Labels
Give objects names (labels) for later reference:
Start: box "Start"
arrow
Process: box "Process"
arrow from Process.s down
End: box "End"
arrow from Start.s to End.w
Start: box "Start"
arrow
Process: box "Process"
arrow from Process.s down
End: box "End"
arrow from Start.s to End.w
Labels must start with an uppercase letter.
8. Sizing
Set dimensions with width/wid, height/ht, radius/rad:
box "Wide" width 2
box "Tall" height 1.5
box "Rounded" rad 0.2
circle "Big" radius 0.5
box "Wide" width 2
box "Tall" height 1.5
box "Rounded" rad 0.2
circle "Big" radius 0.5
Use percentages to scale relative to defaults:
box "150% wide" width 150%
arrow right 200%
box "Normal"
box "150% wide" width 150%
arrow right 200%
box "Normal"
The fit keyword sizes a shape to fit its text:
box "Short" fit
box "A much longer label" fit
box "Short" fit
box "A much longer label" fit
9. Colors and Styling
Colors
box "Red outline" color red
box "Blue fill" fill blue
box "Both" color white fill darkgreen
box "Red outline" color red
box "Blue fill" fill blue
box "Both" color white fill darkgreen
148 named colors are supported (HTML/CSS color names).
You can also create colors programmatically:
box "RGB" fill rgb(255, 128, 0)
box "HSL" fill hsl(210, 80, 60)
box "OKLCH" fill oklch(70, 0.15, 150)
box "RGB" fill rgb(255, 128, 0)
box "HSL" fill hsl(210, 80, 60)
box "OKLCH" fill oklch(70, 0.15, 150)
rgb(r, g, b)— red, green, blue (0-255 each)hsl(h, s, l)— hue (0-360), saturation (0-100), lightness (0-100)oklch(l, c, h)— lightness (0-100), chroma (0-0.4), hue (0-360)
Line styles
line "solid" right
line "dashed" dashed right
line "dotted" dotted right
box dashed
box dotted
line "solid" right
line "dashed" dashed right
line "dotted" dotted right
box dashed
box dotted
Thickness
line thick right 1
line thin right 1
line thickness 0.05 right 1
line thick right 1
line thin right 1
line thickness 0.05 right 1
10. Text Formatting
Position
box "above" above "center" "below" below
box "above" above "center" "below" below
Alignment
box "left" ljust "center" "right" rjust fit
box "left" ljust "center" "right" rjust fit
Style
box "bold" bold
box "italic" italic
box "mono" mono
box "big" big
box "small" small
box "bold" bold
box "italic" italic
box "mono" mono
box "big" big
box "small" small
Combine them:
box "Bold Italic" bold italic fit
box "Bold Italic" bold italic fit
Command Line
The script in bib/picjs will take a markdown file containing picjs code
blocks and generate SVG files (by default in a _diagrams directory. It then
moved the original code block into an HTML comment and embed the
corresponding diagram.
If the diagram in the comment is subsequently changed, the tool will regenerate the corresponding diagram.
READMEs
The picjs tool was written to allow diagrams to be added to GitHub READMEs,
which don't allow scipts to be run.
Just run npm cli README.md, add _diagrams to your repo, and check in.
Alternatively, have a look at this projects .github/workflows for an action that does this for you on commit.
Advanced Topics
How picjs Works
picjs is a single-pass layout engine:
- Parse statements left to right
- Build objects with default positions
- Resolve constraints (at, from, to, with)
- Render to SVG
The key insight: each object's position is determined by constraints relative to previous objects. This is why order matters.
The Position Model
Current position: picjs tracks a "current position" that advances as objects are created.
Default placement: New objects appear at the current position, offset in the current direction.
Explicit placement: Use at to override default placement.
Connection points: from and to connect lines to specific points on objects.
Sublists (Grouping)
Group objects with [ ... ]:
[
box "A"
arrow
box "B"
]
arrow
[
down
box "C"
box "D"
]
[
box "A"
arrow
box "B"
]
arrow
[
down
box "C"
box "D"
]
A sublist acts as a single object with its own bounding box. Use it to:
- Create composite shapes
- Position groups relative to each other
- Isolate local coordinate systems
Variables
Store values in variables (lowercase names or starting with $ or @):
$gap = 0.5
box "A"
move right $gap
box "B"
move right $gap
box "C"
$gap = 0.5
box "A"
move right $gap
box "B"
move right $gap
box "C"
Assignment operators: =, +=, -=, *=, /=
Mathematical constants
Use $pi and $2pi for trigonometric calculations:
for angle from 0 to $2pi step 0.3 do {
dot at (0.8*cos(angle), 0.8*sin(angle))
}
for angle from 0 to $2pi step 0.3 do {
dot at (0.8*cos(angle), 0.8*sin(angle))
}
For working with degrees instead of radians, use d2r() and r2d():
for deg from 0 to 360 step 30 do {
dot at (0.8*cos(d2r(deg)), 0.8*sin(d2r(deg)))
}
for deg from 0 to 360 step 30 do {
dot at (0.8*cos(d2r(deg)), 0.8*sin(d2r(deg)))
}
Built-in variables
Built-in variables control defaults:
boxwid = 1.5
boxht = 0.75
box "Wider and shorter"
boxwid = 1.5
boxht = 0.75
box "Wider and shorter"
String Interpolation
Embed expressions in strings with ${...}:
$n = 42
box "Value: ${$n}"
A: box "A"
box "Width: ${A.wid}"
$n = 42
box "Value: ${$n}"
A: box "A"
box "Width: ${A.wid}"
Macros
Define reusable patterns:
define roundbox { box rad 0.15 $1 }
roundbox("First")
arrow
roundbox("Second")
arrow
roundbox("Third")
define roundbox { box rad 0.15 $1 }
roundbox("First")
arrow
roundbox("Second")
arrow
roundbox("Third")
The $1, $2, etc. are positional parameters.
For Loops
for i from 1 to 3 do {
box "Box ${i}"
arrow
}
for i from 1 to 3 do {
box "Box ${i}"
arrow
}
With step:
for i from 5 to 10 step 2 do {
box "${i}"
move
}
for i from 5 to 10 step 2 do {
box "${i}"
move
}
Chop
The chop keyword shortens lines so they don't overlap shapes:
A: circle "A"
B: circle "B" at 2 right of A
arrow from A to B chop
A: circle "A"
B: circle "B" at 2 right of A
arrow from A to B chop
Without chop, the arrow would extend into the circles.
The same Keyword
Copy attributes from a previous object:
box "Template" color blue fill lightblue rad 0.1 fit
box "Copy 1" same
box "Copy 2" same
box "Template" color blue fill lightblue rad 0.1 fit
box "Copy 1" same
box "Copy 2" same
Or reference a specific object:
A: box "A" color red
B: box "B" color blue
box "Like A" same as A
A: box "A" color red
B: box "B" color blue
box "Like A" same as A
Invisible Objects
Use invis or invisible to create spacing without visible output:
box "Visible"
box invis
box "Also visible"
box "Visible"
box invis
box "Also visible"
Useful for alignment and layout control.
Tips
- Start simple: Get basic layout working before adding styling
- Use labels: Name important objects for easier reference
- Use fit: Let picjs size boxes to fit text
- Use variables: Store repeated values
- Use sublists: Group related objects
- Test incrementally: Add one thing at a time
Next Steps
- Reference: Complete language specification
- Playground: Experiment interactively