Get a key

postMessage API

Download .md

Use the loader unless you drive a hand-built <iframe>. Its handle.on() and handle.set…() methods build every message on this page for you.

The envelope

Every message, in either direction, is a plain object with channel: "cs2embed" and a type. Both sides ignore any other message on the window, such as one from a browser extension or an ad script.

Origin pinning

Both directions are pinned to an origin:

Frame → host: events

Subscribe with handle.on(type, fn). Without the loader, listen for message events on window.

TypePayloadWhen
ready{ backend } The frame has started and accepts commands. The loader queues anything you send earlier and sends it when this fires.
loaded{ id, name, ms, notes } The item is on screen. Usually this is the opening still (first paint), about a second in, before the 3D view has drawn. It also fires alongside a fallback, because the still stays visible under the fallback's note. ms counts from the frame's start. notes lists non-fatal decode notes and is usually empty.
interactive{ id, ms, notes } The 3D view has drawn its first frame. The visitor can now orbit, and screenshot returns the live render. The loader queues commands sent before this. notes is the complete list, including notes from applying the inspect link's stickers and charm. Notes number sticker slots from 0, as st does.
error{ code, message } An access refusal, such as an unknown key, an unregistered origin or a spent quota. Fires instead of loading the frame. See Error codes.
fallback{ code, message, id } An item problem after access was granted: missing WebGPU support, a bad inspect link, an item outside the catalog, or a load failure. The frame keeps its still and shows the reason in a note over it. A frame without a still shows a 2D card instead. See fallback codes.
progress{ value, label } Load progress, value from 0 to 1.
state{ id, wear, seed, nametag } The current item state, sent after each change, including changes from your own setItem commands.
screenshot{ requestId, blob, error? } The answer to a screenshot command, matched by requestId. blob is null and error is set on failure. The loader turns this into the Promise that handle.screenshot() returns.

Host → frame: commands

Send with the matching handle.set…() / handle.resetView() / handle.screenshot() method, or post the object yourself.

TypePayloadEffect
setItem { inspect?, s?, wear?, seed?, st?, name? } Swap the item without remounting the frame. Takes the same fields and rules as the embed parameters.
setScene{ scene }Change the backdrop.
setView{ view }front | back | top | hero.
setAutorotate{ on }Turn autorotate on or off.
setQuality{ renderScale }Set the internal render resolution.
resetView{}Return the camera to its default framing.
screenshot{ requestId, width?, height?, format?, quality? } Capture the current frame as a blob, answered by a screenshot event with the same requestId.

With the loader

javascript
var viewer = CS2Embed.mount(el, { inspect: inspectLink });

viewer.on('loaded', function (e) { console.log(e.id, e.ms + ' ms'); });
viewer.on('error', function (e) { console.error('access refused:', e.code, e.message); });
viewer.on('fallback', function (e) { console.warn('showing fallback:', e.code); });

viewer.setView('back');
viewer.setAutorotate(false);

viewer.screenshot({ width: 1200, height: 630, format: 'image/webp', quality: 0.9 })
  .then(function (blob) { /* a Blob, ready to upload or download */ })
  .catch(function (err) { console.error(err); });

viewer.destroy(); // unmounts the iframe and drops all pending listeners

With a raw iframe

javascript
var EMBED_ORIGIN = 'https://inspekt.gg';
var frame = document.querySelector('iframe.cs2-viewer');

// send commands once the frame is ready
window.addEventListener('message', function (e) {
  if (e.origin !== EMBED_ORIGIN) return;
  var d = e.data;
  if (!d || d.channel !== 'cs2embed') return;

  if (d.type === 'ready') {
    frame.contentWindow.postMessage(
      { channel: 'cs2embed', type: 'setView', view: 'back' },
      EMBED_ORIGIN
    );
  }
  if (d.type === 'error') {
    console.error('embed refused:', d.code, d.message);
  }
});

Add origin=https://yoursite.example to the iframe's src so the frame addresses its messages to your page. See Embed parameters.