Home Docs Install GitHub
中文

Figcraft Documentation

Code-driven professional SVG diagram library for TypeScript / Node.js.

Installation

npm install figcraft

Requires Node.js 18+. The library has no browser dependency — it runs entirely in Node.js.

Quick Start

Create a file diagram.ts and run it with npx tsx diagram.ts:

import { Figure } from 'figcraft'

const fig = new Figure(800, 400, { bg: '#fff' })

const a = fig.rect('Input', {
  pos: [50, 100], size: [120, 50],
  fill: '#e3f2fd', radius: 6
})

const b = fig.rect('Output', {
  pos: [250, 100], size: [120, 50],
  fill: '#c8e6c9', radius: 6
})

fig.arrow(a, b, { head: 'stealth', label: 'data' })

await fig.export('diagram.svg', { fit: true, margin: 20 })

Figure Constructor

new Figure(width?: number, height?: number, options?: FigureOptions)
ParameterTypeDefaultDescription
widthnumber800Canvas width in pixels
heightnumber400Canvas height in pixels
optionsFigureOptions{}See FigureOptions below

FigureOptions

PropertyTypeDefaultDescription
bgstringtransparentBackground color
fontFamilystringsystemDefault font family
mathFontstring'Times New Roman'Font for $math$ formulas
codeFontstring'Menlo'Font for `code` spans
fontsstring[][]Register local font names
autoAlignbooleantrueAuto-align elements in same row
antiOverlapbooleantrueAuto-prevent arrow label overlap
alignTolerancenumber20Row detection Y tolerance (px)

Font Registration

fig.font(name: string, source?: string)

Register fonts for use in diagrams:

// Local system font
fig.font('PingFang SC')

// Google Fonts
fig.font('Inter', 'google')

// Custom URL
fig.font('MyFont', 'https://example.com/font.woff2')

Elements Overview

Figcraft provides 10 element types. All are created through fig.<type>(label, config?).

TypeMethodDescriptionKey Config
rectfig.rect()Rounded rectangleradius
circlefig.circle()Circler (default: 30)
textfig.text()Text label (supports markdown)fontSize, bold
imagefig.image()Embedded imagesrc, size
diamondfig.diamond()Decision nodesize
trapezoidfig.trapezoid()Pooling / reductiontopRatio (0-1)
cylinderfig.cylinder()3D cylinder (DB/CNN)depth (default: 0.15)
cuboidfig.cuboid()3D block (tensor)depth (default: 15px)
spherefig.sphere()3D spherer (default: 30)
stackfig.stack()Multi-layer stackcount (default: 3), stackOffset

2D Shapes

Rect

const box = fig.rect('Label', {
  pos: [50, 50], size: [120, 60],
  fill: '#e3f2fd', radius: 8,
  stroke: { color: '#1565c0', width: 2 }
})
Rect element demo

Circle

const node = fig.circle('Node', {
  pos: [200, 100], r: 35,
  fill: '#fff3e0', color: '#e65100'
})
Circle element demo

Diamond

const decision = fig.diamond('Yes?', {
  pos: [100, 100], size: [80, 60],
  fill: '#fce4ec'
})
Diamond element demo

Trapezoid

const pool = fig.trapezoid('MaxPool', {
  pos: [100, 100], size: [100, 50],
  fill: '#e8f5e9', topRatio: 0.55
})
Trapezoid element demo

Text

const label = fig.text('**Bold** and *italic*', {
  pos: [100, 50], fontSize: 14
})
Text element demo

Image

const img = fig.image('./photo.png', {
  pos: [100, 50], size: [120, 80]
})
Image element demo

3D Shapes

Cylinder

Great for databases and CNN feature maps. depth controls the ellipse ratio (0-1).

const db = fig.cylinder('Database', {
  pos: [50, 50], size: [100, 70],
  fill: '#e8f5e9', depth: 0.2
})
Cylinder element demo

Cuboid

3D block for tensors and data volumes. depth is the extrusion distance in pixels.

const tensor = fig.cuboid('Feature Map', {
  pos: [50, 50], size: [80, 60],
  fill: '#fff8e1', depth: 18
})
Cuboid element demo

Sphere

const node = fig.sphere('Attention', {
  pos: [100, 100], r: 35,
  fill: '#e0f7fa'
})
Sphere element demo

Stack

Multi-layer stacking effect. count sets layers, stackOffset controls offset per layer.

const layers = fig.stack('Conv ×4', {
  pos: [50, 50], size: [100, 60],
  fill: '#ede7f6', count: 4,
  stackOffset: [6, -6]
})
Stack element demo

ElementConfig

All properties are optional. Apply to any element type.

PropertyTypeDescription
pos[x, y]Position (pixels or '%')
size[w, h]Size (pixels or '%')
fillstringFill color. 'none' = transparent
fillOpacitynumberFill transparency (0-1)
colorstringTheme color (sets stroke + fontColor)
strokestring | StrokeConfigBorder. 'none' = no border
radiusnumberCorner radius (Rect)
rnumberRadius for Circle/Sphere (default: 30)
opacitynumberOverall opacity (0-1)
shadowboolean | ShadowConfigDrop shadow
paddingnumberInner padding
fontSizenumberFont size in px
fontFamilystringFont family
fontColorstringText color
fontWeightstring | numberFont weight
boldbooleanShorthand for fontWeight: 'bold'
topRationumberTrapezoid top/bottom ratio (0-1, default: 0.6)
depthnumberCylinder: ellipse ratio (0.15). Cuboid: extrusion px (15)
countnumberStack layer count (default: 3)
stackOffset[dx, dy]Stack per-layer offset (default: [6, -6])
StrokeConfig: { color?: string, width?: number, dash?: number[] }
Dash examples: [6, 3] = dashed, [2, 2] = dotted
ShadowConfig: { dx?: number, dy?: number, blur?: number, color?: string }
Use shadow: true for defaults, or pass a config object.

Nesting (Containers)

Any element can contain child elements. Children are positioned relative to their parent:

const parent = fig.rect('Container', {
  pos: [50, 50], size: [200, 150]
})

// Children use percentage positioning relative to parent
parent.circle('Center', { pos: ['50%', '50%'], r: 20 })
parent.rect('TopLeft', { pos: [10, 30], size: [60, 30] })
Nesting example

Basic Arrow

fig.arrow(source, target, config?)

Connect two elements. Auto-snaps to nearest edges if no anchor is specified.

fig.arrow(a, b)  // simplest form
fig.arrow(a, b, { head: 'stealth', color: '#1565c0' })
Basic arrow demo

Anchor Points

Control exactly where arrows connect to elements:

// Simple: specify a side
fig.arrow(a, b, { from: 'right', to: 'left' })

// Detailed: specify side + position along edge (0-100%)
fig.arrow(a, b, {
  from: { side: 'bottom', at: 30 },  // 30% from left on bottom edge
  to: { side: 'top', at: 70 }         // 70% from left on top edge
})
Anchor points demo

Arrow Head Types

11 built-in arrow head styles:

HeadDescription
triangleSolid triangle (default)
triangle-openOpen triangle
stealthSharp arrow (LaTeX style)
veeV-shape >
circleSolid circle
circle-openOpen circle
diamondSolid diamond
diamond-openOpen diamond
barVertical bar |
dotSmall dot
noneNo arrowhead
All 11 arrow head types

Path Types

PathDescriptionExtra Config
straightDirect line (default)-
curveBezier curvecurve: bend amount (positive=up)
polylineRight-angle pathcornerRadius: rounded corners
// Curved arrow
fig.arrow(a, b, { path: 'curve', curve: 40 })

// Polyline with rounded corners
fig.arrow(a, b, { path: 'polyline', cornerRadius: 8 })
Path types demo

Styles & Labels

fig.arrow(a, b, {
  style: 'dashed',           // 'solid' | 'dashed' | 'dotted'
  color: '#e65100',
  width: 2,
  label: 'data flow',
  bidirectional: true,       // double-headed arrow
  headSize: 10
})
Arrow styles demo

arrows() — Fan-out / Fan-in

Create multiple arrows at once. Supports 1-to-many and many-to-1:

// Fan-out: one source to many targets
fig.arrows(source, [targetA, targetB, targetC], {
  head: 'stealth', color: '#1565c0'
})

// Fan-in: many sources to one target
fig.arrows([srcA, srcB, srcC], target, {
  head: 'triangle', style: 'dashed'
})
Fan-out / fan-in demo

fork() — Branching

Create a forking arrow with a shared main trunk that splits to multiple targets:

fig.fork(source, [targetA, targetB, targetC], {
  head: 'stealth',
  style: 'solid',
  color: '#333'
})
Fork branching demo
Difference: arrows() draws individual arrows from source to each target. fork() draws one trunk from the source, then splits into branches — like a tree.

row()

Arrange elements horizontally. The first element must have pos set.

fig.row([a, b, c], { gap: 40 })  // default gap: 40px
row() layout demo

col()

Arrange elements vertically.

fig.col([a, b, c], { gap: 30 })
col() layout demo

grid()

Arrange elements in a grid.

fig.grid([a, b, c, d, e, f], {
  cols: 3,        // 3 columns (default)
  gap: 20,        // uniform gap
  rowGap: 30,     // override vertical gap
  colGap: 15      // override horizontal gap
})
grid() layout demo

group()

Draw a labeled frame around elements. Call after layout methods (needs resolved positions). Returns a Rect that can be used as an arrow target.

const frame = fig.group([a, b, c], {
  label: 'Pipeline',
  fill: 'rgba(25,118,210,0.05)',
  stroke: { color: '#90caf9', dash: [6, 3] },
  radius: 8,
  padding: 20,
  fontSize: 11,
  fontColor: '#1565c0',
  size: [400, 200]    // optional fixed size (auto-centers members)
})
group() demo

Markdown in Labels

All element labels support inline markdown formatting:

SyntaxResultFont
**bold**bold textSame font, bold weight
*italic*italic textSame font, italic
`code`monospacecodeFont (Menlo)
$E=mc^2$math formulamathFont (Times New Roman)
fig.rect('**Conv2D** $3 \\times 3 \\times 64$', { ... })
fig.text('Loss: `cross_entropy`', { ... })
Markdown formatting demo

Export Formats

File format is auto-detected from the file extension:

ExtensionFormatEngine
.svgSVG vectorBuilt-in
.pngPNG rastersharp
.jpg / .jpegJPEGsharp
.webpWebPsharp
.pdfPDF documentpdfkit
// SVG
await fig.export('output.svg', { fit: true, margin: 20 })

// High-res PNG
await fig.export('output.png', { fit: true, scale: 3, margin: 30 })

// Get SVG string without saving
const svg = fig.render({ fit: true })

ExportOptions

PropertyTypeDefaultDescription
fitbooleanfalseAuto-crop to content bounds
marginnumber20Margin around content (when fit=true)
scalenumber2Resolution multiplier (raster only)
qualitynumber90JPG/WebP quality (1-100)

Export Format Showcase

One source file, every format you need. Click the tabs below to compare outputs:

SVG export
.svg
fig.export('flowchart.svg')
  • Vector format — infinite scaling
  • Smallest file size
  • Full canvas size preserved
PNG export
.png
fig.export('flowchart.png')
  • Default 2x resolution
  • White background
  • Full canvas size
JPG export
.jpg
fig.export('flowchart.jpg', {
  fit: true, margin: 20
})
  • fit: true auto-crops to content
  • 20px margin around content
  • Quality: 90 (adjustable)
PNG fit export
.png fit
fig.export('flowchart-fit.png', {
  fit: true, margin: 20
})
  • fit: true crops to content bounds
  • Removes excess whitespace
  • Best for embedding in documents
Transparent export
.png transparent
// Omit bg = transparent background
const fig = new Figure(800, 350)
fig.export('out.png', { fit: true })
  • Omit bg for transparency
  • Checkerboard = alpha channel
  • Great for overlaying on other content
WebP export
.webp
fig.export('flowchart.webp', {
  fit: true, margin: 20
})
  • Modern format, smaller size
  • Supports transparency
  • Ideal for web publishing

MCP Integration

Figcraft includes a built-in MCP (Model Context Protocol) server. AI agents like Claude Code and Cursor can generate diagrams directly without writing code.

Setup

Add to your Claude Code or Cursor MCP settings:

{
  "mcpServers": {
    "figcraft": {
      "command": "npx",
      "args": ["figcraft-mcp"]
    }
  }
}

Then just describe your diagram to the AI: "Draw a Transformer architecture diagram with encoder and decoder stacks".

MCP Tools

ToolDescription
create_diagramCreate a diagram from a JSON spec. Returns SVG and optionally exports to file.
get_element_typesGet the full API reference. AI calls this first to learn the API.

JSON Specification Format

The create_diagram tool accepts this JSON structure:

{
  "width": 800,
  "height": 400,
  "bg": "#ffffff",
  "fontFamily": "Arial",
  "autoAlign": true,
  "elements": [
    {
      "id": "a",
      "type": "rect",
      "label": "Input",
      "pos": [50, 100],
      "size": [120, 60],
      "fill": "#e3f2fd",
      "radius": 6
    },
    {
      "id": "b",
      "type": "rect",
      "label": "Output",
      "pos": [300, 100],
      "size": [120, 60],
      "fill": "#c8e6c9"
    }
  ],
  "arrows": [
    {
      "from": "a",
      "to": "b",
      "fromSide": "right",
      "toSide": "left",
      "label": "data",
      "head": "stealth"
    }
  ],
  "groups": [
    {
      "members": ["a", "b"],
      "label": "Pipeline",
      "padding": 20,
      "stroke": "#999"
    }
  ],
  "layouts": [
    { "type": "row", "elements": ["a", "b"], "gap": 40 }
  ],
  "fanArrows": [
    { "from": "src", "to": ["a", "b", "c"] }
  ],
  "forks": [
    { "from": "root", "to": ["x", "y", "z"] }
  ],
  "export": {
    "path": "output.svg",
    "fit": true,
    "margin": 20
  }
}

Figure Class — All Methods

MethodReturnsDescription
fig.rect(label, config?)RectCreate rectangle
fig.circle(label, config?)CircleCreate circle
fig.text(content, config?)TextCreate text label
fig.image(src, config?)ImageCreate image
fig.diamond(label, config?)DiamondCreate diamond
fig.trapezoid(label, config?)TrapezoidCreate trapezoid
fig.cylinder(label, config?)CylinderCreate 3D cylinder
fig.cuboid(label, config?)CuboidCreate 3D cuboid
fig.sphere(label, config?)SphereCreate 3D sphere
fig.stack(label, config?)StackCreate stacked layers
fig.arrow(src, tgt, config?)voidSingle arrow
fig.arrows(src, tgt, config?)voidFan-out / fan-in
fig.fork(src, targets, config?)voidBranching arrow
fig.row(els, opts?)voidHorizontal layout
fig.col(els, opts?)voidVertical layout
fig.grid(els, opts?)voidGrid layout
fig.group(members, config?)RectGroup frame
fig.font(name, source?)voidRegister font
fig.render(opts?)stringRender to SVG string
fig.export(path, opts?)PromiseExport to file

ElementConfig — Full Reference

PropertyTypeDefaultDescription
pos[PosValue, PosValue]-Position [x, y]. Pixels or '%'
size[PosValue, PosValue]autoSize [width, height]
fillstring-Fill color. 'none' = transparent
fillOpacitynumber1Fill opacity (0-1)
colorstring-Theme color (stroke + fontColor)
strokestring | StrokeConfig-Border config
radiusnumber0Corner radius (Rect)
rnumber30Circle/Sphere radius
opacitynumber1Overall opacity
shadowboolean | ShadowConfigfalseDrop shadow
paddingnumber-Inner padding
fontSizenumber14Font size (px)
fontFamilystringinheritFont family
fontColorstring#000Text color
fontWeightstring | numbernormalFont weight
boldbooleanfalseShorthand bold
topRationumber0.6Trapezoid ratio
depthnumbervaries3D depth
countnumber3Stack layers
stackOffset[number, number][6, -6]Stack offset

ArrowConfig — Full Reference

PropertyTypeDefaultDescription
fromAnchorSpecautoSource anchor
toAnchorSpecautoTarget anchor
labelstring-Text label
labelOffsetnumberautoManual label offset
stylestring'solid''solid' | 'dashed' | 'dotted'
colorstring'#000'Arrow color
widthnumber1.5Line width
headArrowHead'triangle'Head type
headSizenumberautoHead size
bidirectionalbooleanfalseDouble-headed
pathArrowPath'straight'Path type
curvenumber0Curve bend amount
cornerRadiusnumber0Polyline corner radius

ExportOptions — Full Reference

PropertyTypeDefaultDescription
scalenumber2Resolution multiplier (raster)
fitbooleanfalseAuto-crop to content
marginnumber20Margin with fit=true (px)
qualitynumber90JPG/WebP quality (1-100)