Frameworks
The same loader (cs2embed.js), wired up four ways. Pick the one that
matches your stack and paste it in. Every one of these is a complete, working integration, not a
fragment.
index.html
<script src="https://inspekt.gg/cs2embed.js"></script>
<div id="viewer" style="width:640px;height:400px"></div>
<script>
var viewer = CS2Embed.mount(document.getElementById('viewer'), {
inspect: 'steam://rungame/730/76561202255233023/+csgo_econ_action_preview 0018...',
scene: 'plate_train_video',
autorotate: true,
});
viewer.on('loaded', function (e) {
console.log('rendered', e.id, 'in', e.ms, 'ms');
});
viewer.on('error', function (e) {
console.error('embed refused:', e.code, e.message);
});
</script>
SkinViewer.jsx
import { useEffect, useRef } from 'react';
// There's no npm package for the loader. It's a single dependency-free
// script, loaded once and cached in the module scope so multiple mounted
// viewers on the same page share it.
let loaderPromise;
function loadCs2Embed() {
if (window.CS2Embed) return Promise.resolve(window.CS2Embed);
if (!loaderPromise) {
loaderPromise = new Promise((resolve, reject) => {
const script = document.createElement('script');
script.src = 'https://inspekt.gg/cs2embed.js';
script.onload = () => resolve(window.CS2Embed);
script.onerror = reject;
document.head.appendChild(script);
});
}
return loaderPromise;
}
export function SkinViewer({ inspect, scene = 'plate_train_video', autorotate = true }) {
const hostRef = useRef(null);
const viewerRef = useRef(null);
useEffect(() => {
let cancelled = false;
loadCs2Embed().then((CS2Embed) => {
if (cancelled || !hostRef.current) return;
viewerRef.current = CS2Embed.mount(hostRef.current, { inspect, scene, autorotate });
viewerRef.current.on('error', (e) => console.error('embed refused:', e.code, e.message));
});
return () => {
cancelled = true;
viewerRef.current?.destroy();
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [inspect, scene, autorotate]);
return <div ref={hostRef} style={{ width: '100%', height: 400 }} />;
}
// usage: <SkinViewer inspect={item.inspectLink} />
SkinViewer.vue
<template>
<div ref="host" style="width: 100%; height: 400px"></div>
</template>
<script setup>
import { onBeforeUnmount, onMounted, ref, watch } from 'vue';
const props = defineProps({
inspect: { type: String, required: true },
scene: { type: String, default: 'plate_train_video' },
});
const host = ref(null);
let viewer = null;
let loaderPromise;
function loadCs2Embed() {
if (window.CS2Embed) return Promise.resolve(window.CS2Embed);
if (!loaderPromise) {
loaderPromise = new Promise((resolve, reject) => {
const script = document.createElement('script');
script.src = 'https://inspekt.gg/cs2embed.js';
script.onload = () => resolve(window.CS2Embed);
script.onerror = reject;
document.head.appendChild(script);
});
}
return loaderPromise;
}
onMounted(async () => {
const CS2Embed = await loadCs2Embed();
viewer = CS2Embed.mount(host.value, {
inspect: props.inspect,
scene: props.scene,
autorotate: true,
});
viewer.on('error', (e) => console.error('embed refused:', e.code, e.message));
});
onBeforeUnmount(() => viewer?.destroy());
watch(() => props.inspect, (inspect) => viewer?.setItem({ inspect }));
</script>
html, no script tag
<iframe
src="https://inspekt.gg/embed?inspect=steam%3A%2F%2Frungame%2F730%2F76561202255233023%2F%2Bcsgo_econ_action_preview%2000180720...&scene=plate_train_video&autorotate=1&origin=https%3A%2F%2Fyoursite.example"
style="width:640px;height:400px;border:0"
loading="lazy"
allow="autoplay; fullscreen"
referrerpolicy="strict-origin"
title="3D skin viewer"
></iframe>
<script>
// optional: catch access refusals and item fallbacks without the loader
window.addEventListener('message', function (e) {
if (e.origin !== 'https://inspekt.gg') return;
var d = e.data;
if (!d || d.channel !== 'cs2embed') return;
if (d.type === 'error') console.error('embed refused:', d.code, d.message);
});
</script>
For every option each snippet accepts, see Embed parameters; for the
full command/event surface behind on(), setItem(), and friends, see
postMessage API. To try parameters live before you commit to a
framework, use the sandbox: it regenerates all four of these snippets as
you change item, scene and view.