Tutorial May 18, 2026

Embed an STL Viewer in Any HTML Page — 2 Lines of Code

One script tag, one custom element. That's all you need to display interactive STL files on any web page. Here's the complete guide.

The Simplest Possible STL Embed

Let's start with the finished product. If you want to display an STL file on a web page with full rotate, zoom, and pan controls, here is the complete code you need to add to your HTML:

<script type="module" src="https://geometryviewer.com/embed.js"></script>
<geometry-viewer src="https://example.com/model.stl" camera-controls></geometry-viewer>

That's it. Two lines. The first line loads the GeometryViewer web component library. The second line creates an interactive 3D viewer that loads and renders your STL file. The viewer handles parsing the STL format (both ASCII and binary), creating the 3D mesh, setting up lighting, adding camera controls, and managing the WebGL rendering loop — all automatically.

You can paste these two lines into any HTML page on any web server. They work on static sites, PHP pages, Node.js templates, Hugo sites, Jekyll blogs, or a raw HTML file you're serving from your desktop. No build step, no package manager, no framework required.

A Complete Working Example

Here's a full HTML page you can save as a file and open in your browser to see it working:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>My STL Viewer</title>
  <script type="module" src="https://geometryviewer.com/embed.js"></script>
  <style>
    body { margin: 0; display: flex; justify-content: center; padding: 40px; }
    geometry-viewer { width: 100%; max-width: 800px; height: 500px; border-radius: 12px; }
  </style>
</head>
<body>
  <geometry-viewer
    src="https://geometryviewer.com/v/abc123"
    camera-controls
    auto-rotate
  ></geometry-viewer>
</body>
</html>

Save this as viewer.html, replace the src with a URL to your STL file, and open it in Chrome, Firefox, Safari, or Edge. You'll see your 3D model rendered with lighting, shadows, and full mouse/touch interaction.

Styling the Viewer with CSS

The <geometry-viewer> element is a standard HTML element as far as CSS is concerned. You can style it with any CSS properties that apply to block-level elements. This gives you complete control over how the viewer fits into your page layout.

Fixed dimensions

geometry-viewer {
  width: 600px;
  height: 400px;
}

This gives the viewer a fixed size regardless of the viewport. Good for documentation pages or tool interfaces where the layout is controlled.

Fluid width with fixed height

geometry-viewer {
  width: 100%;
  height: 500px;
}

The viewer stretches to fill its container's width but maintains a fixed height. This is the most common approach and works well for most layouts. Add a max-width to prevent the viewer from becoming absurdly wide on large screens.

Aspect ratio (modern CSS)

geometry-viewer {
  width: 100%;
  aspect-ratio: 16 / 9;
}

The aspect-ratio CSS property (supported in all modern browsers) lets the viewer maintain a consistent proportion at any width. Use 16 / 9 for widescreen, 4 / 3 for traditional, or 1 / 1 for square. This is the cleanest approach for responsive layouts.

Aspect ratio (legacy padding trick)

.viewer-container {
  position: relative;
  width: 100%;
  padding-bottom: 56.25%; /* 16:9 */
  height: 0;
}
.viewer-container geometry-viewer {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
}

If you need to support older browsers that don't understand aspect-ratio, the padding-bottom technique achieves the same result. The percentage is calculated as (height / width) * 100. For 16:9, that's 9/16 = 56.25%.

Visual styling

geometry-viewer {
  border-radius: 16px;
  border: 1px solid #e0e0e0;
  box-shadow: 0 4px 24px rgba(0, 0, 0, 0.08);
  overflow: hidden;
}

You can add borders, shadows, rounded corners, and any other visual effects. The overflow: hidden ensures the WebGL canvas respects the border radius — without it, the canvas corners may poke out beyond rounded corners in some browsers.

Loading from Different Sources

The src attribute accepts any URL that points to an STL file. Where that URL comes from determines how the viewer fetches the data.

From a URL (same server)

<geometry-viewer src="/models/part.stl" camera-controls></geometry-viewer>

If the STL file is hosted on the same server as your HTML page, you can use a relative or absolute path. This is the simplest case — no CORS issues, no external dependencies. The browser fetches the file just like any other resource on your site.

From a URL (external server)

<geometry-viewer src="https://cdn.example.com/models/part.stl" camera-controls></geometry-viewer>

If the STL file is on a different domain, the browser needs CORS headers to fetch it. The server hosting the STL file must include Access-Control-Allow-Origin in its response headers. Most CDNs (AWS S3, Cloudflare R2, Google Cloud Storage, DigitalOcean Spaces) support CORS configuration. If you're using GeometryViewer's hosted URLs, CORS is already configured — no action needed on your part.

From a GeometryViewer viewer link

<geometry-viewer src="https://geometryviewer.com/v/abc123" camera-controls></geometry-viewer>

When you upload a model to GeometryViewer, you get a viewer URL. This URL can be used directly as the src attribute. The component detects GeometryViewer URLs and loads the model data efficiently from our CDN.

From a local file (user upload)

You can also load STL files from the user's local filesystem using JavaScript. This is useful for building file preview tools or upload interfaces:

<input type="file" accept=".stl" id="fileInput">
<geometry-viewer id="viewer" camera-controls style="width:100%;height:500px"></geometry-viewer>

<script>
document.getElementById('fileInput').addEventListener('change', (e) => {
  const file = e.target.files[0];
  if (!file) return;
  const url = URL.createObjectURL(file);
  document.getElementById('viewer').setAttribute('src', url);
});
</script>

This creates a local blob URL from the file and passes it to the viewer. The file never leaves the user's browser — it's processed entirely client-side. This makes it suitable for previewing confidential or proprietary STL files.

CORS Considerations

Cross-Origin Resource Sharing (CORS) is the most common issue developers hit when loading STL files from external servers. Here's what you need to know.

When your HTML page is on https://yoursite.com and the STL file is on https://cdn.example.com, the browser sends a preflight OPTIONS request to cdn.example.com before fetching the file. If the CDN doesn't respond with the right headers, the browser blocks the request and the viewer shows an error.

The required header is straightforward:

Access-Control-Allow-Origin: *

Or, for more restrictive access:

Access-Control-Allow-Origin: https://yoursite.com

If you control the server hosting the STL files, adding this header is usually a one-line configuration change. For nginx: add_header Access-Control-Allow-Origin *; in the relevant location block. For Apache: Header set Access-Control-Allow-Origin "*" in .htaccess. For S3: configure CORS in the bucket settings.

If you don't control the server, you have two options. First, you can proxy the STL file through your own server (fetch it server-side and serve it from your domain). Second, you can upload the file to GeometryViewer, which handles CORS automatically.

Responsive Container Patterns

Different page layouts call for different container strategies. Here are the most common patterns for embedding STL viewers in real-world pages.

Full-width hero

<div style="width: 100vw; margin-left: calc(-50vw + 50%); height: 60vh;">
  <geometry-viewer src="/model.stl" camera-controls style="width:100%;height:100%"></geometry-viewer>
</div>

This makes the viewer break out of a centered content column and span the full viewport width. The height: 60vh makes it 60% of the viewport height. Great for product landing pages or portfolio showcases.

Side-by-side with text

<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 24px; align-items: start;">
  <geometry-viewer src="/model.stl" camera-controls style="width:100%;aspect-ratio:1"></geometry-viewer>
  <div>
    <h2>Product Details</h2>
    <p>Description text here...</p>
  </div>
</div>

A two-column grid with the viewer on one side and text on the other. The square aspect ratio keeps the viewer compact. On mobile, you'd typically switch to grid-template-columns: 1fr with a media query so the viewer stacks above the text.

Thumbnail that expands

For documentation or technical pages where you want to show multiple models without overwhelming the page, start with a small viewer that expands on click. You can achieve this with a few lines of CSS using the :fullscreen pseudo-class, or by toggling a CSS class with a small JavaScript snippet.

Browser Support

The <geometry-viewer> web component works in all browsers that support WebGL and Custom Elements v1. In practice, this means Chrome, Firefox, Safari, and Edge from the last several years. Internet Explorer is not supported, but at this point, IE usage is negligible. On mobile, the component works in Chrome for Android, Safari on iOS (14+), and Samsung Internet.

For visitors on browsers that don't support custom elements (extremely rare today), the <geometry-viewer> tag renders as an unknown inline element with no visual output. You can add fallback content between the opening and closing tags:

<geometry-viewer src="/model.stl" camera-controls>
  <img src="/model-preview.jpg" alt="3D model preview">
</geometry-viewer>

The fallback image is hidden when the web component initializes, and shown only if the component fails to load.

Two lines. Any page. Any server.

Add the script tag and the element. Your STL file is now an interactive 3D experience.

View Embed Documentation