`
`
If you've ever tried to share a DWG file with someone who doesn't have AutoCAD, you know the pain: they can't open it, exporting to PDF kills the layers, and running a full desktop license just to view drawings is overkill.
What if non-technical users could just open a URL?
This post walks through how to render AutoCAD DWG files directly in a web browser — no plugins, no desktop software, no PDF export — using WebGL and a JavaScript SDK called VJMAP.
Here's what the end result looks like: a fully interactive CAD drawing in the browser, with infinite zoom, pan, layer query, and entity hover highlighting.
Why Is This Harder Than It Sounds?
DWG is Autodesk's proprietary binary format. It contains geometric entities (lines, arcs, polylines, ellipses, splines, blocks, text), layer metadata, coordinate systems, and rendering styles — none of which browsers can parse natively.
The common workarounds all have real tradeoffs:
Export to SVG / PDF — Simple, but lossy. Loses layers, metadata, and is completely static. No interaction possible.
Convert DWG → DXF → GeoJSON → Leaflet/OpenLayers — Doable with GDAL + GeoServer/MapServer, but CAD and GIS have fundamentally different data models. CAD entities like ellipses, splines, and blocks don't map cleanly to GIS point/line/polygon types. You end up with a drawing that looks slightly wrong everywhere: different line styles, missing fonts, distorted geometry.
ActiveX controls — Legacy Windows-only approach, requires browser plugins, and Chrome dropped ActiveX support years ago.
Autodesk Forge / APS — Powerful, but your DWG data must live on Autodesk's cloud. Not an option if you have data sovereignty requirements.
What we actually need is something that:
- Parses DWG server-side with full fidelity — no data model conversion
- Serves the result as tile data so the browser only loads what's visible
- Renders with WebGL for smooth infinite zoom
- Exposes a JavaScript API for interaction, querying, and overlays
That's the approach VJMAP takes.
How It Works: The WebGIS Approach
The key insight is to treat CAD drawings the way web maps treat geographic data: tile pyramids.
Traditional approaches download the entire drawing to the browser and render it all at once. For large DWG files (tens of MB or more), this kills both load time and rendering performance.
The WebGIS approach uses space to buy time: the server pre-processes the DWG and generates either raster tiles (PNG images) or vector tiles (Mapbox Vector Tile format). The browser only requests the tiles for the current viewport and zoom level — never the full dataset.
┌─────────────┐ parse & ┌─────────────────────┐ tile ┌──────────────┐
│ DWG File │ ──────────────▶ │ VJMAP Map Service │ ─────────▶ │ Browser │
│ (on server)│ render tiles │ (tile server) │ on demand │ (WebGL) │
└─────────────┘ └─────────────────────┘ └──────────────┘
│
vjmap JavaScript SDK
layers / query / draw
Raster tiles — PNG images generated server-side, very stable, works on any device. Zero client-side rendering cost.
Vector tiles — Geometric data served in PBF format (Mapbox Vector Tile standard), rendered by WebGL in the browser. Supports dynamic style changes, higher visual quality, and client-side querying of entity properties.
Both modes are supported. Vector tiles give better results when you need to query or highlight individual entities; raster tiles are simpler and more broadly compatible.
Getting Started
Option 1: Plain HTML (no build tools)
The quickest way to try VJMAP is a single HTML file. Here's a complete working example:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>VJMAP DWG Viewer</title>
<link rel="stylesheet" href="https://vjmap.com/demo/js/vjmap/vjmap.min.css">
<script src="https://vjmap.com/demo/js/vjmap/vjmap.min.js"></script>
</head>
<body style="margin:0; overflow:hidden; background:#022B4F;">
<div id="map" style="position:absolute; left:0; right:0; top:0; bottom:0;"></div>
</body>
<script>
(async () => {
const env = {
serviceUrl: "https://vjmap.com/server/api/v1",
accessToken: "your-access-token",
exampleMapId: "sys_zp" // use a map ID you've uploaded
};
// Create service object
let svc = new vjmap.Service(env.serviceUrl, env.accessToken);
// Open a DWG map (first time: pass fileid; after that the server caches it)
let res = await svc.openMap({
mapid: env.exampleMapId,
mapopenway: vjmap.MapOpenWay.GeomRender, // geometric rendering mode
style: vjmap.openMapDarkStyle() // dark background style
});
if (res.error) {
console.error(res.error);
return;
}
// Build the coordinate system from the drawing's native bounds
let mapExtent = vjmap.GeoBounds.fromString(res.bounds);
let prj = new vjmap.GeoProjection(mapExtent);
// Create the map — familiar API if you've used Mapbox GL JS
let map = new vjmap.Map({
container: 'map',
style: svc.vectorStyle(), // vector tile rendering
center: prj.toLngLat(mapExtent.center()),
zoom: 1,
pitch: 0,
renderWorldCopies: false
});
// Bind the service and projection to the map
map.attach(svc, prj);
await map.onLoad();
// Add a mouse position control (shows CAD coordinates)
let mousePositionControl = new vjmap.MousePositionControl({ showLatLng: true });
map.addControl(mousePositionControl, "bottom-left");
})();
</script>
</html>
That's it — open the HTML file in a browser and your DWG is rendered with WebGL.
💡 Note: On the first
openMapcall for a newmapid, the server parses the DWG and generates the tile cache. This may take a few seconds depending on file size. All subsequent opens are instant.
Option 2: React
Install the SDK:
npm install vjmap
Here's a complete React component using hooks:
// Map.js
import React, { useRef, useEffect } from 'react';
import ReactDOM from 'react-dom';
import vjmap from '!vjmap'; // webpack raw import
import 'vjmap/dist/vjmap.min.css';
import './Map.css';
const Tooltip = ({ prop }) => (
<div id={`tooltip-${prop.id}`}>
<strong>Entity ID:</strong> {prop.id}<br />
<strong>Layer:</strong> {prop.layerName}<br />
<strong>Type:</strong> {prop.typeName}<br />
<strong>Color:</strong> <span style={{ color: prop.color }}>{prop.color}</span>
</div>
);
const Map = () => {
const mapContainerRef = useRef(null);
const tooltipRef = useRef(new vjmap.Popup({ offset: 15 }));
useEffect(() => {
let map;
(async () => {
const env = {
serviceUrl: "https://vjmap.com/server/api/v1",
accessToken: "your-access-token",
exampleMapId: "sys_zp"
};
let svc = new vjmap.Service(env.serviceUrl, env.accessToken);
let res = await svc.openMap({
mapid: env.exampleMapId,
mapopenway: vjmap.MapOpenWay.GeomRender,
style: vjmap.openMapDarkStyle()
});
if (res.error) {
console.error(res.error);
return;
}
let mapExtent = vjmap.GeoBounds.fromString(res.bounds);
let prj = new vjmap.GeoProjection(mapExtent);
map = new vjmap.Map({
container: mapContainerRef.current,
style: svc.vectorStyle(),
center: prj.toLngLat(mapExtent.center()),
zoom: 1,
pitch: 0,
renderWorldCopies: false
});
map.attach(svc, prj);
await map.onLoad();
// Get layer info and entity type names from the server
const layers = svc.getMapLayers();
const { entTypeIdMap } = await svc.getConstData();
// Mouse position control
map.addControl(new vjmap.MousePositionControl(), "bottom-left");
// Hover highlighting with tooltip in vector tile mode
map.enableVectorLayerHoverHighlight((eventName, feature, layer, e) => {
if (eventName === "mouseleave") {
tooltipRef.current.remove();
return;
}
const prop = feature.properties;
if (!prop) return;
prop.layerName = layers[prop.layer].name;
prop.typeName = entTypeIdMap[prop.type];
prop.id = feature.id;
const tooltipNode = document.createElement('div');
ReactDOM.render(<Tooltip prop={prop} />, tooltipNode);
tooltipRef.current
.setLngLat(e.lngLat)
.setDOMContent(tooltipNode)
.addTo(map);
});
})();
return () => map?.remove();
}, []);
return <div className="map-container" ref={mapContainerRef} />;
};
export default Map;
/* Map.css */
.map-container {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
background: #022B4F;
}
// App.js
import React from 'react';
import Map from './Map';
function App() {
return <div><Map /></div>;
}
export default App;
Option 3: Vue 3
<!-- Map.vue -->
<script setup lang="ts">
import 'vjmap/dist/vjmap.min.css'
import { Map } from "vjmap"
import { onMounted, provide, ref } from 'vue';
import { enableHoverHighlight, initMap } from './lib/map';
let map: Map;
const isLoaded = ref(false);
onMounted(async () => {
map = await initMap();
isLoaded.value = true;
await enableHoverHighlight(map);
});
provide('map', () => map); // inject map into child components
</script>
<template>
<div>
<div id="map-container" />
<UILayer v-if="isLoaded" />
</div>
</template>
<style scoped>
#map-container {
position: absolute;
top: 0; bottom: 0;
left: 0; right: 0;
background: #022B4F;
}
</style>
<!-- App.vue -->
<script setup lang="ts">
import Map from './Map.vue'
</script>
<template>
<Map />
</template>
Working with Layers and Entities
Query layers from the DWG
// Returns all layers defined in the DWG file
const layers = svc.getMapLayers();
console.log(layers.map(l => l.name));
// e.g. ['0', 'WALLS', 'DOORS', 'ELECTRICAL', 'DIMENSIONS', 'TEXT']
Highlight on hover — vector tile mode
// In vector tile mode, use enableVectorLayerHoverHighlight
map.enableVectorLayerHoverHighlight((eventName, feature, layer, e) => {
const prop = feature.properties;
console.log(`Layer: ${layers[prop.layer].name}, Type: ${entTypeIdMap[prop.type]}`);
});
Highlight on click — raster tile mode
// In raster tile mode, use enableLayerClickHighlight
map.enableLayerClickHighlight(svc, e => {
console.log(`type: ${e.name}, id: ${e.objectid}, layer: ${e.layerindex}`);
});
Raster vs Vector Tiles: Which Should You Use?
| Raster Tiles | Vector Tiles | |
|---|---|---|
| How it works | Server renders PNG images | Server sends PBF geometry; browser renders with WebGL |
| Visual quality | Good | Excellent (resolution-independent) |
| Tile size | Larger | Smaller |
| Dynamic styling | ❌ | ✅ |
| Client entity querying | ❌ | ✅ |
| Browser requirements | Low | HTML5 / WebGL |
| Best for | Simple viewing | Interactive apps |
To switch between them, just change the style:
// Vector tiles (recommended for interactive apps)
style: svc.vectorStyle()
// Raster tiles (simpler, broader device compatibility)
style: svc.rasterStyle()
Adding Overlays
VJMAP supports adding any overlay on top of the CAD drawing — markers, polygons, popups, extruded 3D shapes.
Add a marker with a popup
// Convert a CAD coordinate point to map lat/lng
const cadPoint = new vjmap.Point(587500025.09578, 3103899983.60220);
const pt = map.toLngLat(cadPoint);
const popup = new vjmap.Popup({ closeOnClick: false, offset: 5 })
.setHTML('<p>This is a location in the drawing</p>');
const marker = new vjmap.Marker();
marker.setLngLat(pt).setPopup(popup).addTo(map);
marker.togglePopup();
Add 3D extruded shapes
const mapBounds = map.getGeoBounds(0.4);
const geoDatas = [];
for (let i = 0; i < 100; i++) {
const len = vjmap.randInt(mapBounds.width() / 200, mapBounds.width() / 100);
const p1 = mapBounds.randomPoint();
const pts = [
p1,
vjmap.geoPoint([p1.x, p1.y + len]),
vjmap.geoPoint([p1.x + len, p1.y + len]),
vjmap.geoPoint([p1.x + len, p1.y])
];
geoDatas.push({
points: map.toLngLat(pts),
properties: {
name: `square${i + 1}`,
color: vjmap.randomColor(),
height: prj.toMeter(vjmap.randInt(len * 10, len * 20)),
baseHeight: 0
}
});
}
const fillExtrusions = new vjmap.FillExtrusion({
data: geoDatas,
fillExtrusionColor: ['case',
['to-boolean', ['feature-state', 'hover']], 'red',
['get', 'color']
],
fillExtrusionOpacity: 0.8,
fillExtrusionHeight: ['get', 'height'],
fillExtrusionBase: ['get', 'baseHeight'],
isHoverPointer: true,
isHoverFeatureState: true
});
fillExtrusions.addTo(map);
fillExtrusions.clickLayer(e =>
console.log(`Clicked: ${e.features[0].properties.name}`)
);
fillExtrusions.hoverPopup(
f => `<h3>${f.properties.name}</h3>Color: ${f.properties.color}`,
{ anchor: 'bottom' }
);
Drawing Tools
VJMAP includes built-in drawing tools you can add to the map:
const draw = new vjmap.Draw.Tool();
map.addControl(draw, 'top-right');
// Pre-populate with a polygon
const mapBounds = map.getGeoBounds(0.4);
draw.set(vjmap.createPolygonGeoJson(map.toLngLat(mapBounds.randomPoints(3, 3))));
// Start drawing mode
draw.changeMode("draw_polygon");
Deployment
VJMAP supports private deployment — your DWG files never have to leave your own infrastructure.
Trial / development: Connect directly to the VJMAP cloud service at https://vjmap.com/server/api/v1. The free trial has watermarks, upload size limits, and uploaded data may be periodically cleared. Feature set is identical to the licensed version.
Production: Purchase a license and deploy on your own server. VJMAP provides installation packages for:
- Windows — installer-based setup
- Linux — binary package (supports AMD64 and ARM64, compatible with mainstream domestic CPUs and OS)
- Docker — restore from a licensed image provided after purchase
🔒 For Docker deployment and private server setup, contact vjmapcom@163.com after purchasing a license.
Private deployment gives you full control over your data, no watermarks, no file size limits, and the ability to serve large enterprise drawings.
2D and 3D — Same Codebase
One of VJMAP's more useful features is that 2D and 3D views are handled by the same SDK. The pitch parameter on the map object controls the view angle:
// 2D view
let map = new vjmap.Map({ ..., pitch: 0 });
// 3D tilted view
let map = new vjmap.Map({ ..., pitch: 60 });
VJMAP3D extends this with a full Three.js-based 3D engine for more complex spatial visualization, using the same map IDs and service API.
Summary
Here's what we covered:
- Why native DWG rendering in browsers is hard, and why the "convert to GeoJSON" approach has fundamental limitations
- How the WebGIS tile pyramid approach solves the performance and fidelity problems
- The difference between raster and vector tile modes and when to use each
- Working code examples for plain HTML, React, and Vue 3
- How to query layers, highlight entities, and add overlays
- Drawing tool integration
- Deployment options from trial to production private deployment
The result: a fully interactive CAD drawing viewer that runs in any modern browser, with no AutoCAD license, no plugins, and no export step required.
Resources:
- 🔗 VJMAP English Documentation
- 🎮 Live Demo Gallery
- ☁️ Cloud Platform — upload your own DWG
- 🖥️ WebCAD Online Editor
- 📦 npm: vjmap
- 📧 Contact: vjmapcom@163.com
Have you worked with CAD data on the web before? Curious what approach you've taken — drop a comment below.

Top comments (0)