Skip to content

STL & Slicer Crash Course for Forma3D Connect

Target Audience: Developers with no 3D printing background
Goal: Understand the 3D printing pipeline from parametric model to physical print


Table of Contents

  1. The Big Picture
  2. 3D Printing Fundamentals
  3. STL: The 3D Model Format
  4. 3MF: The Modern Alternative
  5. G-code: Printer Instructions
  6. Slicing: Turning Models into Instructions
  7. JSCAD: Parametric 3D Modeling in JavaScript
  8. gridflock-core: Our Geometry Library
  9. BambuStudio CLI: The Slicer
  10. The Slicer Container
  11. Printer Profiles
  12. The Full Pipeline
  13. Gridfinity: The Grid System
  14. Debugging and Troubleshooting
  15. Quick Reference

1. The Big Picture

Forma3D Connect generates custom 3D-printable baseplates to order. The pipeline looks like this:

uml diagram

The key insight: we don't design static 3D models. We write code that generates geometry based on customer parameters. Every order produces a unique model.


2. 3D Printing Fundamentals

How FDM Printing Works

FDM (Fused Deposition Modeling) is the printing technology used by our Bambu Lab printers:

  1. A nozzle heats plastic filament (PLA) to ~210°C
  2. The melted plastic is extruded through the nozzle onto a build plate
  3. The nozzle traces a path in the X/Y plane, depositing a thin line of plastic
  4. After one layer is complete, the nozzle moves up by one layer height (e.g. 0.2mm)
  5. The next layer is printed on top, fusing with the layer below
  6. Repeat hundreds of times until the object is complete

Key Parameters

Parameter What it means Typical value
Layer height Thickness of each printed layer 0.16–0.28mm
Nozzle diameter Width of the extrusion 0.4mm or 0.6mm
Infill How solid the inside is (0%=hollow, 100%=solid) 15–30%
Wall loops Number of outer shell layers 2–4
Print speed How fast the nozzle moves 100–300 mm/s
Bed size Maximum printable area 256×256mm (Bambu A1)
Build plate temp Heated bed temperature 55°C for PLA
Nozzle temp Filament melting temperature 210°C for PLA

Why This Matters for Code

These parameters directly affect:

  • Maximum model size — can't be larger than the printer bed
  • Print time — faster speeds and thicker layers = faster prints, lower quality
  • Model strength — more walls and infill = stronger but heavier
  • Cost — more material = higher cost

Our system stores these as printer profiles that the slicer reads.


3. STL: The 3D Model Format

STL (STereoLithography) is the standard format for 3D printing. It describes a 3D shape as a collection of triangles.

How STL Works

Every 3D surface is approximated by triangular facets:

        /\
       /  \
      /    \
     /______\
    /\      /\
   /  \    /  \
  /    \  /    \
 /______\/______\

A cube needs 12 triangles (2 per face × 6 faces). A cylinder needs many more triangles to approximate the curved surface. A complex baseplate might have 50,000+ triangles.

Binary STL Format

The file contains:

[80 bytes]     Header (usually ignored)
[4 bytes]      Number of triangles (uint32)
For each triangle:
  [12 bytes]   Normal vector (3 × float32)
  [12 bytes]   Vertex 1 (x, y, z as float32)
  [12 bytes]   Vertex 2 (x, y, z as float32)
  [12 bytes]   Vertex 3 (x, y, z as float32)
  [2 bytes]    Attribute byte count (usually 0)

STL in Our Code

From libs/gridflock-core/src/lib/serializer.ts:

import { serialize } from '@jscad/stl-serializer';
import type { Geom3 } from '@jscad/modeling/src/geometries/types';

export function serializeToStl(geometry: Geom3): Buffer {
  const rawData = serialize({ binary: true }, geometry);
  if (rawData.length === 0) {
    throw new Error('STL serialization produced no data');
  }
  const buffers = rawData.map((chunk) => {
    if (typeof chunk === 'string') {
      return Buffer.from(chunk, 'binary');
    }
    return Buffer.from(chunk);
  });
  return Buffer.concat(buffers);
}

export function isValidBinaryStl(buffer: Buffer): boolean {
  if (buffer.length < 84) return false;
  const triangleCount = buffer.readUInt32LE(80);
  const expectedSize = 84 + triangleCount * 50;
  return buffer.length === expectedSize;
}

export function getStlTriangleCount(buffer: Buffer): number {
  if (buffer.length < 84) return 0;
  return buffer.readUInt32LE(80);
}

STL Limitations

  • No units — the file has no concept of mm/inches; interpretation is by convention (we use mm)
  • No color/material — just geometry
  • No structure — can't tell which triangle belongs to which feature
  • Large files — each triangle is 50 bytes; complex models can be tens of MB

4. 3MF: The Modern Alternative

3MF (3D Manufacturing Format) is an XML-based format that addresses STL's limitations:

  • Units — explicitly specifies millimeters
  • Metadata — material, color, print settings
  • Compression — ZIP-based, much smaller files
  • Multiple objects — can contain several models in one file

3MF in Our Pipeline

BambuStudio outputs 3MF files (not G-code directly). The 3MF contains:

  • The sliced model geometry
  • Layer-by-layer toolpath data
  • Print settings that were used
  • Thumbnail preview images

SimplyPrint (our print farm manager) accepts 3MF files for printing on Bambu Lab printers.


5. G-code: Printer Instructions

G-code is the low-level language that controls the printer. It's a series of movement and control commands:

G28          ; Home all axes
M104 S210    ; Set nozzle temperature to 210°C
M140 S55     ; Set bed temperature to 55°C
M109 S210    ; Wait for nozzle to reach temperature

G1 X50 Y50 F3000    ; Move to position (50, 50) at 3000 mm/min
G1 Z0.2 F600        ; Move nozzle to first layer height
G1 X100 E5 F1500    ; Extrude 5mm of filament while moving to X=100
G1 Y100 E10         ; Continue extruding to Y=100

Why You Probably Won't Touch G-code

In our system, G-code is an intermediate representation inside the 3MF file. BambuStudio generates it during slicing, and SimplyPrint sends it to the printer. You'll rarely need to look at raw G-code.


6. Slicing: Turning Models into Instructions

Slicing is the process of converting a 3D model (STL) into printable instructions (G-code/3MF). The slicer:

  1. Orients the model on the build plate (optimal orientation for strength/speed)
  2. Slices the model into horizontal layers (e.g. every 0.2mm)
  3. Generates toolpaths for each layer:
  4. Outer walls (perimeters)
  5. Inner walls
  6. Infill patterns
  7. Support structures (if needed)
  8. Travel moves (non-printing movements)
  9. Calculates temperatures, speeds, accelerations, retractions
  10. Outputs the result as G-code (inside a 3MF for Bambu printers)

Visual Example

3D Model (STL)          Sliced Layers              Toolpath (one layer)

    ┌─────────┐         ┌─────────┐  Layer N      ┌─────────┐
   /         /│         │█████████│               │░░░░░░░░░│ ← Outer wall
  /         / │         │█████████│               │░┌─────┐░│ ← Inner wall
 /         /  │   →     │█████████│  Layer 2  →   │░│/\/\/│░│ ← Infill
│         │   │         │█████████│               │░│/\/\/│░│
│         │  /          │█████████│  Layer 1      │░└─────┘░│
│         │ /           └─────────┘               │░░░░░░░░░│
└─────────┘                                       └─────────┘

Slicer Settings That Matter

Setting Effect Our default
Layer height Quality vs speed tradeoff 0.20mm (Standard)
Infill % Strength vs weight/time 15%
Wall loops Shell thickness 3
Top/bottom layers Surface quality 4
Print speed Time vs quality Profile-dependent
Support For overhangs >45° Off (baseplates don't need it)

7. JSCAD: Parametric 3D Modeling in JavaScript

JSCAD is a JavaScript library for creating 3D geometry programmatically. Instead of drawing shapes in a GUI tool, you write code that describes them.

Core Concepts

Primitives — basic shapes:

import { cuboid, cylinder, sphere } from '@jscad/modeling/src/primitives';

// A box: 10mm wide, 5mm deep, 3mm tall, centered at origin
const box = cuboid({ size: [10, 5, 3] });

// A cylinder: 3mm radius, 5mm tall
const cyl = cylinder({ radius: 3, height: 5, segments: 32 });

// A sphere: 5mm radius
const ball = sphere({ radius: 5, segments: 32 });

Transforms — position, rotate, scale:

import { translate, rotate, scale } from '@jscad/modeling/src/transforms';

// Move the box 20mm along X
const movedBox = translate([20, 0, 0], box);

// Rotate 45 degrees around Z axis
const rotatedBox = rotate([0, 0, Math.PI / 4], box);

Boolean operations — combine shapes:

import { union, subtract, intersect } from '@jscad/modeling/src/booleans';

// Combine two shapes into one
const combined = union(box, cyl);

// Cut a hole (subtract cylinder from box)
const withHole = subtract(box, cyl);

// Keep only the overlapping region
const overlap = intersect(box, cyl);

Why JSCAD?

Alternative Why we chose JSCAD instead
OpenSCAD Requires external binary, separate language
Three.js Rendering-focused, not CSG/manufacturing
CAD software (Fusion 360) Can't be automated, requires GUI
Python (CadQuery) Would need a Python runtime in our Node.js stack

JSCAD is pure JavaScript, runs in Node.js, and produces geometry that can be directly serialized to STL. No external binaries, no separate processes.


8. gridflock-core: Our Geometry Library

libs/gridflock-core is the shared library that generates Gridfinity baseplates using JSCAD.

Architecture

libs/gridflock-core/src/lib/
├── types.ts              # All type definitions (GridFlockParams, PlateSpec, etc.)
├── generator.ts          # Main entry: generatePlateStl(), generateGridFlockPlate()
├── preview-generator.ts  # Preview STL generation (lower quality, faster)
├── serializer.ts         # JSCAD geometry → binary STL buffer
├── stl-combiner.ts       # Merge multiple STL buffers (multi-plate layouts)
├── plate-calculator.ts   # Calculate how to split a surface into printable plates
├── border-generator.ts   # Generate border strips for exact-fit plates
└── geometry/
    ├── base-plate.ts     # Base plate with Gridfinity profile
    ├── grid-cells.ts     # Individual grid cell geometry
    ├── connectors.ts     # Puzzle-piece connectors between plates
    ├── magnets.ts        # Magnet holes for plate attachment
    └── numbering.ts      # Embossed plate numbers

Key Types

From libs/gridflock-core/src/lib/types.ts:

/** Standard Gridfinity grid unit size in mm */
export const GRID_UNIT_MM = 42;

/** Standard Gridfinity profile height above z=0 in mm */
export const PROFILE_HEIGHT_MM = 4.65;

export interface GridFlockParams {
  gridSize: [number, number];       // Grid size in units [columns, rows]
  connectorType: ConnectorType;     // 'intersection-puzzle' | 'edge-puzzle' | 'none'
  magnets: MagnetParams;            // Magnet hole configuration
  edgePuzzle: EdgePuzzleParams;     // Puzzle connector dimensions
  solidBase: number;                // Base thickness in mm
  cornerRadius: number;             // Plate corner radius in mm
  connectorEdges: [boolean, boolean, boolean, boolean]; // [N, E, S, W]
  border: BorderDimensions;         // Border widths for exact-fit
  plateNumber: false | number;      // Plate numbering
}

export interface PlateSpec {
  index: number;                    // 1-based plate index
  gridSize: [number, number];       // Grid size for this plate
  position: [number, number];       // Position in assembly [col, row]
  dimensions: [number, number];     // Physical size in mm
  connectorEdges: [boolean, boolean, boolean, boolean];
  border: BorderDimensions;
}

export interface GenerationResult {
  geometry: Geom3;                  // JSCAD 3D geometry
  stlBuffer: Buffer;               // Binary STL buffer
  filename: string;                 // Suggested filename
}

Generation Flow

import { generatePlateStl } from '@forma3d/gridflock-core';

// 1. Define plate specification (from plate calculator)
const plate: PlateSpec = {
  index: 1,
  gridSize: [4, 3],
  position: [0, 0],
  dimensions: [168, 126],
  connectorEdges: [false, true, true, false],
  border: { left: 0, right: 0, front: 0, back: 0 },
};

// 2. Generate STL buffer
const result: GenerationResult = generatePlateStl(plate, {
  connectorType: 'edge-puzzle',
  magnets: { enabled: true, style: 'press-fit', diameter: 6, height: 2, topWall: 0.4, bottomFloor: 0.5 },
  edgePuzzle: { count: 2, tabSize: [4, 3], bridgeSize: [2, 2], gap: 0.15, heightFemale: 2.5 },
});

// result.stlBuffer is a binary Buffer ready to send to the slicer
// result.filename is e.g. "plate-1-4x3.stl"

Multi-Plate Sets

When a customer orders a surface larger than the printer bed, the plate calculator splits it into multiple printable plates:

Customer orders: 400mm × 300mm surface

Printer bed: 256mm × 256mm

Plate calculator produces:
┌────────────┬────────────┐
│  Plate 1   │  Plate 2   │
│  5×4 units │  5×4 units │
│  (210×168) │  (210×168) │
├────────────┼────────────┤
│  Plate 3   │  Plate 4   │
│  5×3 units │  5×3 units │
│  (210×126) │  (210×126) │
└────────────┴────────────┘

Each plate gets puzzle connectors on shared edges

9. BambuStudio CLI: The Slicer

BambuStudio is the official slicer for Bambu Lab 3D printers. We use its command-line interface (CLI) inside a Docker container to slice STL files into 3MF.

CLI Invocation

/opt/bambustudio/bin/bambu-studio \
  --orient 1 \                              # Auto-orient the model on the bed
  --arrange 1 \                             # Auto-arrange (center on bed)
  --load-settings "machine.json;process.json" \  # Machine + process profiles
  --load-filaments "filament.json" \        # Filament profile
  --slice 0 \                              # Slice plate 0
  --export-3mf output.3mf \               # Output format
  input.stl                                # Input file

CLI Flags

Flag Purpose
--orient 1 Auto-orient the model for optimal printing
--arrange 1 Auto-arrange and center on bed
--load-settings Semicolon-separated machine + process profile paths
--load-filaments Filament profile path
--slice 0 Slice the first plate (0-indexed)
--export-3mf Output as 3MF file

Profile Inheritance

BambuStudio profiles use an inheritance system. A profile can declare "inherits": "Parent Profile Name", but the CLI does not resolve inheritance. Our slicer container handles this with a flattenProfile() function that:

  1. Reads the leaf profile
  2. Walks the inherits chain to the root ancestor
  3. Merges all profiles (child overrides parent)
  4. Writes a flattened JSON file for the CLI

Without flattening, critical settings like printable_area, filament_max_volumetric_speed, and speed limits would be missing, causing extremely conservative defaults.

Virtual Display

BambuStudio requires a GUI display even in CLI mode. The container runs Xvfb (X Virtual Framebuffer) to provide a fake display:

Xvfb :99 -screen 0 1024x768x24 &
export DISPLAY=:99

10. The Slicer Container

The slicer runs as a standalone Docker container with a thin Express REST API.

Container Architecture

uml diagram

API Endpoints

POST /slice

Slices an STL file into a 3MF file.

Request: multipart/form-data

Field Type Description
stl File Binary STL file (max 100MB)
machineProfile String Path to machine profile JSON
processProfile String Path to process profile JSON
filamentProfile String Path to filament profile JSON

Response: Binary 3MF buffer (application/octet-stream)

Example (from SlicerClient):

const formData = new FormData();
formData.append('stl', stlBuffer, { filename: 'plate.stl' });
formData.append('machineProfile', '/profiles/bambu-a1/machine.json');
formData.append('processProfile', '/profiles/bambu-a1/process_020.json');
formData.append('filamentProfile', '/profiles/bambu-a1/filament_pla.json');

const response = await this.httpService.post(`${slicerUrl}/slice`, formData);
const threeMfBuffer = response.data;

GET /health

Returns health status, checking that BambuStudio binary exists and profiles directory is accessible.

GET /profiles

Lists available printer profile directories.

Dockerfile

FROM linuxserver/bambustudio:01.08.03 AS base

RUN apt-get update && apt-get install -y curl xvfb && \
    curl -fsSL https://deb.nodesource.com/setup_20.x | bash - && \
    apt-get install -y nodejs && \
    apt-get clean && rm -rf /var/lib/apt/lists/*

COPY api/ /app/api/
WORKDIR /app/api
RUN npm install --production

COPY profiles/ /profiles/
COPY scripts/entrypoint.sh /app/entrypoint.sh
RUN chmod +x /app/entrypoint.sh

ENV LD_LIBRARY_PATH=/opt/bambustudio/bin

EXPOSE 3010

HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \
    CMD curl -f http://localhost:3010/health || exit 1

ENTRYPOINT ["/app/entrypoint.sh"]

The base image linuxserver/bambustudio includes the BambuStudio binary and its bundled printer profiles.


11. Printer Profiles

Printer profiles configure the slicer for a specific printer, print quality, and filament.

Profile Types

Type What it configures Example
Machine Printer hardware: bed size, nozzle, max speeds bambu-a1/machine.json
Process Print quality: layer height, speeds, infill, walls bambu-a1/process_020.json
Filament Material: temperatures, volumetric flow, cooling bambu-a1/filament_pla.json

Profile Directory Structure

deployment/slicer/profiles/
├── bambu-a1/                    # Bambu Lab A1 (256×256mm bed)
│   ├── machine.json             # 0.4mm nozzle
│   ├── machine_06.json          # 0.6mm nozzle variant
│   ├── process_020.json         # 0.20mm Standard layer height
│   ├── process_016.json         # 0.16mm Optimal (higher quality)
│   ├── process_028_06.json      # 0.28mm Draft with 0.6mm nozzle
│   ├── filament_pla.json        # PLA Basic filament
│   └── filament_pla_06.json     # PLA for 0.6mm nozzle
├── bambu-a1-mini/               # Bambu Lab A1 mini (180×180mm bed)
│   └── machine.json
└── bambu-p1s/                   # Bambu Lab P1S (256×256mm bed)
    └── machine.json

Machine Profile Example

From deployment/slicer/profiles/bambu-a1/machine.json:

{
  "type": "machine",
  "name": "Bambu Lab A1 0.4 nozzle",
  "from": "system",
  "setting_id": "GKA01",
  "inherits": "Bambu Lab A1 0.4 nozzle",
  "printer_model": "Bambu Lab A1",
  "printer_variant": "0.4",
  "nozzle_diameter": ["0.4"],
  "curr_bed_type": "Textured PEI Plate",
  "printable_area": ["0x0", "256x0", "256x256", "0x256"],
  "printable_height": "256",
  "default_filament_profile": ["Bambu PLA Basic @BBL A1"],
  "default_print_profile": "0.20mm Standard @BBL A1",
  "machine_max_speed_x": ["500"],
  "machine_max_speed_y": ["500"],
  "machine_max_acceleration_extruding": ["20000"]
}

Process Profile Example

Process profiles control print quality and speed. The 0.20mm Standard profile is the most commonly used:

{
  "type": "process",
  "name": "0.20mm Standard @BBL A1 (Forma3D)",
  "inherits": "0.20mm Standard @BBL A1",
  "layer_height": "0.2",
  "initial_layer_print_height": "0.2",
  "wall_loops": "3",
  "sparse_infill_density": "15%",
  "sparse_infill_pattern": "grid",
  "top_shell_layers": "4",
  "bottom_shell_layers": "4",
  "outer_wall_speed": "200",
  "inner_wall_speed": "300",
  "sparse_infill_speed": "300"
}

Filament Profile Example

{
  "type": "filament",
  "name": "Bambu PLA Basic @BBL A1 (Forma3D)",
  "inherits": "Bambu PLA Basic @BBL A1",
  "nozzle_temperature": ["210"],
  "nozzle_temperature_initial_layer": ["210"],
  "bed_temperature": ["55"],
  "filament_max_volumetric_speed": ["21"],
  "filament_type": ["PLA"],
  "filament_density": ["1.24"],
  "filament_cost": ["20"]
}

Inheritance Chain

Profiles inherit from BambuStudio's bundled defaults. Our custom profiles only override specific values:

Bundled "Bambu PLA Basic @BBL A1"    ← Base (all defaults)
    ↑ inherits
Custom "Bambu PLA Basic @BBL A1 (Forma3D)"    ← Override volumetric flow, temps

The flattenProfile() function in the slicer API resolves this chain before passing to the CLI.


12. The Full Pipeline

Here's how everything connects in the GridFlock pipeline service:

Step-by-Step Flow

uml diagram

Pipeline Service (simplified)

From apps/gridflock-service/src/gridflock/gridflock-pipeline.service.ts:

@Injectable()
export class GridflockPipelineService {
  async processGridFlockOrder(params: {
    orderId: string;
    sku: string;
    widthMm: number;
    depthMm: number;
    tenantId: string;
  }): Promise<void> {
    // 1. Check if mapping already exists (skip generation)
    const existing = await this.orderServiceClient.getProductMapping(params.sku);
    if (existing) {
      this.logger.log(`Mapping already exists for SKU ${params.sku}`);
      return;
    }

    // 2. Load tenant print settings from SystemConfig
    const printSettings = await this.loadPrintSettings(params.tenantId);

    // 3. Calculate plate set
    const plateSet = calculatePlateSet({
      targetWidthMm: params.widthMm,
      targetDepthMm: params.depthMm,
      printerProfile: printSettings.printerProfile,
      connectorType: 'edge-puzzle',
      magnets: printSettings.magnets,
      edgePuzzle: printSettings.edgePuzzle,
    });

    // 4. Process each plate: generate → slice → upload
    for (const plate of plateSet.plates) {
      const stlResult = generatePlateStl(plate, {
        connectorType: 'edge-puzzle',
        magnets: printSettings.magnets,
        edgePuzzle: printSettings.edgePuzzle,
      });

      const sliceResult = await this.slicerClient.slice({
        stlBuffer: stlResult.stlBuffer,
        machineProfile: printSettings.machineProfilePath,
        processProfile: printSettings.processProfilePath,
        filamentProfile: printSettings.filamentProfilePath,
      });

      await this.printServiceClient.uploadFileToSimplyPrint(
        sliceResult.gcodeBuffer,
        stlResult.filename.replace('.stl', '.3mf'),
        printSettings.uploadFolderId,
      );
    }

    // 5. Create product mapping
    await this.orderServiceClient.createProductMapping(params.sku, simplyPrintFileId);

    // 6. Publish event
    await this.eventBus.publish({
      eventType: SERVICE_EVENTS.GRIDFLOCK_MAPPING_READY,
      data: { orderId: params.orderId, sku: params.sku },
    });
  }
}

Service Dependencies

uml diagram


13. Gridfinity: The Grid System

Gridfinity is an open-source modular storage system designed by Zack Freedman. Our baseplates are Gridfinity-compatible.

Key Dimensions

Measurement Value Description
Grid unit 42mm × 42mm Standard cell size
Profile height 4.65mm Height of the mounting lip
Magnet diameter 6mm Standard magnet size
Magnet height 2mm Standard magnet thickness

Plate Features

A generated baseplate includes:

  • Grid cells — 42mm × 42mm recesses that bins snap into
  • Mounting profile — lip around each cell for bin retention
  • Magnet holes — optional press-fit magnet pockets in each cell
  • Connectors — puzzle-piece edges for joining multiple plates
  • Plate numbers — embossed on the bottom for assembly guidance
  • Corner rounding — only on outer corners, not where plates connect
  • Border strips — for exact-fit in drawers (non-standard 42mm spacing)

Connector Types

Type Description Use case
none No connectors, flat edges Single-plate orders
edge-puzzle Puzzle-piece tabs on shared edges Multi-plate assemblies
intersection-puzzle Cross-shaped connectors at grid intersections Alternative joining method

14. Debugging and Troubleshooting

Common Issues

Slicer timeout (504) - The 120-second timeout expired. Usually caused by very complex models (many triangles) or slicer container under load. - Check docker logs slicer for BambuStudio stderr output.

Empty STL buffer - JSCAD serialization produced no data. Usually a geometry error (degenerate shape, zero-volume solid). - Check the GridFlockParams — are dimensions valid? Is gridSize non-zero?

Profile not found (400) - The requested profile path doesn't exist in the container. Check GET /profiles on the slicer. - Verify profile paths in SystemConfig match what's mounted in the container.

Inheritance not resolved - The slicer's flattenProfile() walks the inheritance chain. If a parent profile is missing, the flattening stops early and settings are incomplete. - Check for missing files in deployment/slicer/profiles/ and the bundled /opt/bambustudio/resources/profiles/BBL/ directory.

Useful Commands

# Check slicer health
curl http://localhost:3010/health

# List available profiles
curl http://localhost:3010/profiles

# Slice a test file
curl -X POST http://localhost:3010/slice \
  -F "stl=@test-plate.stl" \
  -F "machineProfile=/profiles/bambu-a1/machine.json" \
  -F "processProfile=/profiles/bambu-a1/process_020.json" \
  -F "filamentProfile=/profiles/bambu-a1/filament_pla.json" \
  -o output.3mf

# Check slicer container logs
docker logs slicer --tail 100

# Validate an STL file (Node.js)
node -e "
  const fs = require('fs');
  const buf = fs.readFileSync('plate.stl');
  const triangles = buf.readUInt32LE(80);
  const expected = 84 + triangles * 50;
  console.log({ size: buf.length, triangles, valid: buf.length === expected });
"

Viewing STL Files

For visual inspection of generated STL files:

  • Online: 3D Viewer Online — drag and drop STL files
  • Desktop: PrusaSlicer, BambuStudio, or Blender (free)
  • VS Code: 3D Viewer extension

15. Quick Reference

File Formats

Format Purpose Size Contains
STL 3D geometry 50KB–5MB Triangles only
3MF Print-ready package 1–10MB Geometry + toolpaths + settings
G-code Printer instructions 5–50MB Movement commands (inside 3MF)

Key Libraries and Tools

Component Technology Location
Geometry generation JSCAD (@jscad/modeling) libs/gridflock-core/
STL serialization @jscad/stl-serializer libs/gridflock-core/src/lib/serializer.ts
Slicing BambuStudio CLI deployment/slicer/
Slicer API Express.js deployment/slicer/api/
Slicer HTTP client Axios via NestJS HttpService libs/service-common/src/lib/service-client/slicer.client.ts
Pipeline orchestration NestJS service apps/gridflock-service/src/gridflock/gridflock-pipeline.service.ts
Print farm SimplyPrint API libs/api-client/

Data Flow Summary

uml diagram

Profile Cheat Sheet

Printer Machine Process Filament
Bambu A1 (0.4mm) machine.json process_020.json filament_pla.json
Bambu A1 (0.6mm) machine_06.json process_028_06.json filament_pla_06.json
Bambu A1 (fine) machine.json process_016.json filament_pla.json
Bambu A1 mini machine.json (use A1 process) (use A1 filament)
Bambu P1S machine.json (use A1 process) (use A1 filament)

Next Steps

  1. Read gridflock-core types: Start with libs/gridflock-core/src/lib/types.ts for the full type landscape
  2. Trace a generation: Follow generatePlateStl() from parameters to STL buffer
  3. Inspect the slicer: Read deployment/slicer/api/routes/slice.js for the full slicing flow
  4. View a real STL: Generate a plate locally and view it in 3D Viewer
  5. Check profiles: Browse deployment/slicer/profiles/bambu-a1/ to understand print settings
  6. Follow the pipeline: Read apps/gridflock-service/src/gridflock/gridflock-pipeline.service.ts

Resources