napkin-sketchdocs 1.0.0 GitHub

Using napkin-sketch from code#

On this page

API hub · Reference · Quickstart · Cheatsheet

The package has three entries. @isocialpractice/napkin-sketch holds everything that runs anywhere - the editor, the data model, the engines, the composition API and the napkin script language - and reads no file. @isocialpractice/napkin-sketch/node holds what reads and writes files, for Node. @isocialpractice/napkin-sketch/markdown gives the first its Markdown and code readers. This page is the reference for them as a program uses them: importing them, drawing a script, writing files, the result object, and what is an error value and what throws.

Contents

What this is#

A drawing is made the same way in a browser and in Node: a script goes to evaluate, a sketch book comes back, and renderSketch or renderBook writes it as SVG, PNG, PDF, .skbk or an Illustrator script. None of that touches a DOM or a disk, so it all lives in the browser-safe @isocialpractice/napkin-sketch entry, beside the editor and the composition API that were already there.

The disk is the host's business. A script names an image, a document or a linked file, and the host hands it over; the host writes what was drawn. In Node that host is @isocialpractice/napkin-sketch/node: one call reads a script file, loads what it names, runs it, and writes a file for each format asked for, and the result it gives back is the object a command line prints.

The mental model#

 napkin-sketch                                  napkin-sketch/node
 -------------                                  ------------------
 parseScript, validateScript, formatScript      readScript            (a file in)
 evaluate --> book                              loadAssets, loadDocuments, resolveLinkFromDir
 renderSketch, renderBook --> strings, bytes    writeBook             (files out)
 drawSvg: a script straight to SVG              drawFile, drawToFiles (all of it, one call)

Reference#

The entries#

ImportRuns inHolds
@isocialpractice/napkin-sketcha browser, Node, any bundlerThe editor, the data model, the sharpen engine, the composition API, and the language: reading, checking and writing scripts, evaluate, drawSvg, the render calls, and the verb and diagnostic tables.
@isocialpractice/napkin-sketch/nodeNode 22.12 and laterFiles: drawFile, drawToFiles, writeBook, readScript, loadAssets and loadDocuments, and the composition API's file helpers, imageDataUrl, writeComposition, resolveLinkFromDir and mediaTypeOf. It reads Markdown Flow text with marked's lexer.
@isocialpractice/napkin-sketch/markdowna browser, Node, any bundlerMarkdown Flow's and Clean Language Accents' readers: importing it gives @isocialpractice/napkin-sketch marked's lexer and highlight.js, so a Markdown Flow text draws as its Markdown renders and code is colored. Its markdownToType reads with the lexer, and registerAccentLanguage installs a language beyond the 36 common ones.

@isocialpractice/napkin-sketch/graphic-design/files, the composition helpers' own entry, keeps working; @isocialpractice/napkin-sketch/node gives the same functions, so a Node program has one import for files.

@isocialpractice/napkin-sketch holds no Markdown reader of its own: a Markdown Flow text - text "# Notes" at 40 60 markdown - shows its Markdown as typed until one is given. @isocialpractice/napkin-sketch/markdown imports @isocialpractice/napkin-sketch rather than holding a copy of it, so the reader it registers is the one the API reads with:

import { drawSvg } from '@isocialpractice/napkin-sketch';
import '@isocialpractice/napkin-sketch/markdown';

const { svg } = drawSvg('napkin 1\ntext "# Notes\\n\\n- one" at 40 60 markdown\n');

Getting the package#

The package is built from a checkout until it is published to npm:

npm install
npm run build        # dist/, including dist/node/
npm run build:types  # the .d.ts files the exports map points at

A project then depends on the folder - npm install ../napkin-sketch - or links it with npm link, and imports it by name.

ES modules, CommonJS and TypeScript#

Every entry is an ES module:

import { evaluate, renderSketch } from '@isocialpractice/napkin-sketch';
import { drawFile } from '@isocialpractice/napkin-sketch/node';

CommonJS code loads them with import(), which works on every Node the package supports. require works only where Node can require an ES module, and elsewhere fails with ERR_REQUIRE_ESM:

const { drawFile } = await import('@isocialpractice/napkin-sketch/node');

Type declarations ship with both, found through the package's exports map under moduleResolution bundler, node16 or nodenext. Every option and result on this page is a named type: DrawToFilesOptions, DrawResult, WrittenFile and WriteBookOptions from @isocialpractice/napkin-sketch/node, and EvaluateOptions, ScriptResult, Diagnostic, RenderSketchOptions and the rest from @isocialpractice/napkin-sketch.

A browser build imports @isocialpractice/napkin-sketch only. @isocialpractice/napkin-sketch/node reads files with node:fs, which a browser does not have.

Drawing a script#

evaluate(source, options) runs a script and returns { ok, book, diagnostics, stats, output, seed }, and renderSketch and renderBook write the book; Drawing with napkin script and Writing a drawing out are their references. A script is text, or the object form as a JSON value.

drawSvg(source, options) is the three in one call, for the caller that wants a picture: it runs the script and gives back its first page as SVG, cut as the script's crop and registration ask.

import { drawSvg } from '@isocialpractice/napkin-sketch';

const { ok, svg, diagnostics } = drawSvg('napkin 1\npage 400 300\ncircle 200 150 60');
drawSvg optionDefaultMeaning
everything evaluate takesseed, timestamp, assets, documents, resolveLink, limits, name, fragment.
cropthe script's ownWhat to cut the page to: 'auto', 'none', or a box.
registrationthe script's ownOne box, over any crop.
transparentfalseLeave the paper out.

It returns { ok, svg, diagnostics }. For every page, or another format, run evaluate and hand the book to renderBook.

Writing files#

drawFile(path, options) reads a script file and writes what it drew, and drawToFiles(source, options) does the same from a script in hand. Both return a promise of the result.

import { drawFile } from '@isocialpractice/napkin-sketch/node';

const { ok, files, diagnostics } = await drawFile('card.napkin', { out: 'out', formats: ['svg', 'png'] });

writeBook(book, options) is the writing half alone, for a book already in hand - one evaluate made, or one read from a .skbk - and returns the written files. It takes out, name, formats, base and strict as below, and the render options.

Options#

drawFile and drawToFiles take everything evaluate does and everything renderBook does, and these:

OptionDefaultMeaning
outthe working folderThe folder the files go in. It is made when it is missing.
formats['svg']Any of svg, png, pdf, skbk, jsx, each written once.
namethe first page'sThe name the files are written under, and the book's.
assetsnoneImages the script places by name: data URLs, or paths read with loadAssets.
documentsnonePages or books the script copies in with use: objects, or .skbk paths read with loadDocuments.
basethe working folder; the script's folder for drawFileThe folder the script's links are read inside, for their size and for the PNG, and which a .jsx written to out places them from.
strictfalseWrite nothing unless nothing at all was reported, warnings included.
crop, registrationthe script's ownThe box every file is cut to.
transparent, scale, decodeImage, onWarningAs renderBook takes them.
seed, timestamp, limits, resolveLink, fragmentAs evaluate takes them. resolveLink wins over base.

writeBook takes out, formats, name and the render options.

The result#

drawFile and drawToFiles give back a DrawResult:

FieldMeaning
okTrue when nothing was an error. With strict, true only when nothing was reported at all.
filesWhat was written, in the order the formats were asked for and then page by page: { path, format, page }, where path is out joined with the file's name and page, counting from 1, is there for SVG and PNG.
diagnosticsEverything the script reported, reading and running together, in reading order.
statsinstructions, marks, anchors, points and pages: how much the run did.
warningsWhat a format left out or drew as a stand-in, each starting with its page's name.

formatDiagnostic(diagnostic, 'card.napkin') from @isocialpractice/napkin-sketch prints a diagnostic the way editors and CI read one: card.napkin:3:1: error unknown-verb: ....

Assets and documents are named by the host, so no folder guard applies to them. Links are named by the script, so one does.

Errors are values; the disk throws#

Nothing in a script throws. An instruction that cannot run is a diagnostic, the run goes on, and the files are written with what was drawn, ok false and the diagnostics saying why - so a program writing a hundred drawings gets the ninety-nine that worked. strict turns that off: any diagnostic, or any format warning, and nothing is written.

The disk throws, as Node's own calls do, and the message names what failed:

ThrowsWhen
readScript, drawFileThe file cannot be read; a .json file is not JSON, or not an array.
loadAssets, and drawToFiles given assetsAn asset's file cannot be read: asset "logo" could not be read from ....
loadDocuments, and drawToFiles given documentsA document's file cannot be read, or is not .skbk JSON.
drawFile, drawToFiles, writeBookout cannot be made or written to.
every render callAn unknown format, which is a mistake in the calling code.

The language in the barrel#

What @isocialpractice/napkin-sketch exports for scripts:

ExportWhat it is
parseScript, validateScript, formatScriptRead text, check the object form, and write either back as canonical text.
evaluate, drawSvgRun a script to a book; run it straight to SVG.
renderSketch, renderBook, inkBox, renderBox, RENDER_FORMATSWrite a page or a book; the boxes the output is cut to.
sketchToCompositionThe lowering behind the PNG, for a caller that adds to a drawing with the composition API.
sketchesToJsxThe Illustrator script writer behind format: 'jsx', beside sketchesToPdf.
formatDiagnosticPrint a diagnostic as file:line:column: level code: message.
SCRIPT_VERSION, SCRIPT_LIMITS, VERBS, DIAGNOSTICSThe language version, the budget, and the verb and diagnostic tables every listing is made from.

Worked examples#

A folder of scripts, drawn to files. Each script's problems are printed where it failed, and the rest are drawn:

import { readdirSync } from 'node:fs';
import { formatDiagnostic } from '@isocialpractice/napkin-sketch';
import { drawFile } from '@isocialpractice/napkin-sketch/node';

for (const file of readdirSync('scripts').filter((name) => name.endsWith('.napkin'))) {
  const result = await drawFile(`scripts/${file}`, { out: 'out', formats: ['svg', 'png'], timestamp: '2026-09-25T00:00:00.000Z' });
  for (const d of result.diagnostics) console.error(formatDiagnostic(d, file));
  for (const w of result.warnings) console.error(`${file}: ${w}`);
  console.log(result.ok ? 'drew' : 'drew, with errors', result.files.map((f) => f.path).join(', '));
}

A script built as JSON. A program in any language can build the object form; this one is a card with a named layer:

[
  { "verb": "napkin", "version": 1 },
  { "verb": "page", "width": 400, "height": 300 },
  { "verb": "name", "name": "badge" },
  { "verb": "layer", "name": "Badge" },
  { "verb": "fill", "fill": "#ffe08a" },
  { "verb": "circle", "cx": 200, "cy": 150, "r": 80 }
]
import { drawToFiles } from '@isocialpractice/napkin-sketch/node';

const { ok, files } = await drawToFiles(script, { out: 'out', formats: ['png'], scale: 2, strict: true });
// out/badge.png, twice the size; nothing written if anything was reported

A live preview in a browser. The barrel alone, redrawing as someone types:

import { drawSvg } from '@isocialpractice/napkin-sketch';

editor.addEventListener('input', () => {
  const { svg, diagnostics } = drawSvg(editor.value);
  preview.innerHTML = svg;
  problems.textContent = diagnostics.map((d) => d.message).join('\n');
});

Limits#

For agents#

See also#