Skip to content

Scripting

openpencil eval runs JavaScript against an OpenPencil document with a Figma-compatible figma global. Use it for headless batch edits, inspection, fixture setup, and automation without opening the editor UI.

Basic usage

sh
openpencil eval design.fig -c "return figma.currentPage.children.length"

The -c flag accepts JavaScript. If the code does not start with return, OpenPencil wraps it in an async function and returns the value from that function when present.

sh
openpencil eval design.fig -c "
  const frame = figma.createFrame()
  frame.name = 'Card'
  frame.resize(300, 200)
  frame.layoutMode = 'VERTICAL'
  frame.itemSpacing = 12
  return { id: frame.id, name: frame.name }
"

Query nodes

sh
openpencil eval design.fig -c "
  return figma.currentPage
    .findAll((node) => node.type === 'FRAME' && node.name.includes('Button'))
    .map((button) => ({
      id: button.id,
      name: button.name,
      width: button.width,
      height: button.height
    }))
"

Modify and save

Use --write / -w to write changes back to the input file:

sh
openpencil eval design.fig -c "
  figma.currentPage.children.forEach((node) => {
    node.opacity = 0.5
  })
" --write

Use --output / -o to write to a new file:

sh
openpencil eval design.fig -c "figma.currentPage.name = 'Updated'" -o updated.fig

Read scripts from stdin

sh
cat transform.js | openpencil eval design.fig --stdin --write

Live app mode

Omit the file path to run against the currently open document in the desktop app:

sh
openpencil eval -c "return figma.currentPage.name"

The desktop app must be running with a document open.

Output

By default, non-TTY output is JSON. Use --json to force JSON output:

sh
openpencil eval design.fig -c "return figma.currentPage.children.map((n) => n.name)" --json

Use --quiet / -q to suppress output when only writing a file.

Supported API surface

The API is intentionally close to Figma's Plugin API, but it maps to OpenPencil's scene graph and file format.

Document and pages

  • figma.root
  • figma.currentPage
  • figma.currentPage.selection
  • figma.getNodeById(id)
  • figma.createPage()

Node creation

  • figma.createFrame()
  • figma.createRectangle()
  • figma.createEllipse()
  • figma.createText()
  • figma.createLine()
  • figma.createPolygon()
  • figma.createStar()
  • figma.createVector()
  • figma.createComponent()
  • figma.createSection()

Tree operations

  • node.children
  • node.parent
  • node.appendChild(child)
  • node.insertChild(index, child)
  • node.clone()
  • node.remove()
  • node.findAll(callback?)
  • node.findOne(callback)
  • node.findChild(callback)
  • node.findChildren(callback?)
  • figma.group(nodes, parent)
  • figma.ungroup(node)

Components

  • figma.createComponentFromNode(node)
  • component.createInstance()
  • instance.mainComponent

Variables

  • figma.getLocalVariables(type?)
  • figma.getVariableById(id)
  • figma.getLocalVariableCollections()
  • figma.getVariableCollectionById(id)
  • figma.createVariable(name, type, collectionId, value?)
  • figma.setVariableValue(variableId, modeId, value)
  • figma.deleteVariable(id)
  • figma.createVariableCollection(name)
  • figma.deleteVariableCollection(id)
  • figma.bindVariable(nodeId, field, variableId)
  • figma.unbindVariable(nodeId, field)

Properties

Common node properties are readable/writable through the proxy, including:

  • Geometry: x, y, width, height, rotation, resize(width, height)
  • Appearance: fills, strokes, effects, opacity, visible, locked, blendMode, clipsContent
  • Radius: cornerRadius, topLeftRadius, topRightRadius, bottomRightRadius, bottomLeftRadius
  • Text: characters, fontSize, fontName, fontWeight, alignment, line height, letter spacing, style-run helpers
  • Auto-layout: layoutMode, primaryAxisAlignItems, counterAxisAlignItems, itemSpacing, padding, sizing, and layout positioning fields
  • Stroke helpers: strokeWeight, strokeAlign, dashPattern

Utilities

  • figma.mixed
  • figma.createImage(data)
  • figma.loadFontAsync(fontName) no-ops because OpenPencil does not gate text edits on plugin font loading
  • figma.listAvailableFontsAsync() returns host-provided fonts when available
  • figma.notify(message) logs a warning in headless mode
  • figma.viewport

Not yet Figma-compatible

These Figma APIs are not exposed as compatible helpers yet:

  • node.exportAsync()
  • node.setBoundVariable(field, variable)
  • node.detachInstance()
  • figma.combineAsVariants(components, parent)
  • Figma style APIs such as figma.createPaintStyle() / figma.createTextStyle()
  • Full vector boolean operation parity

Use OpenPencil CLI export commands, core tools, or direct scene-graph helpers where available.

Released under the MIT License.