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¶
- The Big Picture
- 3D Printing Fundamentals
- STL: The 3D Model Format
- 3MF: The Modern Alternative
- G-code: Printer Instructions
- Slicing: Turning Models into Instructions
- JSCAD: Parametric 3D Modeling in JavaScript
- gridflock-core: Our Geometry Library
- BambuStudio CLI: The Slicer
- The Slicer Container
- Printer Profiles
- The Full Pipeline
- Gridfinity: The Grid System
- Debugging and Troubleshooting
- Quick Reference
1. The Big Picture¶
Forma3D Connect generates custom 3D-printable baseplates to order. The pipeline looks like this:
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:
- A nozzle heats plastic filament (PLA) to ~210°C
- The melted plastic is extruded through the nozzle onto a build plate
- The nozzle traces a path in the X/Y plane, depositing a thin line of plastic
- After one layer is complete, the nozzle moves up by one layer height (e.g. 0.2mm)
- The next layer is printed on top, fusing with the layer below
- 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:
- Orients the model on the build plate (optimal orientation for strength/speed)
- Slices the model into horizontal layers (e.g. every 0.2mm)
- Generates toolpaths for each layer:
- Outer walls (perimeters)
- Inner walls
- Infill patterns
- Support structures (if needed)
- Travel moves (non-printing movements)
- Calculates temperatures, speeds, accelerations, retractions
- 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:
- Reads the leaf profile
- Walks the
inheritschain to the root ancestor - Merges all profiles (child overrides parent)
- 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¶
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¶
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¶
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¶
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¶
- Read gridflock-core types: Start with
libs/gridflock-core/src/lib/types.tsfor the full type landscape - Trace a generation: Follow
generatePlateStl()from parameters to STL buffer - Inspect the slicer: Read
deployment/slicer/api/routes/slice.jsfor the full slicing flow - View a real STL: Generate a plate locally and view it in 3D Viewer
- Check profiles: Browse
deployment/slicer/profiles/bambu-a1/to understand print settings - Follow the pipeline: Read
apps/gridflock-service/src/gridflock/gridflock-pipeline.service.ts