picjs Language Reference

Complete reference for the picjs language

Program Structure

A program is a sequence of statements separated by whitespace (including newlines). There are no statement terminators.

Statement Statement ...

Statements include: shapes, assignments, shape default setters, animations, if/else, ternary conditionals, group expressions, and inspect (??).

Comments

Line comments start with // and extend to the end of the line.

// This is a comment
Box "hello"  // inline comment

Shapes

Every shape accepts common options and an optional constraint.

Box

Box [options...] [with ...]

Draws a rectangle. Accepts size and corner radii options.

Box "Hello"
Box "Rounded" rx 0.1 ry 0.1
Box 2 x 1 "Wide"
Box wid 3 ht 2 "Sized"

Circle

Circle [options...] [with ...]

A circle. Accepts a single radius option.

Circle "Node" radius 0.8
circle fill ~red

Ellipse

Ellipse [options...] [with ...]

An ellipse with independent horizontal and vertical radii. Accepts corner radii options (rx, ry).

Ellipse "Wide" rx 1.2 ry 0.4
ellipse rx 0.7 ry 0.3 fill ~blue

Oval

Oval [options...] [with ...]

A rectangle with fully rounded ends (pill shape). The corner radii default to half the smaller dimension. Accepts size and corner radii options.

Oval "Button"
oval wid 2 ht 0.5 fill ~green

Abbreviated forms: box, circle, ellipse, oval, arc, line (lowercase) are accepted for all shape keywords.

Line

Lines connect two points. They can be specified with from/to positions, or with just one endpoint (the other defaults to the current layout position).

Line from <position> to <position> [options...]
Line from <position> [options...]
Line to <position> [options...]
Line [options...]

Lines can also be written using arrow abbreviations instead of Line:

Abbreviation Meaning
-> straight line with end arrow
<- straight line with start arrow
<-> straight line with both arrows
-- straight line, no arrows
~> smooth (curved) line with end arrow
<~> smooth line with both arrows
~~ smooth line, no arrows
o-> dot start, arrow end
|-> bar start, arrow end

The general pattern is [start][path][end] where:

  • start: < o | or nothing
  • path: - (straight) or ~ (smooth)
  • end: > o | or nothing

Accepts line endings, line shape, line length, and line label options.

-> "sends"
Line from a.s to b.n
<~> from (0,0) to (3,3) "curved"
line -> from db to server "query" below

Polyline

A polyline is a multi-segment line. Created by adding then waypoints to a line.

Line from <pos> then <pos> [then <pos>...] [close] [options...]
Line from <pos> to <pos> then <pos> [then ...] [close] [options...]

Waypoints can be absolute positions or directional:

line -> from a.s then south 2 then east 3 then to b.w
line -> from a.e then east 2 then south until level with b then to b.w

Directional waypoints use cardinal directions (north, south, east, west, n, s, e, w, and combinations) followed by a distance.

The then ... until [even/level with] <target> form extends in a direction until aligned with a target position.

Add close to close the polyline into a polygon.

Polylines accept corner radii for rounded corners at waypoints.

Arc

Arc from <position> to <position> [options...]
Arc [options...]

Draws a circular arc between two points. Accepts turn direction and line endings.

Arc from (0,0) to (2,2) cw
Arc from a.n to b.s "label" turn ccw

Label

Label <expr> [options...] [with ...]

A standalone text label. The expression is the text content. Accepts text options.

Label "Title" .h1
Label "Subtitle" fill ~blue

Layout

Shapes are placed automatically based on the current layout direction and cursor position.

Face

Changes the layout direction. Subsequent shapes will be placed in the new direction.

Face <cardinal>      // Face n, Face se, Face east, ...
Face <angle>         // Face 45

Gap

Inserts spacing between shapes in the layout direction.

Gap                  // default gap
Gap <distance>       // specific distance
Gap <cardinal>       // gap in a specific direction
Gap <cardinal> <distance>
Gap same             // same gap as last time

Goto

Jumps the layout cursor to a new position.

Goto                 // reset to default
Goto <position>      // absolute position: (x, y) or shape reference
Goto <cardinal>      // direction from current position
Goto <cardinal> <distance>
Goto <distance>      // distance in current direction

Groups

Group

Groups collect shapes and apply a shared coordinate space. The group can be positioned, rotated, and styled as a unit.

{ body } [options...] [with ...]
Group { body } [options...] [with ...]

Inside a group, use self.<name> to name shapes for external reference.

g = {
  Face s
  self.top = Box "A"
  -> "connects"
  self.bottom = Box "B"
} with .nw at (0, 0)

Line from g.top.e to other.w

Groups accept common options (fill, stroke, rotation, etc.) and constraints. Options can appear before or after the with clause.

Aside

Like a group, but shapes inside do not affect the layout cursor or become implicit line endpoints.

Aside { body }

Shape Options

Common Options

These options are available on most shapes:

Option Description
"text" Add a label (see Labels)
rotation <angle> Rotate in degrees
rotation <angle> about <position> Rotate around a point
at <position> Place at a specific position
(<x>, <y>) Place at coordinates
x <expr> Set x position
y <expr> Set y position
fill <color> Fill color
stroke <color> Stroke color
thickness <n> Stroke width
solid / dotted / dashed Line style
opacity <n> Opacity (0–1)
fit Auto-size to fit content
same Copy attributes from previous shape of same type
behind <shape> Render behind another shape
.<class> Apply a CSS class

Abbreviations: rot for rotation, ht for height, wid for width, thick for thickness, len for length, rad/r for radius, step for stepped, curve/curved for smooth.

Labels

Most shapes accept one or more string labels. A bare string after a shape keyword becomes a label:

Box "Hello"
Box "Line 1" "Line 2"

Rich Labels

A rich label is a parenthesized expression with optional styling:

Box ("dynamic: #{count}" fill ~red .highlight 14pt)
-> ("label" fill ~green) ("other" fill ~blue)

Rich label options: fill, stroke, .<class>, font size.

Line Labels

Labels on lines can be positioned along the path and placed above or below:

-> "centered"                          // default: 50%, centered
-> "start" at 0% "end" at 100%        // at specific percentages
-> "top" above "bottom" below         // above/below the line
-> "25%" at 25% outside               // at 25%, outside curve

Position keywords: above, below, inside, outside.

Positioning

at <position>        // at a named position or expression
(<x>, <y>)           // at explicit coordinates
x <expr>             // set x only
y <expr>             // set y only

Size

Box and Oval size options:

<width> x <height>   // e.g., 2 x 1
width <expr>         // or wid <expr>
height <expr>        // or ht <expr>

Radius

Circle radius (sets both dimensions equally):

radius <expr>        // or rad <expr> or r <expr>

Corner Radii

Box, Ellipse, Oval, and Polyline corner rounding:

rx <expr>            // horizontal corner radius
ry <expr>            // vertical corner radius
radius <expr>        // set both rx and ry (on polylines)

Fill and Stroke

fill <color>
stroke <color>
thickness <expr>     // or thick <expr>
solid
dotted
dashed

Rotation

rotation <angle>                    // or rot <angle>
rotation <angle> about <position>   // rotate around a point

Line Endings

Lines and arcs accept endpoint markers:

[start][path][end]

Start markers: < (arrow), o (dot), | (bar) End markers: > (arrow), o (dot), | (bar) Path style: - (straight), ~ (smooth)

Special: -- is a straight line with no markers, ~~ is a smooth line with no markers.

Line Shape

Override the path interpolation:

straight             // straight line segments (default)
stepped              // or step — axis-aligned right-angle segments
smooth               // or curve/curved — bezier curve

Line Length

length <expr>        // or len <expr>

Turn Direction

Arc turn direction:

cw                   // clockwise
ccw                  // counter-clockwise
turn cw              // same, with keyword
turn ccw
turn <angle>         // explicit angle

Text Options

Label and text formatting:

align .<cardinal>    // text alignment: .n, .nw, .ne, .w, .e, .c, etc.
maxwidth <n>         // maximum text width before wrapping
line_height <n>      // line spacing
font <font-spec>     // CSS font specification (see Font Specification)
<font-size>          // e.g., 14pt, 2em, large

Other Options

nodraw               // create line but don't draw it (draw_progress = 0)

Constraint

Position a shape by pinning one of its cardinal points to a location:

with .<cardinal> at <place>
with at <place>                       // defaults to center
with self.<name>.<cardinal> at <place> // pin a named sub-element
Box "A" with .nw at (0, 0)
Box "B" with .e at other.w

CSS Class

.<class-name>

Applies a CSS class to the shape's SVG element. Multiple classes can be stacked.

Box "styled" .highlight .large

Class-qualified shape defaults use the same dot syntax:

Box.highlight.fill = ~yellow

Shape Defaults

Set default attribute values for a shape type:

<Shape>.<attr> = <expr>
<Shape>.<class>.<attr> = <expr>

Read a shape default:

<Shape>.<attr>
<Shape>.<class>.<attr>

Shape (capitalized) refers to the base shape class.

Box.fill = ~lightblue
Box.highlight.fill = ~yellow
Circle.radius = 0.5

Assignments

<target> = <expr>
<target> += <expr>
<target> -= <expr>
<target> *= <expr>
<target> /= <expr>
<target> %= <expr>

Targets can be simple identifiers, qualified paths (obj.attr, list[index]), or the timeline shorthand @ (equivalent to @.now).

x = 10
$name = "hello"
@ = 5                // sets @.now = 5
shape.fill = ~red
$list[0] = "first"

Expressions

Operator Precedence

Lowest precedence first (loosest binding at top):

Precedence Category Operators
1 Logical OR ||
2 Logical AND &&
3 Equality == !=
4 Relational < > <= >=
5 Additive + -
6 Multiplicative * / %
7 Power ^
8 Unary + - !

Access and Calls

<expr>(<args>)       // function call
<expr>[<index>]      // index access
<expr>.<attr>        // attribute/property access

These can be chained: $list.map(fn)[0].x

Conditionals

if (<expr>) <body> else <body>
if (<expr>) <body>

The body can be a single expression or a block { ... }.

Functions

Arrow function syntax:

(<params>) => <body>
<name> => <body>
=> <body>                // no parameters
() => <body>             // explicit empty params

The body can be a single expression or a block { ... }. Functions return the value of their last expression (implicit return).

$double = n => n * 2
$add = (a, b) => a + b
$greet = => Box "Hello"
$complex = (x) => {
  y = x * 2
  y + 1
}

Value Types

Number

Integer, decimal, or scientific notation. Append % to divide by 100.

42       3.14      .5       1e3      50%      1.5e-2

Boolean

true     false

Color

Colors in various formats:

Format Example
Named color ~red, ~darkolivegreen
Hex (3/4 digit) #f00, #f008
Hex (6/8 digit) #ff0000, #ff000080
Color model rgb(255, 0, 0), hsl(0, 100, 50)
Dynamic color ~#{expr}

Named colors can be one of the standard web color names, a foreground or background palette color (~f1..~f8, ~b1..~b8), or ~none (for transparent).

Color models: rgb, hsl, hsv, oklch (case-insensitive, optional a suffix for alpha variant).

Model Parameters
rgb / rgba r (0–255), g (0–255), b (0–255) [, alpha (0–1)]
hsl / hsla h (0–360), s (0–100), l (0–100) [, alpha (0–1)]
hsv / hsva h (0–360), s (0–100), v (0–100) [, alpha (0–1)]
oklch / oklcha L (0–100), C (0–0.4), h (0–360) [, alpha (0–1)]

Dynamic colors evaluate an expression to produce a color name at runtime:

$name = "red"
fill ~#{$name}           // equivalent to fill ~red

String

Single-quoted and double-quoted strings. Double-quoted strings support interpolation.

Syntax Interpolation Escape sequences
'...' No Yes
"..." Yes Yes
'''...''' No No
"""...""" Yes No

Interpolation (double-quoted only): #{expr} embeds an expression. Use ## for a literal #.

"Hello #{name}"
"Total: #{a + b}"
"Price: ###{price}"     // outputs: Price: #42

Escape sequences: \\, \', \", \n, \t, \r, \b, \f, \v, \0, \xHH, \uHHHH.

Triple-quoted strings can span multiple lines and don't process escape sequences (except interpolation in """).

Position

An (x, y) coordinate pair:

(100, 200)
(x + 10, y)
(0, 0)

The comma between x and y is optional.

Array

[1, 2, 3]
["hello", "world", 42]
[~red, ~blue, ~green]
[]

Elements of an array can have different types.

Commas between elements are optional.

Range

An inclusive range between two values:

[1..10]
[0..n-1]
["A".."Z"]
[~red..~blue]

Ranges support interpolation (see Range methods) and can be used with .each(), .map(), .steps().

Timeline

The @ symbol accesses the animation timeline object.

Expression Description
@ Read current time (shorthand for @.now)
@.now Current time
@.max_time Maximum time in the timeline
@.last_animation_start Start time of most recent animation
@.last_animation_end End time of most recent animation
@.start_from Time offset for next animation
@@ Set @ to the end of the last animation

Assignment: @ = <expr> sets @.now. @.start_from = <expr> is also settable.

Palette

The built-in Palette object manages themed color sets.

Property Description
Palette.current Current palette name (read/write)
Palette.names List of available palette names
Palette.b1Palette.b8 Background colors (read/write)
Palette.f1Palette.f8 Foreground colors (read/write)
Palette.current = "ocean"
Box fill ~b3                 // use palette background color 3
Palette.f1 = ~red            // override foreground color 1

Function

Arrow functions (see Functions under Expressions).

n => n * 2
(x, y) => x + y
=> Box "hello"
(n) => { result = n * 2; result + 1 }

Built-in Functions

Function Parameters Description
sin angle (degrees) Sine (−1 to +1)
cos angle (degrees) Cosine (−1 to +1)
tan angle (degrees) Tangent
asin ratio Arc sine (returns degrees)
acos ratio Arc cosine (returns degrees)
atan2 dy, dx Arc tangent of dy/dx (returns degrees). Note: y before x.
polar radius, theta Position at (radius, theta°) from origin
ln number Natural logarithm
log10 number Base-10 logarithm
d2r degrees Convert degrees to radians
r2d radians Convert radians to degrees
PI Constant: π

Type Methods

Methods and properties available on each value type.

Number

Operators: + - * / ^ == != < <= > >=

Method Description
.abs() Absolute value
.interpolate(other, ratio) Interpolate between this and other (ratio 0–1)
.times(callback) Invoke callback n times, passing 0 to n−1

List

Properties: .length

Operators: + - * / ^ (element-wise)

Method Description
.push(item) Append item
.pop() Remove and return last item
.first() First item
.last() Last item
.each([step], callback) Iterate items. Optional step selects every nth; if 0 < step < 1, interpolates.
.map([step], callback) Like .each() but collects results into a list

Color

Operators: == !=

Method Description
.lighten(ratio) Lighter version (ratio 0–1)
.darken(ratio) Darker version (ratio 0–1)
.brighten(ratio) Add white (ratio 0–1)
.desaturate(amount) Reduce chroma (0–1, where 1 = grayscale)
.saturate(amount) Increase chroma (0–1)
.grayscale() Convert to grayscale
.spin(angle) Rotate hue in oklch space (−360 to 360)

Range

Operators: *

Method Description
.start() First value
.end() Last value
.interpolate(ratio) Value at ratio (0–1) between start and end
.ease(style) Apply easing to interpolation
.steps(count, callback) Invoke callback with equally-spaced interpolated values
.each(callback) Iterate integer ranges: callback(value, index)
.map(callback) Like .each() but collects results

Easing styles: linear, cubicIn, cubicOut, cubic, cubicInOut, quadIn, quadOut, quad, quadInOut, bounce

String

Properties: .length

Operators: + (concatenate) * (repeat) / (split) == != < <= > >=

Position

Properties: .x, .y, .length

Operators: + - * /

Indexing: [0] = x, [1] = y

Timeline

Properties: .now, .max_time, .last_animation_start, .last_animation_end, .start_from

Settable: .now, .start_from

Operators: + * / == != < <= > >=

Palette

Properties: .current, .names, .b1.b8, .f1.f8

Settable: .current, .b1.b8, .f1.f8

Font

Properties: .family, .size, .style, .weight, .stretch, .variant, .height

Animations

Animations modify shapes over time.

move

Move a shape to an absolute position or by a relative offset.

move <shape> [to] <position> [take <duration>] [ease <name>]
move <shape> <direction> [<distance>] [take <duration>] [ease <name>]

Directions: up, down, left, right, north, south, east, west, n, s, e, w, ne, nw, se, sw, northeast, northwest, southeast, southwest. Distance defaults to 1 if omitted.

move box1 to (3, 4) take 2 ease cubic
move circle1.ne (5, 5)
move box1 down 2
move box1 right 3 take 1.5
move box1 ne 1 take 2 ease quad

rotate

Rotate a shape by an angle, optionally around a pivot point.

rotate <shape> by <angle> [about <position>] [take <duration>] [ease <name>]
rotate gear by 360 take 4 ease linear
rotate arm by 45 about pivot take 1

set

Animate a property to a new value.

set <target> [, | to] <value> [take <duration>] [ease <name>]
set box1.fill to ~red take 1
set @.now, 5

draw

Animate the drawing of a line or shape (stroke reveal).

draw <shape> [take <duration>] [ease <name>]
draw line1 take 2 ease cubicOut

pause

Pause the animation timeline. Optionally display a message.

pause
pause "Click to continue"

Animation Parameters

Parameter Description
take <duration> Animation duration (in timeline units)
ease <name> Easing function name

Easing functions: linear, cubicIn, cubicOut, cubic, cubicInOut, quadIn, quadOut, quad, quadInOut, bounce

Chaining

Chain animations with then to run sequentially:

move a to (3, 0) take 1 then move b to (3, 0) take 1
move a to (3, 0) take 1 then set a.fill to ~red take 0.5

Each animation after then automatically starts when the previous one finishes.

Cardinals and Directions

Cardinal points reference positions on shapes (.n, .ne, .e, .se, .s, .sw, .w, .nw, .c).

Cardinal direction vectors are used in Face, Gap, Goto, and directional waypoints:

Short Long
n north
ne northeast
e east
se southeast
s south
sw southwest
w west
nw northwest

Identifiers and Reserved Words

Identifiers start with a letter, _, or $, followed by letters, digits, _, or $.

The @ symbol is a special identifier for the timeline.

Reserved words (cannot be used as identifiers):

Shapes: Arc, Aside, Box, Circle, Ellipse, Face, Gap, Goto, Group, Line, Label, Oval

Animations: draw, move, pause, rotate, set, then

Keywords: if, else

Constants: true, false

Attribute keywords (reserved when used as attribute names): align, at, ccw, cw, dashed, dotted, ease, fill, font, font_family, font_size, font_stretch, font_style, font_variant, font_weight, from, height (ht), length (len), line_height, maxwidth, opacity, radius (rad, r), rotation (rot), rx, ry, same, smooth (curve, curved), solid, stepped (step), straight, stroke, stroke_width, take, thickness (thick), to, turn, width (wid), with, x, y

Font Specification

The font attribute accepts a subset of CSS font syntax:

font [style] [variant] [weight] [stretch] <size>[/<line-height>] <family>
Box "Hello" font bold 16px "Helvetica"
Label "Title" font italic small-caps 24pt/1.2 "Georgia", serif

Sizes: xx-small, x-small, small, medium, large, x-large, xx-large, xxx-large, smaller, larger, percentages, or numeric with unit (px, pt, em, rem, cm, mm, in, pc, ex, ch, lh, vh, vw, vmin, vmax, Q).

Styles: italic, oblique [angle]

Variants: small-caps

Weights: bold, lighter, darker, 100900

Stretches: ultra-condensed, extra-condensed, condensed, semi-condensed, semi-expanded, expanded, extra-expanded, ultra-expanded, percentages.


Reference for jp.pegjs grammar