DEV Community

Timur Arbaev
Timur Arbaev

Posted on

Rendering AutoCAD DXF files in the browser — how and why I built dxf-render

A client sends you an AutoCAD drawing. "Just display it in the browser," they say. How hard can it be?

I found out the hard way. After trying every open-source DXF library for JavaScript, I ended up building my own. Here's the story of dxf-render and what I learned about the surprisingly weird world of the DXF format.

Live Demo — drag and drop any DXF file to see it rendered.

"Where are the dimensions?"

My first attempt was simple: grab dxf-parser + three-dxf, wire them together, ship it. The basic shapes looked fine — lines, circles, arcs. Then I opened a real engineering drawing.

Half the dimensions were gone. The dashed center lines showed as solid. Hatched areas were empty outlines. And a floor plan drawn in a rotated coordinate system looked like abstract art.

I switched to dxf-viewer — the most capable option out there at 37K monthly downloads. Better, but still: only linear dimensions (no radial, angular, ordinate), no linetype patterns, no LEADER arrows. Every drawing had something missing.

The core issue? DXF is a 40-year-old format with a lot of features, and building a renderer that handles real-world files — not just textbook examples — takes more than a few entity handlers.

The DXF rabbit hole

If you've never worked with DXF, here's the fun part: the file format is a flat stream of numbered code/value pairs. No nesting, no XML, no JSON. Just thousands of lines like:

0
LINE
8
Layer1
10
0.0
20
0.0
11
100.0
21
50.0
Enter fullscreen mode Exit fullscreen mode

Code 0 = entity type. Code 8 = layer name. Codes 10/20 = start point X/Y. Codes 11/21 = end point. Simple enough for LINE. But then you hit DIMENSION entities with 30+ codes, HATCH with recursive boundary paths, MTEXT with its own inline formatting language (\P for newline, \S for stacking fractions, {\fArial;styled text})...

And then there's OCS — the Object Coordinate System. Some CAD tools save entities in local coordinate systems defined by an extrusion direction vector. You need to implement the "Arbitrary Axis Algorithm" from the DXF spec to transform them back to world coordinates. Skip this and your 3D-originated drawings will be rotated, flipped, or shifted.

I wrote 25 entity handlers. Each one wrapped in try-catch, because real-world DXF files regularly break the spec. An entity parser that crashes on malformed data is useless — you need to skip the broken entity, log a warning, and keep going.

What I ended up with

After months of work, dxf-render handles 21 entity types — including the ones that other libraries skip:

  • All 7 dimension types (linear, rotated, aligned, ordinate, radial, diametric, angular) — not just linear
  • Linetype patterns resolved from the LTYPE table and applied as geometric dash patterns
  • 25 built-in hatch patterns (ANSI31, HONEY, BRICK...) with proper clipping to boundary paths
  • LEADER and MULTILEADER — those annotation arrows that show up in every mechanical drawing
  • Full OCS via the Arbitrary Axis Algorithm
  • Vector text rendered with opentype.js — no bitmap textures, sharp at any zoom

The most satisfying moment was opening a complex architectural plan and seeing it come out right.

Five lines to render a DXF

import { parseDxf, createThreeObjectsFromDXF, loadDefaultFont } from "dxf-render";

const dxf = parseDxf(dxfText);
await loadDefaultFont();
const { group } = await createThreeObjectsFromDXF(dxf);
scene.add(group); // add to any Three.js scene
Enter fullscreen mode Exit fullscreen mode

That's the core API. Parse, create Three.js objects, add to scene. It works with React, Vue, Svelte, Angular, vanilla JS — anything that can host a Three.js canvas.

Full working examples: Vanilla TS | React | Vue — all on StackBlitz, runnable in the browser.

Parser without Three.js

There's a separate entry point for when you just need the data:

import { parseDxf } from "dxf-render/parser";

const dxf = parseDxf(dxfText);
// → layers, entities, blocks, styles, header variables — all typed
Enter fullscreen mode Exit fullscreen mode

Zero dependencies. Works in Node.js, Deno, Bun. Useful for data extraction, server-side processing, or building your own renderer.

Performance tricks I learned the hard way

The draw call problem

My first naive renderer created one THREE.Line per DXF entity. A modest floor plan with 5,000 lines = 5,000 draw calls = slideshow.

The fix: a GeometryCollector that accumulates all line segments, points, and mesh triangles by layer::color key, then flushes them into merged BufferGeometry objects. 5,000 draw calls became ~50. The GPU doesn't care about individual lines — it cares about batches.

Keeping the UI alive

Large DXF files can have 50,000+ entities. Processing them synchronously freezes the browser. createThreeObjectsFromDXF() yields back to the event loop every ~16ms and supports AbortSignal for cancellation:

const { group } = await createThreeObjectsFromDXF(dxf, {
  signal: abortController.signal,
  onProgress: (p) => updateProgressBar(p),
});
Enter fullscreen mode Exit fullscreen mode

Instant dark mode without re-rendering

AutoCAD's color index 7 means "black on light background, white on dark." Instead of re-building the entire scene when the user toggles dark mode, I use sentinel color values tracked in a MaterialCacheStore. Calling materials.switchTheme(true) updates the affected materials in-place — the theme switch is instant.

How it compares

I'll let the table speak:

Feature dxf-render dxf-viewer dxf-parser three-dxf
Rendering ✅ Three.js ✅ Three.js ❌ parse only ✅ Three.js
Entity types 21 ~15 ~15 parsed ~8
Linetype patterns ❌ all solid
Dimension types all 7 linear only
LEADER / MULTILEADER
Hatch patterns 25 built-in
OCS support full Z-flip only
TypeScript native .d.ts native
Tests 853 0 0
Parser-only mode ✅ zero deps
Last updated 2026 2024 2023 2019

No shade to these projects — they solved real problems for a lot of people. dxf-render just goes deeper on entity coverage and rendering accuracy.

What I'd do differently

Start with tests earlier. I wrote most of the 853 tests after the fact. Having them from the start would have caught color resolution edge cases (ByLayer inheriting from ByBlock inside nested INSERTs) much sooner.

Don't underestimate text. MTEXT parsing alone is ~400 lines. The inline formatting language is underdocumented and inconsistently implemented across CAD tools. I ended up building a custom glyph cache with hand-crafted vector paths for special characters that opentype.js doesn't handle (diameter symbols, plus-minus signs, degree marks).

DXF files in the wild are broken. Entities with missing required codes, color indices out of range, splines with zero control points. The parser needs to be paranoid and forgiving at the same time.

What's next

  • Polyline width — LWPOLYLINE with variable start/end width (common in P&ID diagrams)
  • Entity interaction — click and hover events via raycaster
  • More hatch patterns — importing from the QCAD pattern library

Try it

npm install dxf-render three
Enter fullscreen mode Exit fullscreen mode

For Vue 3 users: dxf-vuer wraps dxf-render into a drop-in viewer component.


Working with CAD files in the browser? I'd love to hear about your use case — drop a comment or open an issue.

Top comments (0)