npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@sienci/gviewer

v0.1.0

Published

GCode visualization library using Three.js

Downloads

176

Readme

gviewer

TypeScript library for parsing, virtualizing, and visualizing GCode toolpaths. Built on Three.js with an optional React wrapper.

Installation

npm install @sienci/gviewer

Peer dependencies — install whichever you need:

npm install three             # required for the viewer
npm install react react-dom   # required for the React component

Import paths

| Path | Contents | |---|---| | @sienci/gviewer | Parser, virtualizer, geometry builders, shared types | | @sienci/gviewer/viewer | GCodeViewer class, themes, viewer types | | @sienci/gviewer/react | GCodeVisualizer React component | | @sienci/gviewer/viewer/viewcube.css | Stylesheet for the ViewCube overlay |


Quick start

React

import { useRef } from "react";
import { GCodeVisualizer } from "@sienci/gviewer/react";
import type { GCodeViewerHandle } from "@sienci/gviewer/viewer";
import "@sienci/gviewer/viewer/viewcube.css";

export function App() {
  const ref = useRef<GCodeViewerHandle>(null);

  return (
    <>
      <GCodeVisualizer
        id="main"
        ref={ref}
        style={{ width: "100%", height: "600px" }}
      />
      <button onClick={() => ref.current?.loadFromUrl("/part.gcode")}>
        Load
      </button>
    </>
  );
}

Vanilla JS

import { GCodeViewer } from "@sienci/gviewer/viewer";
import "@sienci/gviewer/viewer/viewcube.css";

const viewer = new GCodeViewer({
  id: "main",
  container: document.getElementById("viewer")!,
});

await viewer.loadFromUrl("/part.gcode");
viewer.focusToModel();

API reference

@sienci/gviewer — core

GCodeParser

Stateless line-by-line parser. No state is kept between calls.

import { GCodeParser } from "@sienci/gviewer";

const parser = new GCodeParser();
const result = parser.parseLine("G1 X10 Y5 F1200 ; move");
// result.words    — all letter-value pairs
// result.gcodes   — G and M words only
// result.params   — non-G/M words (X, Y, F, …)
// result.comments — extracted semicolon and paren comments

parseLine(line: string): ParsedLine

Returns a ParsedLine:

type ParsedLine = {
  raw: string;
  words: GCodeWord[];    // all tokens
  gcodes: GCodeWord[];   // G/M codes
  params: GCodeWord[];   // axis and parameter words
  comments: Comment[];   // extracted comments with positions
};

type GCodeWord = {
  letter: string;   // uppercase, e.g. "G", "X"
  value: number;
  raw: string;      // original matched text
  start: number;    // char offset in stripped line
  end: number;
};

type Comment = {
  type: "paren" | "semicolon";
  text: string;
  start: number;    // char offset in original line
  end: number;
};

GCodeVirtualizer

Stateful interpreter. Tracks modal state and machine position across calls to processLine().

import { GCodeVirtualizer } from "@sienci/gviewer";

const virt = new GCodeVirtualizer({
  onLinearMove({ modals, start, end, transformedStart, transformedEnd }) {
    // called for every G0/G1 segment (subdivided at ≤5° A-axis steps)
  },
  onArcMove({ modals, start, end, center, max, plane, motion, ... }) {
    // called for every G2/G3 move
  },
});

for (const line of gcodeLines) {
  virt.processLine(line);
}

virt.getModals();             // current ModalState
virt.getPosition();           // current Position { X, Y, Z, A, B, C }
virt.getUniqueFeedRates();    // number[]
virt.getUniqueSpindleSpeeds(); // number[]
virt.getUniqueTools();        // number[]
virt.reset();                 // restore defaults

Modal state defaults: G0, G90 (absolute), G17 (XY plane), G21 (mm).

Unit conversion: When G20 is active, X/Y/Z values are automatically multiplied by 25.4 before being stored and passed to callbacks. A/B/C axes are not scaled.

Supported G-codes:

| Code | Effect | |---|---| | G0, G1 | Set motion mode (rapid / feed) | | G2, G3 | Arc CW / CCW | | G17, G18, G19 | Plane selection (XY / ZX / YZ) | | G20, G21 | Units (inches / mm) | | G90, G91 | Distance mode (absolute / incremental) | | G93, G94 | Feed mode | | G54–G59 | Coordinate system selection | | M3, M4, M5 | Spindle on/off | | M7, M8, M9 | Coolant | | T | Tool number | | F | Feed rate | | S | Spindle speed |


Geometry builders

Build Three.js-ready Float32Array position buffers from arrays of GCode lines. All functions are browser-safe (no Node.js APIs).

buildVerticesFromLines
import { buildVerticesFromLines } from "@sienci/gviewer";

const positions: Float32Array = buildVerticesFromLines(lines, {
  arcSegments: 30,   // tessellation quality for arcs
});
// Flat [x0,y0,z0, x1,y1,z1, ...] line-segment pairs
buildMovementVerticesFromLines

Separates rapid (G0) and cutting (G1/G2/G3) moves.

import { buildMovementVerticesFromLines } from "@sienci/gviewer";

const { rapid, cutting } = buildMovementVerticesFromLines(lines);
buildMovementGeometryFromLinesBatched

Async, progress-reporting version that also tracks per-line vertex ranges.

import { buildMovementGeometryFromLinesBatched } from "@sienci/gviewer";

const result = await buildMovementGeometryFromLinesBatched(lines, {
  arcSegments: 30,
  batch: {
    onProgress(processed, total) { /* update UI */ },
    yieldEveryLines: 50000,   // yield to event loop periodically
    shouldAbort: () => cancelled,
  },
});

// result.positions        — Float32Array of all vertices
// result.prefixEndVertex  — Int32Array; result.prefixEndVertex[i] is the
//                           cumulative vertex count after line i
// result.lineStartVertex  — Int32Array; first vertex for line i (-1 if none)
// result.lineEndVertex    — Int32Array; last vertex+1 for line i
// result.lineKind         — Uint8Array; 0=none, 1=rapid, 2=cut, 3=mixed
buildToolpathGeometryFromLinesBatched

Unified builder for both standard and laser modes. Returns separate rapid and per-power-bucket cut streams, each with per-line prefix arrays for progress visualization.

import { buildToolpathGeometryFromLinesBatched } from "@sienci/gviewer";

const result = await buildToolpathGeometryFromLinesBatched(lines, {
  laserMode: false,
  bucketCount: 16,   // power buckets for laser mode
  arcSegments: 30,
});

// result.rapid            — { positions, prefixEndVertex }
// result.cuts             — array of { positions, prefixEndVertex }
// result.cutBucketCount   — number of cut streams (1 in non-laser mode)
// result.maxPower         — max spindle speed seen (laser mode)
buildLaserGeometryFromLinesBatched

Laser-specific variant. Returns rapid positions plus opacity-bucketed cut streams.

import { buildLaserGeometryFromLinesBatched } from "@sienci/gviewer";

const result = await buildLaserGeometryFromLinesBatched(lines, {
  bucketCount: 16,
  baseOpacity: 0.9,
});

// result.rapidPositions         — Float32Array
// result.rapidPrefixEndVertex   — Int32Array
// result.buckets[i].positions   — Float32Array
// result.buckets[i].opacity     — number (0–1, proportional to power)
// result.buckets[i].prefixEndVertex

@sienci/gviewer/viewer — Three.js viewer

GCodeViewer

Full 3D viewer with orbit controls, grid, bounding box, bit marker, and ViewCube.

import { GCodeViewer } from "@sienci/gviewer/viewer";
import "@sienci/gviewer/viewer/viewcube.css";

const viewer = new GCodeViewer({
  id: "my-viewer",
  container: document.getElementById("viewer")!,
  options: { /* Partial<GCodeViewerOptions> */ },
  callbacks: {
    onProgress(event) {
      // event.state: "hidden" | "indeterminate" | "determinate"
    },
    onBoundsChanged(event) {
      // event.bounds: { min, max } | null
    },
  },
});
Loading GCode
await viewer.loadFromUrl("/path/to/file.gcode");
await viewer.loadFromFile(fileInputElement.files[0]);
await viewer.loadFromText("G21\nG0 X10 Y10\n...");
await viewer.loadFromLines(["G21", "G0 X10 Y10"]);
viewer.unload();
Camera
viewer.focusToModel();          // animate camera to fit the loaded geometry
viewer.resetCamera();           // return to initial position
viewer.snapCameraToView("front", { durationMs: 300 });
// views: "front" | "back" | "left" | "right" | "top" | "bottom"
//        | "front-top-left" | "front-top-right" | ... (14 presets)
Progress / simulation
// Show geometry only from lineIndex onward
viewer.hideUntilLine(lineIndex, "grey");  // grey out processed lines
viewer.hideUntilLine(lineIndex, "hide");  // hide processed lines
viewer.showAll();
viewer.resetColors();
Bit marker
viewer.setBitPosition({ x: 10, y: 5, z: 0 });
viewer.setBitPosition({ x: 10, y: 5, z: 0 }, { immediate: true });
viewer.setBitVisible(false);

The bit type is controlled via options.bit.type. Four types are available:

| Type | Description | |---|---| | "drill" | Real drill-bit mesh (STL) with metallic shading. Default. | | "laser" | Tapered beam with additive purple glow. Set automatically when mode.laser is enabled. | | "circle" | Simple sphere. | | "triangle" | Cone. |

Laser mode auto-switch: when mode.laser is set to true, the bit type automatically switches to "laser". When mode.laser is set back to false, the bit reverts to whatever type was active before laser mode was enabled.

viewer.setOptions({ mode: { laser: true } });
// bit type is now automatically "laser"

viewer.setOptions({ mode: { laser: false } });
// bit type is restored to its previous value (e.g. "drill")
Options
viewer.setOptions({
  units: "mm",             // "mm" | "in"
  mode: { laser: false },  // setting true auto-switches bit type to "laser"
  render: {
    theme: gCodeViewerThemePresets["tokyo-night"],
  },
  grid: { size: 1000, axisDepth: 200, labels: true },
  boundingBox: { visible: true, labels: true },
  camera: { fov: 45 },
});

viewer.getOptions();   // returns current options (readonly)
viewer.getBounds();    // { min, max } | null
viewer.resize();       // call after container resizes (automatic via ResizeObserver)
viewer.dispose();      // clean up Three.js resources and DOM elements
Themes
import { gCodeViewerThemePresets } from "@sienci/gviewer/viewer";

// Available presets:
// "dark" | "light" | "flexoki-dark" | "tokyo-night"
// "gruvbox-light" | "ayu-dark" | "ayu-light"

viewer.setOptions({ render: { theme: gCodeViewerThemePresets["ayu-dark"] } });
GCodeViewerOptions — full reference
type GCodeViewerOptions = {
  units: "mm" | "in";
  mode: { laser: boolean };
  bit: {
    enabled: boolean;
    type: "drill" | "laser" | "circle" | "triangle";  // default: "drill"
    size: number;        // world units (default: 4.05)
    opacity: number;     // 0–1
    tweenMs: number;     // animation duration
    colorSource: "cutting" | "rapid" | "custom";
    color: string;
  };
  progress: { mode: "hide" | "grey" };
  grid: { size: number; axisDepth: number; labels: boolean };  // default size: 1000
  boundingBox: { visible: boolean; labels: boolean };
  geometry: {
    arcSegments: number;
    batching: { progressEveryLines: number; yieldEveryLines: number };
  };
  render: { antialias: boolean; theme: GCodeViewerTheme };
  camera: {
    fov: number;
    focusDurationMs: number;
    orbit: { enableDamping: boolean };
    initialPosition: { x: number; y: number; z: number };
  };
};

@sienci/gviewer/react — React component

import { GCodeVisualizer } from "@sienci/gviewer/react";
import type { GCodeViewerHandle, GCodeViewerOptions } from "@sienci/gviewer/viewer";
import "@sienci/gviewer/viewer/viewcube.css";

Props

type GCodeVisualizerProps = {
  id: string;
  options?: Partial<GCodeViewerOptions>;
  callbacks?: GCodeViewerCallbacks;
  className?: string;
  style?: React.CSSProperties;
};

The component forwards a GCodeViewerHandle ref that exposes the full GCodeViewer imperative API:

const ref = useRef<GCodeViewerHandle>(null);

<GCodeVisualizer id="viewer" ref={ref} style={{ height: 500 }} />

// Later:
ref.current?.loadFromText(gcode);
ref.current?.focusToModel();
ref.current?.hideUntilLine(currentLine, "grey");
ref.current?.snapCameraToView("top");
ref.current?.setBitPosition({ x, y, z });

options and callbacks props are synced to the viewer whenever they change (via useEffect).


Development

npm install
npm run build        # compile + generate types
npm test             # run all test suites
npm run test:watch   # watch mode
npm run dev          # rebuild on file changes
npm run demo         # start the demo dev server
npm run build:demo   # build the demo for deployment

The demo is automatically built and published to GitHub Pages on every push to master.

Output

dist/
  gviewer.js / gviewer.cjs       — core (parser, virtualizer, geometry)
  viewer.js  / viewer.cjs        — Three.js viewer
  react.js   / react.cjs         — React component
  viewcube.css                   — ViewCube styles
  types/                         — TypeScript declarations

License

MIT