Skip to main content

Installation

FluidCAD ships as a desktop app for Windows, Linux and macOS, and as an npm package for running it from a Node.js project in your browser. Pick the download for your system below, or skip to the npm installation if you prefer to work from a terminal.

Models are .part.js files (parts) and .assembly.js files (assemblies), written by the viewport as you work.

The links below point at the current release, FluidCAD 0.0.43; every build is also on the GitHub releases page. The app updates itself from there afterwards: it checks quietly in the background and installs the next version when you quit.

Windows

Run the installer and follow the prompts. The installer is not code-signed, so Windows SmartScreen shows Windows protected your PC on first launch; choose More info → Run anyway.

Linux

For the AppImage, mark it executable (chmod +x FluidCAD-*.AppImage) and run it. For the .deb, install it with sudo apt install ./FluidCAD-*.deb. The AppImage updates itself; the .deb is updated by installing the next release's package.

macOS

Open the disk image and drag FluidCAD into Applications. The build is signed and notarized, so it opens without a Gatekeeper prompt.

npm installation

If you would rather run FluidCAD from a Node.js project and model in your browser, install the fluidcad package into a folder, start its server, and model in the tab it opens.

Prerequisites

  • Node.js version 24 or higher
  • npm (comes with Node.js)

Create a new project

mkdir my-hinge && cd my-hinge
npm i fluidcad
npx fluidcad init
npx fluidcad serve

init creates a project with the following structure:

my-hinge/
├── package.json
├── jsconfig.json
├── fluidcad.json ← project configuration (engine pin, unit)
├── init.js ← required project entry point
└── box.part.js ← your first model

serve starts the FluidCAD server and opens the viewport in your browser. Leave it running while you model; press Ctrl+C to stop it.

FlagDescriptionDefault
-w, --workspace <path>Path to your projectCurrent directory
-p, --port <port>Server port — if it's already taken, the next free one is used3100
--no-openDon't launch a browser — for CI and remote sessionsopens by default

To run from elsewhere, point -w at your project: npx fluidcad serve -w ./my-app.

fluidcad.json records which FluidCAD version the project was authored against and, optionally, its default length unit. See Project configuration.

note

FluidCAD model files use the .part.js extension (assemblies use .assembly.js). The viewer only picks up files with this naming convention.

note

FluidCAD is installed per-project and run with npx fluidcad …; a global install (npm install -g fluidcad) is not supported. The engine and your editor's type hints both resolve from the project's node_modules, so the package must live there. If you see Cannot find module 'fluidcad' imported from …, run npm i fluidcad inside the project.

The init.js file

Every FluidCAD project must have an init.js file at its root. It initializes the engine and must be present before any .part.js file can run:

import { init } from 'fluidcad';

export default await init();

Do not modify this file — it is generated by npx fluidcad init and is required for the viewer to work. Project configuration covers what it does and what else lives in the project folder.

The starter file

Open box.part.js. This is the file that was generated for you, and it is what the viewport shows when the server starts:

import { arc, line, extrude, shell, sketch, part } from "fluidcad/core";
import { radius, distance, equal, vertical, horizontal, tangent, coincident } from 'fluidcad/constraints';


export const box = part("Box", () => {
sketch("xy", () => {
const l1 = line([-30, -20], [30, -20]);
const a1 = arc([30, -20], [40, -10], [30, -10]);
const l2 = line([40, -10], [40, 10]);
const a2 = arc([40, 10], [30, 20], [30, 10]);
const l3 = line([30, 20], [-30, 20]);
const a3 = arc([-30, 20], [-40, 10], [-30, 10]);
const l4 = line([-40, 10], [-40, -10]);
const a4 = arc([-40, -10], [-30, -20], [-30, -10]);
coincident(l1.end(), a1.start());
coincident(a1.end(), l2.start());
coincident(l2.end(), a2.start());
coincident(a2.end(), l3.start());
coincident(l3.end(), a3.start());
coincident(a3.end(), l4.start());
coincident(l4.end(), a4.start());
coincident(a4.end(), l1.start());
tangent(l1, a1);
tangent(a1, l2);
tangent(l2, a2);
tangent(a2, l3);
tangent(l3, a3);
tangent(a3, l4);
tangent(l4, a4);
tangent(a4, l1);
horizontal(l1);
horizontal(l3);
vertical(l2);
vertical(l4);
equal(a1, a2);
equal(a1, a3);
equal(a1, a4);
distance(l4, l2, 80);
radius(a1, 10);
});

const e = extrude(25);
shell(-2, e.endFaces());
});

Here's what each part does:

  1. part("Box", () => { ... }) declares a named part and exports it as box. Everything inside the callback builds that part, and the export is what an assembly would later insert().
  2. sketch("xy", () => { ... }) starts a sketch on the XY plane (the horizontal plane). The geometry inside is drawn with explicit coordinates, and the constraint statements pin it down.
  3. The four line(...) and four arc(...) calls draw a rounded rectangle: each arc(start, end, center) sits between two sides. The coincident statements join the ends into a closed loop and tangent makes every corner blend smoothly.
  4. horizontal, vertical, equal, distance, radius square the sides up, make all four corners the same size, set the width between the left and right sides to 80, and dimension the corner radius to 10.
  5. extrude(25) takes the sketch and pulls it up 25 units into a 3D solid. We store the result in e so we can reference its faces later.
  6. shell(-2, e.endFaces()) hollows out the solid with a wall thickness of 2, removing the top face (endFaces()). The negative value means the shell goes inward.

The result is a small open-topped tray. In the viewport, every one of those statements is a row in the History panel, and the sketch's constraints fold behind an N constraints row under the sketch.

Beyond serve

The same CLI writes STEP, STL, and PNG files without opening the UI, and runs the MCP server that lets AI agents drive your workspace:

npx fluidcad export step # every shape → <entry>.step
npx fluidcad export png --view front --open

See the CLI Reference for every command and flag, or run npx fluidcad --help.

Editor setup

FluidCAD runs on its own — no editor required. If you'd rather keep modelling in the editor you already use, it also ships official extensions for VS Code and Neovim. Every option drives the same engine and the same live 3D viewport; pick whichever fits your workflow.

The desktop app and npx fluidcad serve both open the whole product: the 3D viewport, every interactive modelling tool, and a code editor with completion driven by the engine's own type declarations — so extrude( shows its signature and edge().onPlane( completes, always matching the version you're running.

The code editor stays hidden until you ask for it. Open it from the ☰ menu or with Ctrl+B; it takes width from the left, so the model narrows rather than being covered. Files are tabs in the top bar, added with + — there's no file tree, because the editor already knows about every file in the workspace.

VS Code

  1. Install the FluidCAD extension from the VS Code Marketplace.
  2. Open your project folder in VS Code.
  3. Open the Command Palette (Ctrl+Shift+P on Windows/Linux, Cmd+Shift+P on macOS) and run Show FluidCAD Scene.

A 3D viewport opens in a side panel. It updates live as you edit any .part.js file.

Neovim

Add the plugin with lazy.nvim:

{
"Fluid-CAD/FluidCAD",
config = function()
require("fluidcad").setup()
end,
ft = { "javascript" },
}

Open a .part.js file — the server starts automatically. Run :FluidCadOpenBrowser to open the 3D viewport in your browser.

Any other editor

npx fluidcad serve works alongside any editor too: leave the browser tab on the viewport and edit .part.js in whatever you prefer — the model rebuilds on save. Opening the same file in two editors at once is the one thing to avoid.

Next

Getting started builds a butt hinge from an empty part to an animated assembly, one tool per step.