A worker-based, cross-vendor G-code toolpath stack for the browser: parse .gcode and
.gcode.3mf off the main thread, normalize them into a versioned intermediate representation
(ToolpathIR), and render an interactive Three.js preview — with first-class Vue, React, and
Svelte integrations that are thin adapters over one shared, framework-neutral engine.
Status: published. All ten
@chestnutlabs/*packages are on npm (latestv0.2.0, lockstep-versioned with npm provenance). E0–E9 of the master plan are closed; E10 (motion-model correctness) is in progress. Install from npm — see Quick start below.
- Off-thread parsing — a Web Worker session (
GcodeParseSession) with streaming input, progressive preview for large files, resource limits, and an adversarial-input corpus behind it. - Cross-vendor dialect handling — PrusaSlicer, OrcaSlicer/Bambu Studio, Cura, Klipper, Marlin,
and RepRap-flavor annotations (features, objects, bed geometry, thumbnails, multi-tool), each
claim capability-tagged as
known | inferred | approximated | unavailable— the stack refuses to fabricate what it cannot know. .gcode.3mfcontainer support — zero-dependency, bounded ZIP extraction with multi-plate selection, hardened against adversarial archives.- A versioned neutral IR (
ToolpathIR) — structure-of-arrays geometry + metadata + source index; the seam between parsing and everything else. - A Three.js renderer — layer chunks with decimation disclosure, layer-range clip and segment-level scrub, tube or line geometry with automatic quality fallback, per-file build plates, WebGL context-loss recovery.
- Honest live progress (for printer telemetry) — a normalized
ProgressObservationcontract mapped onto the toolpath with tiered confidence: a precise cut + marker when the source position is known, an uncertainty band when it is approximated, stale-signal handling, and user scrub always winning. - Three equal framework adapters — each ships a ready-to-use
<GcodePreview>component and a lower-level surface, with the same capabilities, options, events, and TypeScript contracts, enforced by a shared behavioral suite that runs against all three in CI.
| Package | What it is |
|---|---|
@chestnutlabs/toolpath-core |
ToolpathIR, capability model, progress mapping |
@chestnutlabs/gcode-parser |
Worker parse core + session client (streaming, limits, workers) |
@chestnutlabs/gcode-dialects |
Slicer/firmware dialect annotators |
@chestnutlabs/gcode-containers |
.gcode.3mf / ZIP container adapters |
@chestnutlabs/gcode-renderer-three |
Three.js toolpath renderer (peer: three) |
@chestnutlabs/gcode-preview-core |
Framework-neutral preview controller + portable behavioral suite |
@chestnutlabs/gcode-preview-vue |
Vue 3 component + useGcodePreview() composable |
@chestnutlabs/gcode-preview-react |
React component + useGcodePreview() hook |
@chestnutlabs/gcode-preview-svelte |
Svelte component + createGcodePreview() store/action |
@chestnutlabs/gcode-preview-element |
Framework-free <gcode-preview> Web Component (no peer framework) |
Install the adapter for your framework plus three (the renderer declares three as a
peerDependency, range ^0.178.0 — npm ≥ 7 installs it automatically; pnpm/yarn users add it
explicitly):
npm install @chestnutlabs/gcode-preview-vue three # or -react / -svelteEach adapter has two adoption levels — a complete component, and a lower-level API for building your own controls (composable / hook / store) — documented in its package README.
<script setup>
import { GcodePreview } from '@chestnutlabs/gcode-preview-vue';
import { shallowRef } from 'vue';
const file = shallowRef(null);
</script>
<template>
<input type="file" accept=".gcode,.3mf" @change="file = $event.target.files?.[0] ?? null" />
<div style="height: 70vh">
<GcodePreview :source="file" @ready="(s) => console.log(`${s.segments} segments`)" />
</div>
</template>Lower level: useGcodePreview() — canvas ref, worker
parse, controls, reactive state summaries.
import { GcodePreview } from '@chestnutlabs/gcode-preview-react';
function Viewer({ file }) {
return (
<div style={{ height: '70vh' }}>
<GcodePreview source={file} onReady={(s) => console.log(`${s.segments} segments`)} />
</div>
);
}StrictMode-safe. Lower level: useGcodePreview()
(useSyncExternalStore-backed state, identity-stable handle).
<script>
import GcodePreview from '@chestnutlabs/gcode-preview-svelte/GcodePreview.svelte';
let file = null;
</script>
<div style="height: 70vh">
<GcodePreview source={file} on:ready={(e) => console.log(`${e.detail.segments} segments`)} />
</div>Ships as raw .svelte (your bundler's Svelte plugin compiles it). Lower level:
createGcodePreview() — store contract +
use: canvas action.
All three components share the same defaulted prop surface — source, parseOptions,
buildVolume, quality, colorMode, layerRange, scrub, showTravel, progress,
createWorker — with matching events/callbacks. <GcodePreview source={file} /> is the whole
thin path; the full viewer is reachable without switching APIs.
- Default (zero setup): the adapters create the batteries worker — every supported dialect
adapter plus
.gcode.3mfsupport — via the bundler-nativenew Worker(new URL(...))pattern. Vite resolves it out of the box (tested in CI). - Custom: pass
createWorkerfor the slim build, custom dialect adapters, other bundlers, or strict-CSP environments. Both paths are documented in the Vue package README (shared across adapters by design).
- Formats:
.gcode(plain),.gcode.3mf(sliced-plate container; multi-plate viaparseOptions.plate). - Dialects: PrusaSlicer, OrcaSlicer/Bambu, Cura, Klipper, Marlin, RepRap-flavor — see the evidence-dated compatibility matrix.
- Motion commands: which position-affecting G/M-codes are honored — absolute/relative
positioning (
G90/G91), extruder mode (M82/M83), and theG92E-datum are modeled (E10 phase 1); arc planes (G17–G19) and work-coordinate systems are still in progress — see G-code motion coverage. - Build volume: per-file discovered bed geometry with consumer-wins precedence (your configured plate is never silently overridden; discovery is emitted instead).
- Live progress: the normalized progress signal contract plus consumer notes for wiring real printer telemetry (Moonraker, Bambu, OctoPrint-class sources).
- Support & deprecation policy: Node/browser/framework matrix and the rolling support window.
- Headless still render:
renderStill— a single non-interactive image from a WorkerOffscreenCanvas, Electron hidden window, or headless Chromium (server-side thumbnails).
tools/demo— the showcase: full control panel over the whole pipeline (corpus picker, dialect annotations, quality/color modes, layer clip + scrub, simulated live-progress tiers), plus a Vue-component parity page and the visual-regression harness.npm run devinside the directory.tools/example-react,tools/example-svelte— complete Vite apps per framework, run the same way. All three apps consume the packages exactly as an external consumer would.
Docs-first: every architecture-sensitive epic passes a Design Document gate before
implementation. The master plan controls direction; the
docs index tracks epic status; accepted designs live in
docs/design/. Current state: E0–E9 closed and accepted (parser, dialects,
containers, renderer, live progress, multi-framework integration, the v0.1.0 release, and
v0.2.0's toolpath annotations + renderer options); E10 (motion-model correctness) is in
progress — its phase 1 (M82/M83, G90/G91, the G92 E-datum) has shipped, with arc
planes and coordinate systems to follow.
Contributions: see CONTRIBUTING.md · security policy: SECURITY.md.
# Node >= 22
npm ci
npm run build # inherited engine build (rollup)
npm run test # root suite (IR goldens, manifest validation, adapters)
npm run test:packages # all workspace package suites
npm run lint && npm run typeCheck && npm run license:check
npm run test:consumer-vue # tarball consumer fixture
npm run pack:check # packaged-artifact gate (pack snapshots + publint + attw)This project began as a fork of
xyz-tools/gcode-preview (project identity
remcoder/gcode-preview) by Remco Veldkamp and contributors, MIT-licensed. Chestnut Labs has
since rebuilt it into the worker-based multi-package stack described above; the inherited Git
history is preserved, upstream copyright notices are retained in LICENSE, and the
full attribution and provenance record lives in NOTICE.md,
docs/UPSTREAM_PROVENANCE.md, and the
upstream & licensing policy.
Upstream changes are adopted deliberately through review, never auto-synced.
MIT — inherited code © 2017–2025 Remco Veldkamp and the xyz-tools/gcode-preview
contributors; Chestnut Labs additions © 2026 Chestnut Labs.


