Embed the Kreativsimulator via <iframe>
Embed the Kreativsimulator into your own website in seconds – and optionally drive it entirely from the outside: your own product picker, your own creatives, your own UI. This guide walks every step with copy-paste code and a live demo to try.
Get your domains whitelisted
For security reasons the browser only allows embedding from domains we have whitelisted in advance (Content-Security-Policy: frame-ancestors).
Before embedding, send us all domains the iframe should appear on. We add them to your tenant configuration. Wildcards are supported.
For example:
https://www.your-agency.comhttps://campaign.your-agency.comhttps://*.your-agency.com (wildcard)
Without whitelisting the iframe stays blank (white area). The live demo below also only loads if the embedding domain is whitelisted – otherwise use the “Open in new tab” link.
Embed code
A single <iframe> tag is enough. No API key, no extra configuration on your side – authentication runs entirely server-side.
View code HTML
<iframe
src="https://demo.kreativsimulator.com/"
title="Kreativsimulator"
style="width: 100%; height: 720px; border: 0;"
allow="fullscreen"
loading="lazy"
></iframe> Embed URL: https://<dein-mandant>.kreativsimulator.com/
The tenant is derived from the subdomain. For the public sandbox use demo.kreativsimulator.com.
Language and start product can optionally be preset via query parameters – see the next section.
URL parameters
Optionally append these query parameters to the embed URL (start with ?, chain with &):
| Parameter | Values | Effect | Default |
|---|---|---|---|
lang | de · en · fr · it | UI language | Tenant → browser → de |
product | Product code (ec5472c2) | Loads this product directly (deep link). Unknown code → first product. | First product |
header | 0 · 1 | Show/hide the header (logo + product selector) | 1 (visible) |
sidebar | 0 · 1 | Show/hide the sidebar (incl. mobile drawer + hamburger) | 1 (visible) |
playback | 0 · 1 | Show/hide the playback/transport buttons in the bar | 1 (visible) |
timeline | 0 · 1 | Show/hide the timeline (scrubbing bar) | 1 (visible) |
export | 0 · 1 | Show/hide the “Export creation” button | 1 (visible) |
effects | 0 · 1 | Show/hide the effect dropdown (photo products only) – e.g. for your own picker | 1 (visible) |
fullscreen | 0 · 1 | Fullscreen icon: 0=never, 1=always (incl. desktop); omitted = mobile / in fullscreen only | adaptive |
embed | minimal · kiosk | Shorthand: hides header, sidebar and export (playback bar stays) | – |
theme | URL to a CSS file | Loads a custom theme (CSS variables) that overrides the tenant theme | – |
bg | any CSS color, transparent or image URL | Background of the area around the canvas (letterbox/frame), not the video | Theme background |
stage | <width>x<height> (1280x720) | Fixed display pixel size of the canvas instead of fluid width | fluid (100%) |
placeholderBg | any CSS color | Background of the placeholder box (loader without a creative) – direct shortcut, no ?theme= file | Theme/default (#ffffff) |
placeholderText | any CSS color | Color of the letter inside the placeholder box | Theme/default (#000000) |
placeholderBorder | any CSS color | Border color of the placeholder box | Theme/default (#000000) |
- Boolean parameters accept
0/1(alsotrue/false,yes/no).embed=kioskis evaluated first – individual parameters override it afterwards (e.g.?embed=kiosk&header=1still shows the header). - The visibility switches (
header/sidebar/playback/timeline/export/effects) can also be toggled at runtime viasetChrome(see API). Playback, export and effect selection always work via postMessage – regardless of whether the matching controls are visible. So you can build a fully custom UI (your own effect picker viasetEffect, hide the built-in dropdown witheffects=0). - theme CSS: loaded as an extra
<link>after the tenant theme, overriding its CSS variables (e.g.--vc-accent,--vc-bg). CSS cannot run JavaScript; the file may be cross-origin (no CORS needed). Values for?theme=/?bg=must be URL-encoded (e.g.%23for#). bg=transparent: the parent page shows through the area around the canvas (best with?embed=kiosk). The video canvas itself always stays opaque – transparent only affects the frame/letterbox edges.- Placeholder colors directly in the URL (
placeholderBg/placeholderText/placeholderBorder): brands just the placeholder box (the generated letter motif shown until a creative is set) – a shortcut with no?theme=file. Accepts any CSS color (hex%23ff5a10,rgb(...),hsl(...), names likewhite) and overrides the tenant theme and?theme=; only the params you set override. Tip: don't setplaceholderBgequal toplaceholderText(invisible letter). For more branding (accent, buttons, spinner …) keep using?theme=.
Examples
View code URL
https://demo.kreativsimulator.com/ # default
https://demo.kreativsimulator.com/?lang=en # English UI
https://demo.kreativsimulator.com/?product=ec5472c2 # open a product
https://demo.kreativsimulator.com/?product=ec5472c2&lang=fr # language + product
https://demo.kreativsimulator.com/?embed=kiosk # canvas + playback bar only
https://demo.kreativsimulator.com/?header=0&sidebar=0 # header + sidebar off
https://demo.kreativsimulator.com/?timeline=0&playback=0 # timeline + transport off
https://demo.kreativsimulator.com/?effects=0 # effect dropdown off (own picker)
https://demo.kreativsimulator.com/?fullscreen=1 # fullscreen icon also on desktop
https://demo.kreativsimulator.com/?stage=1280x720 # fixed 1280×720 canvas
https://demo.kreativsimulator.com/?bg=%23000000 # black letterbox area
https://demo.kreativsimulator.com/?embed=kiosk&bg=transparent # parent page shows through
https://demo.kreativsimulator.com/?theme=https%3A%2F%2Fcdn.kunde.de%2Fks-theme.css
# Placeholder colors directly (no theme file) — encode # as %23:
https://demo.kreativsimulator.com/?placeholderBg=%23ff5a10&placeholderText=%23ffffff&placeholderBorder=%23ffffff
# … or with rgb()/color names (no %23 needed):
https://demo.kreativsimulator.com/?placeholderBg=rgb(255,90,16)&placeholderText=white&placeholderBorder=white Embed responsively
In embed mode (chrome off, e.g. ?embed=kiosk or ?sidebar=0) the canvas fills the iframe edge-to-edge and stays centered – no left offset, no 1280px cap. So just drop the iframe with width:100%; height:100% into any sized parent container (flexbox/grid too). If its aspect ratio differs from 16:9 you get a symmetric letterbox (color via ?bg=).
Recommended recipe – the iframe fills its flex/grid container:
View code HTML
<!-- Any parent container; the iframe fills it completely. -->
<div style="display:flex; width:100%; height:480px;">
<iframe
src="https://demo.kreativsimulator.com/?embed=kiosk"
title="Kreativsimulator"
style="flex:1; border:0;"
allow="fullscreen"
loading="lazy"
></iframe>
</div> For an exact aspect ratio without letterboxing, give the wrapper aspect-ratio: 16 / 9:
View code HTML
<!-- Exact 16:9, no letterbox: constrain the wrapper. -->
<div style="aspect-ratio: 16 / 9; width: 100%;">
<iframe
src="https://demo.kreativsimulator.com/?embed=kiosk"
title="Kreativsimulator"
style="width: 100%; height: 100%; border: 0;"
allow="fullscreen"
loading="lazy"
></iframe>
</div> With the sidebar visible (standalone view without ?embed/?sidebar=0) the canvas stays left-aligned (max 1280px) – the fill/center logic deliberately applies only in embed mode.
Just the canvas – drive everything yourself
The most common case: show only the WebGL canvas and build the entire UI yourself. Hide header, sidebar and buttons – via URL with ?embed=kiosk (header + sidebar + export off, playback bar stays) or selectively with ?header=0&sidebar=0&playback=0&export=0. At runtime the same works via setChrome.
The key point: playback and export always work via postMessage – regardless of whether the built-in buttons are visible. So your own buttons drive the iframe; for a custom progress bar, listen to the playbackState event.
Handy without the sidebar: when the sidebar is hidden, clicking a placeholder directly on the canvas opens the file dialog for exactly that placeholder – so end users can upload a creative even without your own upload UI.
embed=kiosk hides the built-in chrome – your buttons send play/pause/seek/export. Try it in the live demo below with the “Chrome” toggles and the custom playback bar.
Canvas in kiosk mode + your own playback controls, kept in sync via the playbackState event:
View code HTML + JS
<!-- Canvas only — your own playback UI drives it from the outside -->
<iframe id="ks" src="https://demo.kreativsimulator.com/?embed=kiosk"
style="width:100%;height:720px;border:0" allow="fullscreen"></iframe>
<button id="play">▶︎</button>
<button id="pause">⏸</button>
<button id="stop">⏹</button>
<button id="back">⏮</button>
<button id="fwd">⏭</button>
<input id="scrub" type="range" min="0" value="0" />
<button id="export">Export</button>
<button id="fs">⛶ Fullscreen</button>
<script>
const iframe = document.getElementById("ks");
const send = (msg) => iframe.contentWindow.postMessage(msg, "*");
const P = "kreativsimulator:";
play.onclick = () => send({ type: P + "play" });
pause.onclick = () => send({ type: P + "pause" });
stop.onclick = () => send({ type: P + "stop" });
back.onclick = () => send({ type: P + "stepBackward" });
fwd.onclick = () => send({ type: P + "stepForward" });
scrub.oninput = (e) => send({ type: P + "seek", frame: Number(e.target.value) });
// Direct export at 1280×720 (no dialog):
export.onclick = () =>
send({ type: P + "export", filename: "creation", width: 1280, height: 720 });
// Fullscreen ON reliably via requestFullscreen on the iframe (the user gesture
// is here, on the click). OFF/toggle could also go via the postMessage event.
fs.onclick = () => {
if (document.fullscreenElement) document.exitFullscreen();
else iframe.requestFullscreen(); // iframe needs allow="fullscreen"
};
window.addEventListener("message", (e) => {
const m = e.data;
if (!m || m.source !== "kreativsimulator") return;
if (m.type === "playbackState") {
scrub.max = m.frameCount - 1;
if (document.activeElement !== scrub) scrub.value = m.frame; // don't fight the drag
play.disabled = m.playing;
pause.disabled = !m.playing;
}
if (m.type === "fullscreenChanged") {
fs.textContent = m.fullscreen ? "⛶ Exit fullscreen" : "⛶ Fullscreen";
}
});
// Ask for the current state once (e.g. after load):
// send({ type: P + "getState" });
</script> Your own design via CSS (theme & bg)
The iframe adopts your branding through CSS variables. ?theme= loads a custom CSS file after the tenant theme and overrides its variables (e.g. --vc-accent, --vc-bg). ?bg= sets the letterbox area around the canvas (color or image URL), ?stage= a fixed canvas pixel size.
Embed with theme + background
View code HTML
<!-- Your own theme + a black letterbox around the canvas -->
<iframe
src="https://demo.kreativsimulator.com/?embed=kiosk&theme=https%3A%2F%2Fcdn.kunde.de%2Fks-theme.css&bg=%23000000"
title="Kreativsimulator"
style="width: 100%; height: 720px; border: 0;"
allow="fullscreen"
loading="lazy"
></iframe> Theme template
Download templateks-theme.css View template kreativsimulator-theme-template.css
/* =============================================================================
Kreativsimulator – Theme-Vorlage (CSS Custom Properties)
=============================================================================
So passt du das Aussehen des eingebetteten Kreativsimulators an deine Marke an.
VERWENDUNG
----------
1. Diese Datei kopieren, Werte unten anpassen, auf einem öffentlich
erreichbaren HTTPS-URL ablegen (z. B. https://cdn.deine-agentur.de/ks-theme.css).
2. Den URL per `?theme=` an die iframe-Einbettung hängen – URL-kodiert:
<iframe src="https://demo.kreativsimulator.com/?theme=https%3A%2F%2Fcdn.deine-agentur.de%2Fks-theme.css"
style="width:100%;height:720px;border:0" allow="fullscreen"></iframe>
HINWEISE
--------
• Die Datei wird als zusätzliches <link> NACH dem Mandanten-Theme geladen und
überschreibt dessen Werte (CSS-Kaskade). Cross-Origin ist erlaubt (kein CORS
nötig) – CSS kann kein JavaScript ausführen.
• Du musst NICHT alle Variablen setzen. Lösche einfach die Zeilen, die du nicht
ändern willst – dann gilt der jeweilige Standard des Simulators/Mandanten.
• Farben: jede gültige CSS-Farbe (#hex, rgb(), hsl(), benannt).
• Den Hintergrund UM den Canvas (Letterbox) steuerst du besser per
`?bg=<farbe|transparent|url>` – nicht über `--vc-bg` (das ist auch der
Canvas-Hintergrund während des Ladens).
========================================================================== */
:root {
/* ── Meta ──────────────────────────────────────────────────────────────
Hell-/Dunkel-Schema. Beeinflusst u. a. native Formular-Elemente.
Werte: dark | light */
--vc-color-scheme: dark;
/* Schriftfamilie der gesamten Oberfläche.
Eigene Webfont? Oben in der Datei per @import laden, z. B.:
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;600&display=swap');
und hier referenzieren: 'Inter', system-ui, sans-serif */
--vc-font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
/* ── Flächen ───────────────────────────────────────────────────────────
Grundfarben der UI-Container (Sidebar, Dialoge, Karten). */
--vc-bg: #0a0a0a; /* App-Hintergrund / Canvas-Hintergrund beim Laden */
--vc-surface: #181818; /* Sidebar, Dialog-Flächen */
--vc-surface-raised: #2a2a2a;/* erhöhte Elemente (Tracks, Chips, Inputs) */
--vc-surface-hover: #3a3a3a; /* Hover-Fläche */
--vc-border: #2a2a2a; /* Standard-Rahmenfarbe */
--vc-border-strong: #444444; /* betonte Rahmen */
/* ── Text ──────────────────────────────────────────────────────────────*/
--vc-text: #eeeeee; /* Standard-Textfarbe */
--vc-text-muted: #888888; /* sekundärer Text */
--vc-text-subtle: #666666; /* dezente Hinweise */
--vc-text-on-accent: #ffffff;/* Text auf Akzent-/Primärflächen */
/* ── Akzent (Marke) ────────────────────────────────────────────────────
Wird u. a. für aktive Zustände, Timeline/Abspielkopf und Slider-Punkte
verwendet. `--vc-accent-rgb` MUSS dieselbe Farbe als "R, G, B" enthalten
(ohne rgb()-Klammern) – wird für halbtransparente Hervorhebungen genutzt. */
--vc-accent: #df3b00;
--vc-accent-strong: #ff5722; /* kräftigere Akzentvariante (Hover) */
--vc-accent-rgb: 223, 59, 0; /* = --vc-accent als R, G, B */
--vc-accent-muted: #3a3a3a; /* gedämpfter Akzent-Hintergrund */
/* ── Header (Kopfzeile mit Logo + Produktauswahl) ──────────────────────
Nur relevant, wenn der Header sichtbar ist (sonst per ?header=0 ausblenden). */
--vc-header-bg: transparent; /* Hintergrund der Kopfzeile */
--vc-header-text: #eeeeee; /* Titel-/Header-Text */
--vc-header-text-muted: #888888; /* sekundärer Header-Text */
--vc-header-border: #1a1a1a; /* untere Trennlinie */
--vc-header-border-width: 1px; /* Stärke der Trennlinie (0px = keine) */
--vc-header-padding: 12px 20px; /* Innenabstand der Kopfzeile */
--vc-logo-height: 26px; /* Höhe des Mandanten-Logos */
/* ── Buttons ───────────────────────────────────────────────────────────*/
--vc-button-bg: #2a2a2a; /* Button-Fläche */
--vc-button-bg-hover: #3a3a3a; /* Button-Fläche bei Hover */
--vc-button-text: inherit; /* Button-Textfarbe */
--vc-button-border: #444444; /* Button-Rahmen */
--vc-button-radius: 4px; /* Eckenradius der Buttons */
/* ── Dialoge (Export-Dialog „Export als MP4") ──────────────────────────
Ohne Override folgen sie den Surface-/Border-Variablen. */
--vc-dialog-bg: var(--vc-surface); /* Dialog-Fläche */
--vc-dialog-border: var(--vc-border); /* Dialog-Rahmen */
--vc-dialog-radius: 8px; /* Eckenradius des Dialogs */
--vc-dialog-backdrop: rgba(0, 0, 0, 0.6); /* Abdunklung hinter dem Dialog */
/* ── Eingabefelder & Selects ───────────────────────────────────────────
Gilt für alle Input-/Select-Felder – auch das Effekt-Dropdown bei
Foto-Produkten. Ohne Override folgen sie den Surface-/Text-Variablen. */
--vc-input-bg: var(--vc-surface-raised); /* Feld-Hintergrund */
--vc-input-border: var(--vc-border-strong); /* Feld-Rahmen */
--vc-input-text: var(--vc-text); /* Eingabetext */
--vc-input-radius: 4px; /* Eckenradius der Felder */
--vc-label-text: var(--vc-text-muted); /* Feld-Beschriftungen */
/* ── Slider / Zeitleiste ───────────────────────────────────────────────
Der Abspielkopf und die In/Out-Marken nutzen --vc-accent (s. o.). */
--vc-slider-track: #3a3a3a; /* Track-Farbe der Regler */
/* ── Lade-Spinner (Ladeanzeige beim Initialisieren / Footage-Laden) ────
Ohne Override folgt der Spinner automatisch --vc-accent (Bogen) bzw.
--vc-border (Ring) – diese Zeilen also nur setzen, wenn du ihn abweichend
gestalten willst. */
--vc-spinner-color: var(--vc-accent); /* rotierender Bogen */
--vc-spinner-track: var(--vc-border); /* ruhender Ring */
--vc-spinner-size: 48px; /* Durchmesser */
/* ── Vorschau-Canvas (Rahmen um das Video) ─────────────────────────────*/
--vc-canvas-outline-color: #2a2a2a; /* Outline-Farbe des Canvas */
--vc-canvas-outline-width: 1px; /* Outline-Stärke (0px = kein Rahmen) */
--vc-canvas-radius: 0px; /* Eckenradius des Canvas – z. B. 16px für
runde Ecken (rundet Video, Lade-Fläche
und Rahmen einheitlich). 0px = eckig. */
/* ── Viewport-Rahmen (optionaler Rahmen um die ganze Fläche) ───────────*/
--vc-frame-color: transparent; /* Farbe des Außenrahmens */
--vc-frame-width: 0px; /* Stärke (0px = aus) */
/* ── Platzhalter (Plakatflächen ohne hochgeladenes Motiv) ──────────────
Die generierte „A/B"-Platzhaltertextur. */
--vc-placeholder-bg: #ffffff; /* Hintergrund der Platzhalterfläche */
--vc-placeholder-text: #000000; /* Buchstabe/Beschriftung */
--vc-placeholder-border: #000000; /* Rahmen der Platzhalterfläche */
}
/* =============================================================================
BEISPIEL: helles Marken-Theme (zum Vergleich auskommentiert).
Zum Ausprobieren das obige :root entfernen und dieses aktivieren.
============================================================================*/
/*
:root {
--vc-color-scheme: light;
--vc-bg: #ffffff;
--vc-surface: #ffffff;
--vc-surface-raised: #f2f2f2;
--vc-surface-hover: #e8e8e8;
--vc-border: #e2e2e2;
--vc-border-strong: #c2c2c2;
--vc-text: #1a1a22;
--vc-text-muted: #5e5e68;
--vc-text-subtle: #8c8c94;
--vc-text-on-accent: #ffffff;
--vc-accent: #0066ff;
--vc-accent-strong: #0047b3;
--vc-accent-rgb: 0, 102, 255;
--vc-accent-muted: #d6e4ff;
--vc-header-bg: #ffffff;
--vc-header-text: #1a1a22;
--vc-header-text-muted: #6a6a74;
--vc-header-border: #e2e2e2;
--vc-header-border-width: 1px;
--vc-button-bg: #0066ff;
--vc-button-bg-hover: #0047b3;
--vc-button-text: #ffffff;
--vc-button-border: #0066ff;
--vc-button-radius: 6px;
--vc-slider-track: #e0e0e0;
--vc-canvas-outline-color: #0066ff;
--vc-canvas-outline-width: 2px;
}
*/ - No CORS needed: the CSS file is loaded as a
<link>(not viafetchlike creatives) and may be cross-origin. CSS cannot run JavaScript. - URL-encode: values for
?theme=/?bg=must be URL-encoded (e.g.%23for#,%2Ffor/). - The loading spinner is themeable too:
--vc-spinner-color(rotating arc),--vc-spinner-track(ring) and--vc-spinner-size(diameter) – without an override it follows--vc-accent/--vc-borderautomatically. - Rounded canvas corners:
--vc-canvas-radius(default0px) rounds the preview canvas – video, loading area and frame alike – e.g.--vc-canvas-radius: 16px;in the?theme=CSS. - Dialogs & input fields are themeable too: the export dialog (
--vc-dialog-bg,--vc-dialog-border,--vc-dialog-radius,--vc-dialog-backdrop) and all input/select fields incl. the photo-effect dropdown (--vc-input-bg,--vc-input-border,--vc-input-text,--vc-input-radius,--vc-label-text) can be styled individually – without an override they follow the surface/text variables. - Only placeholder colors? You don't need a theme file for that:
?placeholderBg=/?placeholderText=/?placeholderBorder=set the placeholder box directly via URL (see URL parameters above) and override the theme +?theme=. - You'll find the full list of overridable CSS variables in the theme template below.
Try remote control live
Here you see the real iframe (left) next to a completely custom control UI (right) – your own product images, your own creatives, your own buttons. Every click sends a postMessage to the iframe. This is exactly how you rebuild it on your site; the matching code is right below.
Real iframe from demo.kreativsimulator.com Open in new tab
Show / hide chrome
Hide header/sidebar/buttons live → setChrome. “Canvas only” hides everything.
Your playback bar
Works even without visible buttons. Scrubber via seek, synced via playbackState.
Ken-Burns effects
Photo products only: camera moves from ready.effects. Click → setEffect.
Your product picker
Your page builds these cards from the ready event. Click → setProduct. Photo products show their effect count via getEffects (pre-fetched, no product switch).
Your creatives
Your own placeholder creatives. Click → setCreative (loads the image via CORS).
Your own file per placeholder
Drag an image onto A or B → setCreative with loader. In production you upload the file to your CORS host and pass the URL; here it's a data URL for the demo.
Export
Resolution from ready.render.presets. Click → export (MP4).
Load variants (URL parameters)
Reloads the iframe with parameters – this is how embed/bg/stage take effect.
Placeholder colors (URL)
Brands just the placeholder box directly via URL – no ?theme= file. “Apply” reloads the iframe with ?placeholderBg/Text/Border.
Language
Event log (iframe → your page)
- No events received yet …
The following code drives exactly this live demo. Use it as your starting point:
View code HTML + JS
<iframe id="ks" src="https://demo.kreativsimulator.com/"
style="width:100%;height:720px;border:0" allow="fullscreen"></iframe>
<script>
const iframe = document.getElementById("ks");
const send = (msg) => iframe.contentWindow.postMessage(msg, "*");
window.addEventListener("message", (e) => {
const m = e.data;
if (!m || m.source !== "kreativsimulator") return;
if (m.type === "ready") {
// Build your own buttons from the product list
for (const p of m.products) {
const btn = document.createElement("button");
btn.textContent = p.name;
btn.onclick = () =>
send({ type: "kreativsimulator:setProduct", code: p.code });
document.body.appendChild(btn);
}
// Build your own resolution picker + export button from m.render.presets
const sel = document.createElement("select");
for (const r of m.render.presets) {
const o = document.createElement("option");
o.value = r.width + "x" + r.height;
o.textContent = r.label;
sel.appendChild(o);
}
const exportBtn = document.createElement("button");
exportBtn.textContent = "Export";
exportBtn.onclick = () => {
const [width, height] = sel.value.split("x").map(Number);
send({ type: "kreativsimulator:export", filename: "creation", width, height });
};
document.body.append(sel, exportBtn);
}
// The product is active now → only here may you target a placeholder.
if (m.type === "productChanged") {
const first = m.loaders[0]; // take the name from the event
if (first) {
send({
type: "kreativsimulator:setCreative",
url: "https://cdn.your-agency.com/creative.png",
loader: first.name, // this placeholder only
});
}
}
if (m.type === "error") console.warn("KS error:", m.message);
});
// Creative on ALL placeholders (no loader) — fine right after `ready`:
// send({ type: "kreativsimulator:setCreative", url: "https://cdn.your-agency.com/creative.png" });
</script> postMessage API
Build your own UI and drive the iframe via window.postMessage. The iframe also sends events back (including the list of available product codes).
Incoming commands (your page → iframe)
| Message | Effect |
|---|---|
{ type: "kreativsimulator:setProduct", code: "ec5472c2" } | Loads the product with this code. |
{ type: "kreativsimulator:setCreative", url: "https://…/creative.png" } | Places the image/video as the creative on all placeholders. |
{ type: "kreativsimulator:setCreative", url: "…", loader: "Loader A" } | Fills only the named placeholder. |
{ type: "kreativsimulator:clearCreative", loader?: "Loader A" } | Resets one (or without loader all) placeholders to the default. |
{ type: "kreativsimulator:setLanguage", lang: "fr" } | Switches the UI language (de/en/fr/it). |
{ type: "kreativsimulator:setEffect", id: "static-5s" } | Photo products: selects an entry from ready.effects – a static preset (static-5s/static-10s, always present) or a named Ken-Burns effect. id: null = static still (1 frame). Changes the timeline length → a playbackState follows. |
{ type: "kreativsimulator:getEffects", code: "ec5472c2" } | Queries the effect list of any product without activating it (live view stays unchanged) → reply as an effects event. For a per-product effect picker before calling setProduct. |
{ type: "kreativsimulator:play" } · pause · stop | Starts / pauses / stops (back to start) playback. |
{ type: "kreativsimulator:stepForward", frames?: 1 } · stepBackward | Steps frames frames forward/back (default 1) and pauses. |
{ type: "kreativsimulator:seek", frame: 120 } | Jumps to an absolute frame index (0…frameCount-1). |
{ type: "kreativsimulator:export", filename?, width?, height? } | Starts the MP4 export. With width+height directly at any resolution; without, the export dialog opens. |
{ type: "kreativsimulator:setChrome", header?, sidebar?, playback?, timeline?, export?, effects? } | Toggles individual UI areas at runtime (each true/false). effects = effect dropdown (photo products). |
{ type: "kreativsimulator:fullscreen", enabled? } | Fullscreen: true=on, false=off, without enabled=toggle (see note). |
{ type: "kreativsimulator:getState" } | Requests the current playback state → reply as playbackState. |
Outgoing events (iframe → your page)
View code JS
{ source: "kreativsimulator", v: 1, type: "ready",
product: "ec5472c2", locale: "de",
products: [{ code: "ec5472c2", name: "City-Light …" }, …],
loaders: [{ name: "Loader A", aspectRatio: 2.96 }, …],
// native render resolution (16:9) + suggested export presets
render: { width: 1280, height: 720, aspectRatio: 1.7778,
presets: [{ label: "E-Mail / WhatsApp (640×360)", width: 640, height: 360 },
{ label: "Standard HD (1280×720)", width: 1280, height: 720 },
{ label: "YouTube / Full HD (1920×1080)", width: 1920, height: 1080 }] },
// photo products (productType "bild"): effects[] = two always-present static
// presets (static-5s, static-10s) + named Ken-Burns effects; default = static-5s.
// video products report isPhoto:false, effects:[], selectedEffect:null.
isPhoto: true,
effects: [{ id: "static-5s", name: "Static (5s)", durationFrames: 125 },
{ id: "static-10s", name: "Static (10s)", durationFrames: 250 },
{ id: "fx-a", name: "Effect A", durationFrames: 125 }, …],
selectedEffect: "static-5s" }
{ source: "kreativsimulator", v: 1, type: "productChanged", code: "ec5472c2",
loaders: [{ name: "Loader A", aspectRatio: 2.96 }, …],
isPhoto: true,
effects: [{ id: "static-5s", name: "Static (5s)", durationFrames: 125 },
{ id: "static-10s", name: "Static (10s)", durationFrames: 250 },
{ id: "fx-a", name: "Effect A", durationFrames: 125 }, …],
selectedEffect: "static-5s" }
// reply to getEffects {code}: a product's effect list WITHOUT activating it.
// same fields as productChanged (incl. the static presets), plus code.
// video products report isPhoto:false, effects:[], selectedEffect:null.
{ source: "kreativsimulator", v: 1, type: "effects", code: "ec5472c2",
isPhoto: true,
effects: [{ id: "static-5s", name: "Static (5s)", durationFrames: 125 },
{ id: "static-10s", name: "Static (10s)", durationFrames: 250 },
{ id: "fx-a", name: "Effect A", durationFrames: 125 }, …],
selectedEffect: "static-5s" }
// playback state: on play/pause, ~4×/s while playing, after seek/stop/step, and on getState
// for photo products, frameCount equals the active effect's duration
{ source: "kreativsimulator", v: 1, type: "playbackState",
frame: 120, playing: true, frameCount: 250, fps: 25 }
// confirmation after setChrome, with the new visibility state
{ source: "kreativsimulator", v: 1, type: "chromeChanged",
header: false, sidebar: false, playback: true, timeline: true, export: false, effects: false }
// fullscreen state — on every change (e.g. ESC); for syncing your own button
{ source: "kreativsimulator", v: 1, type: "fullscreenChanged", fullscreen: true }
{ source: "kreativsimulator", v: 1, type: "error", message: "…", code, loader } Always filter incoming messages on source === "kreativsimulator". v is the protocol version (currently 1).
Complete example
Build your own buttons from the product list, place a creative on a targeted placeholder (correct ordering), and a custom resolution picker from render.presets:
View code HTML + JS
<iframe id="ks" src="https://demo.kreativsimulator.com/"
style="width:100%;height:720px;border:0" allow="fullscreen"></iframe>
<script>
const iframe = document.getElementById("ks");
const send = (msg) => iframe.contentWindow.postMessage(msg, "*");
window.addEventListener("message", (e) => {
const m = e.data;
if (!m || m.source !== "kreativsimulator") return;
if (m.type === "ready") {
// Build your own buttons from the product list
for (const p of m.products) {
const btn = document.createElement("button");
btn.textContent = p.name;
btn.onclick = () =>
send({ type: "kreativsimulator:setProduct", code: p.code });
document.body.appendChild(btn);
}
// Build your own resolution picker + export button from m.render.presets
const sel = document.createElement("select");
for (const r of m.render.presets) {
const o = document.createElement("option");
o.value = r.width + "x" + r.height;
o.textContent = r.label;
sel.appendChild(o);
}
const exportBtn = document.createElement("button");
exportBtn.textContent = "Export";
exportBtn.onclick = () => {
const [width, height] = sel.value.split("x").map(Number);
send({ type: "kreativsimulator:export", filename: "creation", width, height });
};
document.body.append(sel, exportBtn);
}
// The product is active now → only here may you target a placeholder.
if (m.type === "productChanged") {
const first = m.loaders[0]; // take the name from the event
if (first) {
send({
type: "kreativsimulator:setCreative",
url: "https://cdn.your-agency.com/creative.png",
loader: first.name, // this placeholder only
});
}
}
if (m.type === "error") console.warn("KS error:", m.message);
});
// Creative on ALL placeholders (no loader) — fine right after `ready`:
// send({ type: "kreativsimulator:setCreative", url: "https://cdn.your-agency.com/creative.png" });
</script> Important
- Ordering on product change: after
setProductthe new product is only active once theproductChangedevent arrives. Send placeholder-targeted commands (setCreative/clearCreativewithloader) only afterwards – before that the iframe only knows the old product's placeholders. A product change also resets all creatives. - Your own controls: playback (
play/pause/stop/step…/seek) andexportalways work – whether or not the built-in buttons are visible. Hide them with?embed=kioskand drive everything yourself. - Pick the export resolution:
exportwithwidth+heightrenders at any resolution. Native resolution (1280×720, 16:9) and presets come fromready.render– keep the 16:9 aspect ratio or the image distorts. - Photo products & Ken-Burns effects: some products are still images with named camera moves.
ready/productChangedthen reportisPhoto: trueand a list ofeffects({ id, name, durationFrames }). The list always starts with two static presets (static-5s= 5 s,static-10s= 10 s), followed by the named effects; on loadstatic-5sis selected automatically (selectedEffect). UsesetEffect { id }to switch entry (orid: nullfor a pure still image, 1 frame). Playback and export work just like video;playbackState.frameCountequals the active entry's duration. - Pre-fetch effects (without switching product):
ready/productChangedonly carry the effects of the active product. To build an effect picker for other products without switching the live view, sendgetEffects { code }and listen for theeffectsevent (same fields asproductChanged, pluscode). That gives you each effect'sid/nameto drive later viasetEffect(aftersetProductto that code). - Turn fullscreen on reliably: browsers only allow enabling it as a direct reaction to a user gesture – a
fullscreenpostMessage usually lacks that and may be blocked. So calliframe.requestFullscreen()on your page on click (the iframe needsallow="fullscreen"); the postMessage is fine for off/toggle. The state comes viafullscreenChanged. - Placeholder names are not free-form – use exactly one
namefrom theloaders[]array ofreadyorproductChanged. - CORS: on
setCreativethe iframe fetches the image/video URL. The host of your file must therefore sendAccess-Control-Allow-Origin, otherwise anerrorevent comes back. - Security: which pages may embed the iframe is set server-side via
frame-ancestors– only those can send messages at all.
Good to know
- No API key needed – authentication runs entirely server-side.
- Users can operate the simulator directly in the iframe or you drive it via postMessage from your page.
- The iframe loads video footage – a stable connection is recommended.
Troubleshooting
The iframe stays blank / white area
Most likely the embedding domain is not whitelisted. The browser console (F12) then shows “Refused to frame … frame-ancestors …”. Send us the exact domain (incl. https:// and any subdomain) and we'll whitelist it.
The iframe shows “403”
The tenant is not yet fully configured server-side (no API key stored). Please contact us.
A creative doesn't appear (error event)
The creative URL sends no CORS, or the loader name doesn't match the active product. Check the image URL's Access-Control-Allow-Origin and send loader-targeted commands only after the productChanged event.
Ready to embed?
Send us your domain(s) – we'll whitelist the embed and you'll be live in minutes.