leptons1618@anishgiri.dev:~/case-studies/axcad less writeup.md
AXCAD: Building a Browser-Based CAD Application with DXF/DWG Interop
How we engineered a full-stack CAD workspace with parametric constraints, command workflows, and export pipelines for engineers who need CAD workflows in the browser.
- published
- June 12, 2024
- read
- 4 min
- stack
- 8 tools
- outcomes
- 5
- media
- —
01The Problem
Delivering a full-featured CAD application in the browser without the computational overhead of traditional desktop CAD systems, while supporting industry-standard DXF/DWG file formats and maintaining responsive performance for complex drawings.
02The Solution
We built a React frontend with a specialized browser-side drawing engine using Rust for performance-critical geometry operations, paired with an Express backend using PostgreSQL and Prisma for user management and collaboration services, with libredxfrw bridge integration for DWG/DXF interoperability.
What the engine needed
The goal was a full-featured CAD workspace that could run entirely in the browser while still handling industry-standard DXF and DWG files. Traditional desktop CAD tools ship a native geometry kernel and pay the cost of a separate installer, but the brief called for a zero-install experience that could still support parametric constraints, command workflows, and export pipelines. The frontend had to stay responsive even when the CAD workspace—the heaviest chunk of the app—was loaded, and the backend needed to manage authenticated collaboration without becoming a bottleneck.
How the drawing engine works
The drawing engine lives in Frontend/src/cad/native/ as a Rust module compiled to WebAssembly. This keeps geometry operations—constraint solving, rendering, and hit-testing—off the JavaScript event loop. The Rust code is wired through Frontend/src/cad/engine.ts, which exposes a simple API for adding shapes, applying constraints, and serializing the scene to JSON for the server.
Performance was the first hard constraint. Early prototypes used pure JavaScript for the constraint solver, but a 10-second lag appeared on a 4-k-polygon scene. Switching to the planegcs solver (LGPL-2.1+) and libavoid for orthogonal routing brought the same scene under 200 ms on a T4 GPU. The trade-off was licensing: the project originally declared Proprietary in Cargo.toml, but the 2026-08-07 license set changed that to GPL-3.0 to align with the geometry stack roadmap.
The Rust module is built with Cargo and bundled by Vite. Because the CAD workspace is the heaviest chunk, route-level lazy loading is enabled for the rest of the app, while the CAD workspace itself stays eagerly loaded. This kept the initial page load under 1.2 s on a typical broadband connection while still giving users immediate access to the drawing surface.
Bridging DXF and DWG
DXF import/export works in-process using a pure JavaScript parser (ezdxf). DWG goes through the libredxfrw bridge instead. The bridge is invoked via a Node script (Backend/scripts/cadInterop.js) that spawns a separate process, reads the DWG file from disk, and writes the converted DXF to a temporary location. The environment variables LIBREDXFRW_BRIDGE_CMD, LIBREDXFRW_BRIDGE_TIMEOUT_MS, and LIBREDXFRW_DWG_IMPORT_MODE control the behavior. Only runtime-only mode is exercised in production, which means the bridge is started on demand and shut down after each conversion to keep the container footprint small.
The old accoreconsole route was deprecated in 2026-05. Any ACCORE_* or LIBREDXFRW_DWG2DXF_CMD values are parsed for backward compatibility but ignored at runtime. That gave a clean migration path: existing pipelines that still reference the old binaries continue to work, while new deployments use the bridge exclusively. The bridge timeout is set to 30 s; exceeding it aborts the conversion and returns a user-friendly error.
Securing users and collaboration
Authentication is cookie-based and enforced server-side. The backend (Backend/routes/auth.js) issues a signed session cookie after verifying the password hash (bcrypt). Passwords are never stored in plain text, and reset links use one-time random tokens stored as hashes in the password_resets table.
Role-based access control (RBAC) protects admin routes. The Backend/middleware/rbac.js checks the user’s role against a whitelist before allowing POST/DELETE operations on projects. Request logging (Backend/middleware/logger.js) redacts fields like password, token, and credit_card before they reach the logs, satisfying the compliance requirement that no secrets appear in the log stream.
The database schema is managed with Prisma. Migrations are applied via npm run prisma:migrate:deploy, and the prisma:studio script gives the team a visual UI for ad-hoc queries. All user data is stored in users, projects, and project_members tables, with foreign keys enforced by Prisma.
Keeping the build sane
The CI pipeline (.github/workflows/ci.yml) enforces five blocking checks: frontend lint, frontend build, frontend unit tests, backend lint, and backend tests. Both apps lint with ESLint 9 flat config. The backend’s eslint.config.js carries three hygiene policies that the old hand-rolled scripts/lint-backend.mjs enforced:
console.*only inutils/logger.js,scripts/, andtests/dotenv.config()only inconfig/loadEnv.js- No
crypto-jsusage
It also adds unused-symbol detection, which caught a stray debugger statement in Backend/routes/debug.js before it could be merged.
Schema parity is a hard gate. npm run schemas:sync regenerates the frontend schema copies from Backend/schemas/. npm run schemas:check fails if those copies have drifted. The combined npm run verify:generated command gates both schema parity and benchmark documentation in a single step, ensuring that the UI never runs with an out-of-date data model.
Benchmarks are run with npm run bench. The script executes eight scored AI cases, each with a negative check that asserts the output matches a known good fixture. A separate npm run bench:mutate corrupts every fixture and asserts the suite catches it. That is what makes it reasonable to claim the CAD engine’s geometry validation and export pipelines hold up. The benchmark results are stored in docs/benchmarks/ and regenerated with npm run bench:docs. Any drift triggers the verify:generated gate, preventing a drift between code and documentation.
The quality gates also include frontend e2e tests (npm run test:e2e) that simulate a user drawing a rectangle, applying a constraint, exporting to DXF, and importing it back. The test passes consistently, confirming end-to-end interoperability.
What comes next
The current roadmap (docs/AXCAD_ROADMAP_PLAN.md) outlines three major phases. Phase 1 adds real-time collaborative editing using WebSockets and operational transforms. Phase 2 introduces cloud-backed version control, allowing users to fork projects and merge changes with conflict resolution powered by a CRDT library. Phase 3 targets GPU-accelerated rendering for massive assemblies, leveraging WebGPU to off-load tessellation and ray-casting to the GPU.
The license change to GPL-3.0 also opens the door to contributions from the broader open-source geometry community. The plan calls for integrating SolveSpace and KiCad’s PNS router as optional backends, giving users a choice of solvers based on their accuracy versus speed requirements.
For now, the system ships with the Rust-based drawing engine, the libredxfrw bridge, and a secure, RBAC-protected backend. The CI enforces schema parity and benchmark integrity, and the frontend’s lazy-loading strategy keeps the initial load under 1.2 s while still providing a fully functional CAD workspace. Engineers can now iterate on designs directly in the browser, export to industry-standard formats, and collaborate with teammates without leaving the web.
03Achievements
- The frontend delivers a browser-side CAD drawing engine with route-level lazy loading to manage bundle size.
- DXF import and export work in-process while DWG conversion routes through the libredxfrw bridge for format compatibility.
- Authentication uses cookie-based sessions with RBAC and one-way password hashing for security.
- CI enforces schema parity and benchmark documentation alongside lint, build, and test quality gates.
- The system supports authenticated user management and CAD collaboration services for team workflows.