Embedding the 3D Viewer

Drop the Innerscene 3D viewer into any web page with a one-line iframe, then control the starting view (camera, render mode, layers, lighting) entirely through URL parameters.

Open the 3D Viewer

Quick Start

The simplest embed points at a hosted model file. Paste this into any HTML page and swap in your own file URL:

<iframe
  src="https://www.innerscene.com/tools/3d-viewer?url=https://example.com/model.glb&embedded=1"
  width="800" height="600" frameborder="0" allowfullscreen>
</iframe>

The viewer auto-detects the format from the file content, so any of the supported formats (SKP, DWG, STEP, IFC, Rhino, glTF, OBJ, STL, and more) works with the same snippet. The fastest way to generate a snippet is the Embed button in the viewer toolbar, which fills in your current view automatically.

Compressed vs Readable Links

The Embed dialog offers two ways to encode the same view. Both load identically; they differ only in length and legibility.

Compressed

Every parameter is packed into a single opaque ?d= token. Shortest, but not hand-editable. Best when you just want the link the dialog gives you.

?d=q1bKTEktSi0uVrJSykwtVrIqS8wpTVWyUg...

Readable

Every parameter is spelled out in the query string. Longer, but you can read and edit it by hand (change the view, tweak a camera value, toggle a layer).

?url=...&v=top&s=wireframe&gm=grid

Source Parameters

Pick one of these to tell the viewer which model to load. Use url for the readable form; d and share are produced by the Share and Embed dialogs.

ParamValuesDescription
urlpublic file URL (URL-encoded)

Loads a model from any publicly reachable URL. The format is auto-detected from the file content, so the extension does not matter.

url=https://example.com/model.glb
dcompressed token

A single self-contained token that packs the file URL and every view-state parameter below. Produced by the Share and Embed dialogs. Shorter and opaque (not hand-editable).

d=q1bKTEktSi0uVrJSykwt...
shareshare id

Loads a server-stored share record (created by the Share button when the file was uploaded rather than linked). Carries its own saved view state.

share=4UW0tgPl3fFf6fhBp9xTV7
url (data: URI)data:<mime>;base64,<payload>

The same url parameter also accepts a data: URI, which carries the file inline with no server involved. Decoded in the page — nothing is fetched or uploaded. Must be passed through encodeURIComponent(), otherwise the + characters in the base64 body arrive as spaces. Practical up to a few megabytes; past that a URL or postMessage is faster.

url=data%3Amodel%2Fstl%3Bbase64%2Cc29saWQ...
url (blob: URL)blob:<origin>/<uuid>

Accepted, but only resolves inside the origin that created it. A blob: URL minted by a parent page cannot be read by the viewer in a cross-origin iframe — the browser refuses the lookup and no workaround exists. Use it only on the viewer page itself; for embeds, post the bytes with postMessage instead.

url=blob%3Ahttps%3A%2F%2Fexample.com%2F9f2c…

Display Parameters

ParamValuesDescription
embedded1

Hides the toolbar and surrounding page chrome for a clean, chromeless embed. Omit it to keep the full toolbar (open, share, export, layers, lighting).

embedded=1
pm1

Enables the postMessage API, letting the host page hand the viewer a model directly instead of naming a URL. Off unless set. See the postMessage section below.

pm=1

View-State Parameters

These set the starting view. Add them to a ?url= embed (readable form), or let the Embed dialog capture them for you. Defaults are skipped, so a plain embed simply opens in the default 3D solid view.

ParamValuesDescription
v3d, top, bottom, front, back, left, right

Starting view preset. 3d is an orbit view; the rest are orthographic. Skipped when it equals the default (3d). Overridden by c when both are present.

v=top
ssolid, wireframe, x-ray, blueprint, clay

Render mode (style). Defaults to solid.

s=wireframe
cpx,py,pz,tx,ty,tz[,zoom]

Exact camera position (px,py,pz), look-at target (tx,ty,tz), and optional zoom. This is how pan, zoom, and orbit angle are captured. When present it wins over v, so the embed opens framed exactly as the sender had it.

c=10.5,20.3,15.2,0,0,0,1.2
gmnone, plane, grid

Ground visualization under the model. Defaults to none.

gm=grid
31

DWG only: prefer the 3D view of solids when the drawing contains them, instead of the flat plan view.

3=1
lpipe-separated layer names (URL-encoded)

Layers to load hidden. Names are joined with a pipe (|) then URL-encoded. Everything not listed stays visible.

l=Body%7CScrews
Lpipe-separated key:value segments (URL-encoded)

Compact lighting state: only the fields that differ from the default are listed, as short key:value segments joined by a pipe (|), then URL-encoded. Keys include E (environment preset), I (environment intensity), A (ambient), H (hemisphere), and K/B/R (key/back/rim lights as on,intensity,azimuth,elevation). Produced when you adjust the Lighting panel; easiest to copy from the Embed dialog rather than write by hand.

L=E%3Asunset%7CI%3A1.2 (decodes to E:sunset|I:1.2)

Loading a Model Without a URL

?url= needs an address the viewer can fetch. Three common cases have no such address: the host page already holds the bytes in memory, the file sits behind authentication the iframe cannot replay, or the file is on the visitor’s own machine. There are two ways to load a model in those situations.

1. Inline with a data: URI

Small models can travel inside the URL itself. The viewer decodes them in the page — nothing is fetched and nothing is uploaded.

const dataUri = 'data:model/stl;base64,' + base64Bytes;
iframe.src = 'https://www.innerscene.com/tools/3d-viewer'
  + '?url=' + encodeURIComponent(dataUri)
  + '&embedded=1';

encodeURIComponent is required. Without it the + characters in a base64 body arrive as spaces and the payload will not decode. Browsers cap URL length, so treat a few megabytes as the practical ceiling — past that, use postMessage.

2. postMessage (any size)

Add ?pm=1 and the viewer will accept the file from the page that embeds it. Bytes are transferred in memory, so there is no size ceiling beyond available RAM and no network round trip. Wait for the viewer-ready handshake before posting — the frame ignores anything that arrives before its listener mounts.

const iframe = document.querySelector('iframe');

window.addEventListener('message', async (e) => {
  if (e.source !== iframe.contentWindow) return;
  if (e.data?.source !== 'innerscene-3d-viewer') return;

  if (e.data.type === 'viewer-ready') {
    const buffer = await file.arrayBuffer();
    iframe.contentWindow.postMessage(
      { type: 'load-model', name: 'part.stl', data: buffer },
      'https://www.innerscene.com',
      [buffer],            // optional: transfer instead of copy
    );
  }

  if (e.data.type === 'load-result') {
    console.log(e.data.ok ? 'loaded ' + e.data.name : 'failed: ' + e.data.error);
  }
});
MessageDirectionFields
viewer-readyviewer → hostapi — protocol version (currently 1)
load-modelhost → viewerdata — ArrayBuffer, Uint8Array, Blob, File, or a data: URI string. name — optional filename. mimeType — optional hint.
load-resultviewer → hostok, then either name and bytes, or error.

Format detection is content-based, so name is only a hint — a model loads correctly even with the wrong extension or none at all. Only the immediate parent frame may post; messages from other frames are ignored.

Loading from the visitor’s own machine. Pointing ?url= at a local web server such as http://127.0.0.1:8000/model.stl looks like the obvious solution and generally will not work: a page served over https is gated by the browser’s local-network permission when it reaches into 127.0.0.1, and a blocked request fails with a bare “Failed to fetch” before anything reaches the server. CORS headers cannot fix it, because no request is sent. Read the bytes in the host page and post them in instead — or just let the user drag the file onto the viewer.

A Worked Example

This embed opens a model in a top-down wireframe view, over a grid ground, with two layers hidden and the toolbar off:

<iframe
  src="https://www.innerscene.com/tools/3d-viewer?url=https://example.com/part.step&v=top&s=wireframe&gm=grid&l=Fasteners%7CWelds&embedded=1"
  width="800" height="600" frameborder="0" allowfullscreen>
</iframe>

Reading the query string: v=top (top view), s=wireframe (wireframe), gm=grid (grid ground), l=Fasteners|Welds (those two layers hidden), embedded=1 (no toolbar).

Responsive Embeds

An iframe has no intrinsic aspect ratio, so to make the viewer fill its container set width="100%" and give the iframe a fixed or aspect-ratio height with CSS:

<div style="position: relative; width: 100%; aspect-ratio: 4 / 3;">
  <iframe
    src="https://www.innerscene.com/tools/3d-viewer?url=https://example.com/model.glb&embedded=1"
    style="position: absolute; inset: 0; width: 100%; height: 100%; border: 0;"
    allowfullscreen>
  </iframe>
</div>

The Embed dialog’s “Responsive width” checkbox emits the width="100%" form for you.

Notes & Limits

  • The url you embed must be publicly reachable (no login wall), and the host should allow cross-origin fetches (CORS) so the browser can download the file.
  • Files load and render in the visitor’s browser. Nothing is uploaded to Innerscene for a ?url= embed.
  • Use a share link when the file is not hosted on a public URL: the Share button uploads it and stores the view state for 90 days.
  • Remember to URL-encode values that contain spaces or special characters, especially layer names in l=.

Also see: 3D Viewer · DWG Viewer · SKP Viewer · STEP Viewer

Need a wellness lighting solution?
Contact our experts today!