Zum Inhalt springen

Kreativsimulator per <iframe> einbinden

Mit einem einzigen iframe-Tag läuft der Kreativsimulator auf Ihrer eigenen Website. Wenn Sie möchten, steuern Sie ihn auch von außen, mit Ihrer eigenen Produktauswahl, Ihren Motiven und Ihren Bedienelementen. Diese Anleitung zeigt jeden Schritt mit kopierbarem Code. Weiter unten können Sie alles in einer Live-Demo ausprobieren.

Für KI-Coding-Agenten (Claude Code, Cursor & Co.)

Diese Anleitung gibt es auch als Markdown unter einer eigenen URL, die Ihr Coding-Agent direkt lesen kann. Geben Sie ihm diesen Prompt:

Prompt Prompt
Prompt
Lies https://virtualcampaign.dev/embed.md und binde den Kreativsimulator per iframe in unsere Website ein.

/embed.md /llms.txt

Außerdem verlinkt https://virtualcampaign.dev/llms.txt die Doku nach der llms.txt-Konvention. Dort finden KI-Agenten sie von selbst.

Schritt 1

Domains freischalten

Aus Sicherheitsgründen erlaubt der Browser das Einbetten nur von Domains, die freigeschaltet sind (Content-Security-Policy: frame-ancestors).

Freigeschaltet werden müssen alle Domains, auf denen der iframe erscheinen soll. Wildcards sind möglich.

Zum Beispiel:

  • https://www.ihre-agentur.de
  • https://kampagne.ihre-agentur.de
  • https://*.ihre-agentur.de (Wildcard)

Wer trägt die Domains ein?

  • Mandanten mit Zugang zu VirtualCampaign pflegen die Liste selbst: Einstellungen → Kreativsimulator → Erlaubte Webseiten für die Einbindung, eine Adresse pro Zeile. Dort stellen Sie auch Titel, Sprache, Logo und Theme ein. Änderungen greifen nach spätestens einer Minute.
  • Alle anderen schicken uns die Domains. Wir tragen sie ein.

URL-Parameter Ihrer Einbindung (?theme=, ?lang=, ?logo= …) haben immer Vorrang vor den Einstellungen in VirtualCampaign.

Ohne Freischaltung bleibt der iframe leer (weiße Fläche). Das gilt auch für die Live-Demo weiter unten: Sie lädt nur auf einer freigeschalteten Domain. Andernfalls nutzen Sie den Link „In neuem Tab öffnen“.

Schritt 2

Einbettungs-Code

Ein einziges <iframe>-Tag genügt. Sie brauchen keinen API-Key im Browser und keine weitere Konfiguration, denn die Authentifizierung läuft vollständig serverseitig. Nur wenn Sie den Zugang mit einem Token schützen, holt Ihr Server pro Seitenaufruf ein Token (siehe Schritt 9).

Code ansehen HTML
HTML
<iframe
  src="https://demo.kreativsimulator.com/"
  title="Kreativsimulator"
  style="width: 100%; height: 720px; border: 0;"
  allow="fullscreen"
  loading="lazy"
></iframe>

Embed-URL: https://<tenant>.kreativsimulator.com/

Der Mandant wird aus der Subdomain abgeleitet. Für die öffentliche Sandbox verwenden Sie demo.kreativsimulator.com.

Sprache und Start-Produkt können Sie per Query-Parameter vorgeben. Wie das geht, steht im nächsten Abschnitt.

Schritt 3

URL-Parameter

Optional lassen sich folgende Query-Parameter an die Embed-URL anhängen (mit ? beginnen, mit & verketten):

ParameterWerteWirkungDefault
langde · en · fr · itUI-SpracheMandant → Browser → de
productProdukt-Code (ec5472c2)Lädt direkt dieses Produkt (Deep-Link). Unbekannter Code → erstes Produkt.Erstes Produkt
header0 · 1Kopfzeile (Produkt-Auswahl) aus-/einblenden1 (sichtbar)
sidebar0 · 1Seitenleiste aus-/einblenden. Geöffnet wird sie per Klick oder Tipp auf einen Platzhalter im Bild oder über die Lasche am rechten Rand. Ein Tipp auf einen noch leeren Platzhalter öffnet sofort die Motivauswahl. Ohne Seitenleiste öffnet der Klick die Dateiauswahl.1 (sichtbar)
playback0 · 1Abspiel-/Transport-Knöpfe in der Leiste aus-/einblenden1 (sichtbar)
timeline0 · 1Zeitleiste (Scrubbing-Balken) aus-/einblenden1 (sichtbar)
export0 · 1Export-Knopf rechts in der Abspielleiste aus-/einblenden: mit freigeschalteter Auswahl das Teilen-Symbol (Tooltip „Exportieren und teilen“), sonst das Download-Symbol (Tooltip „Kreation exportieren“). Ohne ihn bleibt im Dialog nur der Reiter „Meine Auswahl“.1 (sichtbar)
effects0 · 1Effekt-Auswahl (nur Foto-Produkte, Knopf unten links auf dem Bild) aus-/einblenden, z. B. für einen eigenen Picker1 (sichtbar)
viewAnsichts-Key (0e23675447)Nur bei freigeschalteter Ansichtsauswahl (siehe „Ansichten“): startet das Produkt aus product in dieser Ansicht. Unbekannter Key → Standard-Ansicht.Standard-Ansicht
views0 · 1Nur bei freigeschalteter Ansichtsauswahl: Knopf „Ansichten (n)“ oben links auf dem Bild aus-/einblenden, z. B. für einen eigenen Picker1 (sichtbar)
selection0 · 1Nur bei freigeschalteter Auswahl (siehe „Auswahl, Teilen-Link und Slideshow“): Knöpfe + „Hinzufügen“ und „Auswahl“ aus-/einblenden1 (sichtbar)
library0 · 1Nur bei freigeschalteter Produkt-Bibliothek (siehe „Produkt-Bibliothek“): Knopf „Katalog (n)“ aus-/einblenden. Die Produkt-Leiste bleibt.1 (sichtbar)
hint0 · 1Tipp-Hinweis: Nach dem ersten Laden pulsieren die Platzhalter einmal in der Akzentfarbe (--vc-hint-color), damit Nutzer sehen, dass man darauf tippen kann. Hat ein Nutzer schon einmal einen Platzhalter angetippt, kommt er nicht wieder. Nie im Export. ?embed=kiosk schaltet ihn aus.1 (an)
editorauto · sidebar · overlayWo Platzhalter bearbeitet werden. overlay = ein Tipp auf den Platzhalter öffnet den Editor direkt auf dem Bild (Effekt-Symbole und Regler, jede Änderung sofort sichtbar, ✓ übernehmen, ✕ verwerfen) statt der Seitenleiste. sidebar = immer die Seitenleiste. auto = auf dem Handy (bis 768 px) der Editor auf dem Bild, am Desktop die Seitenleiste. Mit sidebar=0 oder embed=kiosk öffnet sich der Editor auf dem Bild nie von selbst.auto
selWert aus selectionLinkCopied.param bzw. selectionChanged.paramNur bei freigeschalteter Auswahl: öffnet eine geteilte oder gespeicherte Auswahl (Produkte, Ansichten und Motive per URL)–
fullscreen0 · 1Vollbild-Icon: 0=nie, 1=immer (auch Desktop); ohne Param = nur mobil/im Vollbildadaptiv
fit0 · 11 = das iframe bekommt das Layout der eigenen Seite: Der Canvas passt nach Breite und Höhe und steht mittig, die offene Sidebar nimmt rechts Platz weg und verdeckt den Canvas nicht, die Knopfleiste bricht bei wenig Platz um. Empfohlen für jede Einbindung mit sichtbarer Sidebar.0 (bisheriges iframe-Layout)
embedminimal · kioskKurzform: blendet Header, Sidebar und Export aus (Abspielleiste bleibt)–
themeURL zu einer CSS-DateiLädt ein eigenes Theme (CSS-Variablen), das das Mandanten-Theme überschreibt–
bgbeliebige CSS-Farbe, transparent oder Bild-URLHintergrund der Fläche um den Canvas (Letterbox/Rahmen), nicht das VideoTheme-Hintergrund
stageauto · <Breite>x<Höhe> (1024x1024)Form und Größe des Canvas. auto = Film/Clip 16:9, Foto im eigenen Seitenverhältnis (ganzes Foto, fluid). BxH = feste Form und feste Anzeige-Pixelgröße für alle Produkte (Bild wird mittig eingepasst, Ränder werden abgeschnitten). Zur Laufzeit per setStage/setProduct{stage} änderbar.auto
placeholderBgbeliebige CSS-FarbeHintergrund der Platzhalter-Box (Loader ohne Motiv) – Direkt-Shortcut ohne ?theme=-DateiTheme/Default (#ffffff)
placeholderTextbeliebige CSS-FarbeFarbe des Buchstabens in der Platzhalter-BoxTheme/Default (#000000)
placeholderBorderbeliebige CSS-FarbeRahmenfarbe der Platzhalter-BoxTheme/Default (#000000)
watermark0 · 11 = jedes exportierte MP4 und jedes gespeicherte Einzelbild (JPG) bekommt das „Vorschau“-Wasserzeichen (Dialog- und postMessage-Export). Nur zuschaltend – beim Demo-Mandanten immer an und nicht abschaltbar.0 (nur Demo-Mandant)
logo0 · 10 = exportiertes MP4 und Einzelbild ohne Mandanten-Logo oben links (Dialog- und postMessage-Export). Pro Export überschreibbar mit dem Feld logo an export/exportImage.1 (Logo im Export), außer der Mandant hat „ohne Logo“ als Standard (bitte bei uns anfragen)
  • Kein eigenes Branding in der Oberfläche: Der Simulator zeigt kein Logo und keinen Titel. Die Kopfzeile enthält nur die Produkt-Auswahl (und die Lasche der Seitenleiste), das Branding liefert Ihre Seite rund um das iframe. Im exportierten MP4 und im gespeicherten Einzelbild ist das Logo des Mandanten standardmäßig oben links einkomponiert. Auf Wunsch stellen wir den Standard für Ihren Mandanten auf „ohne Logo“ um. Außerdem steuern Sie es selbst: ?logo=0 schaltet es für alle Exporte der Einbettung ab, das Feld logo: false/true an export/exportImage für einen einzelnen Export.
  • Boolean-Parameter akzeptieren 0/1 (auch true/false, yes/no). embed=kiosk wird zuerst ausgewertet – einzelne Parameter überschreiben es danach (z. B. zeigt ?embed=kiosk&header=1 trotzdem die Kopfzeile).
  • Die Sichtbarkeits-Schalter (header/sidebar/playback/timeline/export/effects/hint/editor, mit Freischaltung auch views/selection/library) lassen sich auch zur Laufzeit per setChrome umschalten (siehe API). Wiedergabe, Export und Effekt-Auswahl funktionieren immer per postMessage, auch wenn die zugehörigen Bedienelemente ausgeblendet sind. So bauen Sie eine komplett eigene Bedienung (eigener Effekt-Picker via setEffect, eingebaute Effekt-Auswahl mit effects=0 ausblenden).
  • Tipp-Hinweis und Editor gelten auch für bestehende Einbindungen: Der Tipp-Hinweis ist standardmäßig an, abschalten mit ?hint=0 oder setChrome {hint: false}. Auf dem Handy (bis 768 px) öffnet ein Tipp auf einen Platzhalter den Editor direkt auf dem Bild. ?editor=sidebar zeigt überall die Seitenleiste. Das Protokoll bleibt v: 1, bestehende Nachrichten bekommen keine neuen Felder.
  • theme-CSS: wird als zusätzliches <link> nach dem Mandanten-Theme geladen und überschreibt dessen CSS-Variablen (z. B. --vc-accent, --vc-bg). CSS kann kein JavaScript ausführen; die Datei darf cross-origin liegen (kein CORS nötig). Werte für ?theme=/?bg= müssen URL-kodiert sein (z. B. %23 für #).
  • bg=transparent: die Elternseite scheint durch die Fläche um den Canvas (am sinnvollsten mit ?embed=kiosk). Der Video-Canvas selbst bleibt immer deckend – transparent wirkt nur auf den Rahmen/die Letterbox-Ränder.
  • Platzhalterfarben direkt in der URL (placeholderBg/placeholderText/placeholderBorder): färbt nur die Platzhalter-Box ein (das generierte Buchstaben-Motiv, solange kein eigenes Motiv gesetzt ist). Das ist der kurze Weg ohne eigene ?theme=-Datei. Akzeptiert jede CSS-Farbe (Hex %23ff5a10, rgb(...), hsl(...), Namen wie white) und überschreibt Mandanten-Theme und ?theme=; nur gesetzte Parameter überschreiben. Tipp: placeholderBg und placeholderText nicht gleich setzen (sonst unsichtbarer Buchstabe). Für mehr Branding (Akzent, Buttons, Spinner …) nutzen Sie ?theme=.

Beispiele

Code ansehen URL
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 picker off (own picker)
https://demo.kreativsimulator.com/?product=ec5472c2&view=0e23675447  # photo product in this view (views enabled only)
https://demo.kreativsimulator.com/?views=0                  # "Views (n)" button off (own picker, views enabled only)
https://demo.kreativsimulator.com/?selection=0              # selection buttons off (selection enabled only)
https://demo.kreativsimulator.com/?library=0                # "Catalog (n)" button off (product library enabled only)
https://demo.kreativsimulator.com/?hint=0                   # no tap hint (placeholders do not pulse)
https://demo.kreativsimulator.com/?editor=overlay           # edit placeholders in the editor on the image
https://demo.kreativsimulator.com/?editor=sidebar           # always the sidebar, also on phones
https://demo.kreativsimulator.com/?sel=1.eyJ…               # open a shared or saved selection (selection enabled only)
https://demo.kreativsimulator.com/?fullscreen=1             # fullscreen icon also on desktop
https://demo.kreativsimulator.com/?fit=1                    # own-window layout in the iframe (recommended with sidebar)
https://demo.kreativsimulator.com/?watermark=1              # every export gets the preview watermark
https://demo.kreativsimulator.com/?logo=0                   # exports without the tenant logo
https://demo.kreativsimulator.com/?stage=1280x720           # fixed 1280×720 canvas (16:9)
https://demo.kreativsimulator.com/?stage=1024x1024          # square canvas, 1024 px
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
Schritt 4

Responsive einbetten

Im Embed-Modus (Chrome aus, z. B. ?embed=kiosk oder ?sidebar=0) füllt der Canvas das iframe vollflächig und bleibt dabei mittig zentriert. Legen Sie das iframe also einfach mit width:100%; height:100% in einen beliebig dimensionierten Eltern-Container (auch Flexbox/Grid). Weicht dessen Seitenverhältnis von dem des Canvas ab, entsteht eine symmetrische Letterbox (Farbe per ?bg=).

Unsere Empfehlung ist ein iframe, das seinen Flex- oder Grid-Container ausfüllt:

Code ansehen HTML
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>

Wer ein exakt passendes Seitenverhältnis ohne Letterbox will, gibt dem Wrapper aspect-ratio: 16 / 9 – das passt für Film- und Clip-Produkte:

Code ansehen HTML
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>

Welche Form hat der Canvas? Die Stage

Die Form bestimmt die Stage (?stage=, Default auto): Film- und Clip-Produkte sind 16:9, Foto-Produkte haben das Seitenverhältnis des Fotos (z. B. 3:2 oder hochkant) – das ganze Foto ist sichtbar, nichts wird abgeschnitten. Die aktuelle Form meldet der Simulator im render-Block von ready und productChanged sowie im Event stageChanged (width, height, aspectRatio). Passen Sie damit Ihre iframe-Box an, dann gibt es keine Letterbox:

Code ansehen JavaScript
JavaScript
// Let the iframe box follow the canvas shape — no letterbox, no matter
// whether a 16:9 film or a portrait photo is loaded.
window.addEventListener("message", (e) => {
  const m = e.data;
  if (!m || m.source !== "kreativsimulator") return;
  // ready / productChanged carry `render`; stageChanged carries the fields itself.
  const r = m.type === "stageChanged" ? m : m.render;
  if (r) iframe.style.aspectRatio = r.width + " / " + r.height;
});

// Force ONE fixed shape for every product instead (e.g. square tiles):
// send({ type: "kreativsimulator:setStage", stage: "1024x1024" });
// send({ type: "kreativsimulator:setStage", stage: "auto" });   // back to per-product

Wollen Sie eine feste Form für alle Produkte (z. B. quadratische Kacheln), setzen Sie ?stage=1024x1024 oder senden setStage. Das Bild wird dann mittig in die Form eingepasst (Cover): Bei einem 16:9-Film im Quadrat gehen links und rechts je ca. 22 % des Bildes verloren – Platzhalter am Rand können aus dem Bild fallen. Prüfen Sie das pro Produkt.

Mit sichtbarer Sidebar (ohne ?embed/?sidebar=0) bleibt der Canvas im iframe links-bündig (max. 1280px). Die Voll- und Zentrier-Logik greift dort nur im Embed-Modus, damit bestehende Einbindungen gleich aussehen. Öffnet man den Simulator direkt als eigene Seite (z. B. per Link), passt sich der Canvas an Breite und Fensterhöhe an, und die Knopfleiste bricht bei wenig Platz sauber um. Dasselbe Layout im iframe gibt ?fit=1. Für Einbindungen mit sichtbarer Sidebar ist das die empfohlene Einstellung: Ohne fit kann die Sidebar den Canvas verdecken und die Abspielleiste unter den Rand rutschen, wenn das iframe nicht hoch genug ist. Knopfleiste: eine Zeile, als Ganzes mittig unter dem Bild, auf dem Handy wie am Desktop. Sie zeigt Abspielen (Stopp, ◀, ▶, ▶|), „Hinzufügen“ und Export. Alle Knöpfe sind 32 px hoch, Symbole haben einen Tooltip und ein aria-label. „Hinzufügen“ gibt es nur mit freigeschalteter Auswahl. Der Textknopf steht im Reiter „Produkte“ in der Hauptfarbe und behält beim Tippen seine Breite. Der Export-Knopf zeigt mit freigeschalteter Auswahl das Teilen-Symbol, sonst das Download-Symbol. Mit Auswahl steht im Reiter „Meine Auswahl“ darunter „Ganze Auswahl abspielen“ mit dem Musik-Knopf. Dieselbe Höhe bleibt im Reiter „Produkte“ frei, beim Wechsel der Reiter springt nichts.

Bedienung im iframe

  • Knöpfe auf dem Bild: „Ansichten (n)“ oben links (nur mit Ansichtsauswahl), die Effekt-Auswahl der Foto-Produkte unten links (sie öffnet einen Dialog mit der Liste), Vollbild oben rechts und darunter „1:1“, sobald gezoomt ist. Die Leiste unter dem Bild bleibt dadurch niedrig, und der Canvas im iframe wird größer. Ausblenden mit ?views=0 bzw. ?effects=0.
  • Kopfzeile mit Produkt-Bibliothek: „Katalog (n)“, „Produkte (n)“ und „Meine Auswahl“ stehen als Text. Auf dem Handy (bis 768 px) liegt oben eine Umschalt-Leiste „Produkte (n) | Meine Auswahl“ über die ganze Breite, rechts daneben ein runder Knopf in der Theme-Farbe, der die Seitenleiste öffnet. Links vor der Produkt-Leiste steht fest eine Kachel „Katalog“ (Lupe und Wort, gesetzte Filter als Zahl), im Reiter „Meine Auswahl“ an derselben Stelle „Leeren“ (Mülleimer und Wort, mit Rückfrage). Beide sind so hoch wie die Produkt-Kacheln. Produkte, die schon in der Auswahl liegen, tragen ein kleines ✓. Die Leiste scrollt weich mit, das aktive Produkt bleibt in der Mitte, auf Touch-Geräten ohne Scrollleiste. Eine leere Auswahl zeigt „Noch nichts in der Auswahl“ mit einem Hinweis auf „Hinzufügen“. Der Rand um Bild und Kopfzeile ist 8 px breit.
  • Editor auf dem Bild: Standard auf dem Handy, überall mit ?editor=overlay, nie mit ?editor=sidebar. Der Editor legt sich über die ganze iframe-Fläche und ist aufgebaut wie ein Foto-Editor. Das Bild füllt die Fläche und ist auf den Platzhalter gezoomt. Oben links steht „Motiv“ (bei mehreren Flächen: nur diese Fläche oder alle), oben rechts ✕ (verwerfen) und ✓ (übernehmen). Unten liegen die Funktionen als Symbole, darüber die Regler der gewählten Funktion als einklappbarer Streifen (Symbol, Schieber, ✓ fertig, an/aus und Zurücksetzen je Funktion). Ein Knopf „OK“ über den Reglern schließt sie. Ein Tipp auf eine leere Fläche öffnet die Motivauswahl, danach steht der Editor auf „Transformieren“. Eine belegte Fläche tauscht ihr Motiv über „Motiv“. Ein Tipp auf eine andere Fläche wechselt dorthin. Das Menü ⋯ bietet „Effekte zurücksetzen“, „Motiv auf alle Flächen“, „Effekte auf alle Flächen übernehmen“ und „Alles zurücksetzen“. Unten links zeigt „1:1“ das ganze Bild und „Fit“ die Fläche mittig. Zwei Finger oder das Mausrad zoomen, ein Doppeltipp springt auf „Fit“. Die Änderungen gelten für alle Szenen des Platzhalters.
  • Vollbild: Im Vollbild fehlt die Leiste. Unten rechts liegt dann ein runder Knopf für Abspielen und Pause, links auf gleicher Höhe die Effekt-Auswahl. Beide halten Abstand zum unteren Rand, damit Hinweise des Handys sie nicht verdecken.
  • Motiv wählen: Der Dialog zeigt das aktuelle Motiv („Motiv wechseln“), bei Videos mit In/Out, und hat ein Feld für einen Link zu Bild, Video oder Stream. Auf dem Handy nutzt er die volle Höhe.
  • Zoom im Bild: Zwei Finger zoomen ins Bild, am Desktop Strg und Mausrad oder das Trackpad. Der Knopf „1:1“ setzt zurück. Senkrechtes Scrollen mit einem Finger bewegt weiter Ihre Seite.
Schritt 5 · Der häufigste Fall

Nur der Canvas, die Bedienung bauen Sie selbst

Meist zeigen Sie nur den WebGL-Canvas und bauen die komplette Bedienung selbst. Dazu blenden Sie Header, Sidebar und Buttons aus, per URL mit ?embed=kiosk (Header + Sidebar + Export aus, Abspielleiste bleibt) oder gezielt mit ?header=0&sidebar=0&playback=0&export=0. Zur Laufzeit geht dasselbe per setChrome.

Wiedergabe und Export funktionieren immer per postMessage, auch wenn die eingebauten Buttons ausgeblendet sind. Ihre eigenen Buttons steuern also das iframe. Für einen eigenen Fortschrittsbalken lauschen Sie auf das playbackState-Event.

Ist die Seitenleiste ausgeblendet, öffnet ein Klick direkt auf eine Plakatfläche im Canvas den Datei-Dialog für genau diesen Platzhalter. So können Ihre Nutzer auch ohne eigenes Upload-Feld ein Motiv hochladen.

embed=kiosk versteckt das eingebaute Chrome – Ihre Buttons senden play/pause/seek/export. Probieren Sie es unten in der Live-Demo mit den „Chrome“-Schaltern und der eigenen Wiedergabeleiste aus.

Canvas im Kiosk-Modus + eigene Abspielsteuerung, synchron gehalten über das playbackState-Event:

Code ansehen HTML + JS
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="still">Save image</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 });
  // Still image (JPG) of the currently shown frame (photo products: the whole photo):
  still.onclick = () =>
    send({ type: P + "exportImage", 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>
Schritt 6 · Eigenes Design

Eigenes Design per CSS (theme & bg)

Das iframe übernimmt Ihr Branding über CSS-Variablen. ?theme= lädt eine eigene CSS-Datei nach dem Mandanten-Theme und überschreibt dessen Variablen (z. B. --vc-accent, --vc-bg). ?bg= setzt die Letterbox-Fläche um den Canvas (Farbe oder Bild-URL), ?stage= die Form und Größe des Canvas.

Einbettung mit Theme + Hintergrund

Code ansehen HTML
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-Vorlage

Vorlage herunterladen ks-theme.css
Vorlage ansehen kreativsimulator-theme-template.css
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; /* reserviert — wird derzeit von keinem UI-Element gelesen */
  --vc-accent-rgb: 223, 59, 0; /* = --vc-accent als R, G, B */
  --vc-accent-muted: #3a3a3a;  /* gedämpfter Akzent-Hintergrund */
  --vc-danger: #e5484d;        /* Löschen (Mülleimer in der Auswahl-Liste, „Auswahl leeren") */
  --vc-warning: #f5a524;       /* Warnung: leere Werbeflächen vor Export/Teilen */
  --vc-hint-color: #df3b00;    /* Tipp-Hinweis: Platzhalter pulsieren einmal (Default = --vc-accent) */

  /* ── Header (Kopfzeile mit Produktauswahl) ─────────────────────────────
     Nur relevant, wenn der Header sichtbar ist (sonst per ?header=0 ausblenden). */
  --vc-header-bg: transparent;     /* Hintergrund der Kopfzeile */
  --vc-header-text: #eeeeee;       /* Header-Text (aktives Produkt-Label, Menü-Icon) */
  --vc-header-text-muted: #888888; /* sekundärer Header-Text (inaktive Produkt-Labels) */
  --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 */

  /* ── 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 */
  /* Inaktiver Auswahl-Button (Platzhalter-/Szenen-Selector). Bewusst NICHT die
     Primärbutton-/Akzentfarbe — neutraler Ton, damit aktiv/inaktiv sichtbar trennt. */
  --vc-button-inactive-bg: #3a3a3a;   /* Fläche des nicht-aktiven Selector-Buttons */
  --vc-button-inactive-text: #eeeeee; /* dessen Textfarbe (auf der Fläche lesbar) */

  /* ── Slider / Zeitleiste ───────────────────────────────────────────────
     Die In/Out-Marken nutzen --vc-accent (s. o.); der Abspielkopf lässt sich
     darüber hinaus separat einfärben. */
  --vc-slider-track: #3a3a3a;    /* Track-Farbe der Regler */
  /* Abspielkopf (Linie + Griff) BEIDER Zeitleisten — Haupttimeline und
     Platzhalter-Mini-Leiste. Ohne diese Zeile folgt er --vc-accent. */
  --vc-playhead-color: #df3b00;
  /* Bereich außerhalb von In/Out in beiden Zeitleisten. Bei einem HELLEN Theme
     unbedingt beide Werte anpassen (s. Beispielblock unten) — sonst wirkt der
     inaktive Bereich dunkler als der aktive und die Timecodes verschwinden. */
  --vc-timeline-dim: rgba(0, 0, 0, 0.55);      /* Schleier außerhalb In/Out */
  --vc-timeline-dim-text: #ffffff;             /* Timecodes im Schleier */

  /* ── 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;            /* Abgerundete Canvas-Ecken (0px = eckig) */

  /* ── Lade-Spinner (während Hintergrund/Produkt lädt) ───────────────────
     Ohne diese Zeilen folgt der Spinner --vc-accent (Bogen) / --vc-border (Ring). */
  --vc-spinner-color: #df3b00; /* rotierender Bogen (Default: Akzent) */
  --vc-spinner-track: #2a2a2a; /* Ring/Spur (Default: Border) */
  --vc-spinner-size: 32px;     /* Durchmesser */

  /* ── 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 */

  /* ── Dialoge & Eingabefelder ───────────────────────────────────────────
     Export-Dialog („Export als MP4") sowie alle Input-/Select-Felder
     (Dateiname, Auflösung, Effekt-Dropdown). Ohne diese Zeilen folgt alles
     den Surface-/Text-Defaults oben. */
  --vc-dialog-bg: #181818;                /* Dialog-Fläche */
  /* --vc-dialog-border: seit 2026-09-28 ohne Wirkung (Dialoge haben Schatten statt Rahmen) */
  --vc-dialog-radius: 16px;               /* Eckenradius der Dialoge (Blatt von unten: +4px) */
  --vc-dialog-backdrop: rgba(0,0,0,0.78); /* Abdunklung hinter dem Dialog */
  /* --vc-slideshow-bg: #000;             Hintergrund der Slideshow-Seite (Default: --vc-bg) */
  --vc-input-bg: #181818;                 /* Fläche von Input/Select */
  --vc-input-border: #444444;             /* Rahmen von Input/Select */
  --vc-input-text: #eeeeee;               /* Textfarbe in Input/Select */
  --vc-input-radius: 4px;                 /* Eckenradius von Input/Select (Default = --vc-button-radius) */
  --vc-input-icon: #df3b00;               /* Chevron-Pfeil des Dropdowns (Default = --vc-accent) */
  --vc-input-focus: #df3b00;              /* Fokus-Ring + Hover-Rahmen von Input/Select (Default = --vc-accent) */
  --vc-input-selected-bg: #df3b00;        /* Aufgeklappte Liste: gewählter Eintrag (Default = --vc-accent) */
  --vc-input-selected-text: #ffffff;      /* … dessen Textfarbe (Default = --vc-text-on-accent) */
  --vc-input-option-hover: #3a3a3a;       /* Aufgeklappte Liste: Eintrag unter der Maus (Default = --vc-surface-hover) */
  --vc-label-text: #888888;               /* Beschriftung über den Feldern */
}

/* =============================================================================
   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-button-inactive-bg: #e8e8e8;
  --vc-button-inactive-text: #1a1a22;
  --vc-slider-track: #e0e0e0;
  --vc-timeline-dim: rgba(0, 0, 0, 0.10);
  --vc-timeline-dim-text: var(--vc-text-muted);
  --vc-canvas-outline-color: #0066ff;
  --vc-canvas-outline-width: 2px;
}
*/
Alle CSS-Variablen im Überblick 61

Sie müssen nicht alle Variablen setzen – nicht gesetzte Variablen behalten den Standard des Simulators/Mandanten. Farben akzeptieren jede gültige CSS-Farbe (#hex, rgb(), hsl(), benannt).

VariableWirkungDefault
--vc-color-schemeHell-/Dunkel-Schema (dark · light) – beeinflusst u. a. native Formular-Elementedark
--vc-font-familySchriftfamilie der gesamten Oberfläche (eigene Webfonts per @import oben in der Theme-CSS laden)system-ui, …
--vc-bgApp-Hintergrund / Canvas-Hintergrund beim Laden#0a0a0a
--vc-surfaceSidebar, Dialog-Flächen#181818
--vc-surface-raisederhöhte Elemente (Tracks, Chips, Inputs)#2a2a2a
--vc-surface-hoverHover-Fläche#3a3a3a
--vc-borderStandard-Rahmenfarbe#2a2a2a
--vc-border-strongbetonte Rahmen#444444
--vc-textStandard-Textfarbe#eeeeee
--vc-text-mutedsekundärer Text#888888
--vc-text-subtledezente Hinweise#666666
--vc-text-on-accentText auf Akzent-/Primärflächen#ffffff
--vc-accentMarken-Akzent: aktive Zustände, In/Out-Marken, Slider-PunkteMandanten-Theme
--vc-accent-rgbdieselbe Farbe als „R, G, B“ (ohne rgb()-Klammern) – für halbtransparente Hervorhebungen; muss zu --vc-accent passenMandanten-Theme
--vc-accent-strongreserviert – wird derzeit von keinem UI-Element gelesen–
--vc-accent-mutedgedämpfter Akzent-Hintergrund#3a3a3a
--vc-dangerLöschen: Mülleimer in der Auswahl-Liste, Kachel „Leeren“, Bestätigen in Lösch-Rückfragen (nur mit Auswahl)#e5484d
--vc-warningWarnung: Symbol für leere Werbeflächen vor Export und Teilen (nur mit Auswahl)#f5a524
--vc-hint-colorTipp-Hinweis: Platzhalter pulsieren einmal nach dem Ladenfolgt --vc-accent
--vc-header-bgHintergrund der Kopfzeiletransparent
--vc-header-textHeader-Text (aktives Produkt-Label, Menü-Icon)#eeeeee
--vc-header-text-mutedsekundärer Header-Text (inaktive Produkt-Labels)#888888
--vc-header-borderuntere Trennlinie der Kopfzeile#1a1a1a
--vc-header-border-widthStärke der Trennlinie (0px = keine)1px
--vc-header-paddingInnenabstand der Kopfzeile12px 20px
--vc-button-bgButton-Fläche#2a2a2a
--vc-button-bg-hoverButton-Fläche bei Hover#3a3a3a
--vc-button-textButton-Textfarbeinherit
--vc-button-borderButton-Rahmen#444444
--vc-button-radiusEckenradius der Buttons4px
--vc-button-inactive-bgFläche des nicht-aktiven Auswahl-Buttons (Platzhalter-/Szenen-Selector) – bewusst neutral statt Akzent, damit aktiv/inaktiv sichtbar trennt#3a3a3a
--vc-button-inactive-textTextfarbe des nicht-aktiven Auswahl-Buttons#eeeeee
--vc-slider-trackTrack-Farbe der Regler#3a3a3a
--vc-playhead-colorAbspielkopf (Linie + Griff) beider Zeitleisten – Haupttimeline + Platzhalter-Mini-Leistefolgt --vc-accent
--vc-timeline-dimSchleier außerhalb der In/Out-Marken – bei hellen Themes anpassen (s. Hinweis)rgba(0, 0, 0, 0.55)
--vc-timeline-dim-textTimecodes im Schleier – bei hellen Themes anpassen (s. Hinweis)#ffffff
--vc-canvas-outline-colorOutline-Farbe des Vorschau-Canvas#2a2a2a
--vc-canvas-outline-widthOutline-Stärke (0px = kein Rahmen)1px
--vc-canvas-radiusabgerundete Canvas-Ecken (Video, Lade-Fläche und Rahmen einheitlich)0px (eckig)
--vc-spinner-colorLade-Spinner: rotierender Bogenfolgt --vc-accent
--vc-spinner-trackLade-Spinner: Ring/Spurfolgt --vc-border
--vc-spinner-sizeLade-Spinner: Durchmesser32px
--vc-frame-coloroptionaler Außenrahmen um die ganze Fläche: Farbetransparent
--vc-frame-widthoptionaler Außenrahmen: Stärke (0px = aus)0px
--vc-placeholder-bgHintergrund der Platzhalter-Box (generierte „A/B“-Textur)#ffffff
--vc-placeholder-textBuchstabe/Beschriftung der Platzhalter-Box#000000
--vc-placeholder-borderRahmen der Platzhalter-Box#000000
--vc-dialog-bgFläche der Dialoge (Export, Auswahl, Katalog …)folgt Surface
--vc-dialog-radiusEckenradius der Dialoge (Blatt von unten: +4px)16px
--vc-dialog-backdropAbdunklung hinter dem Dialog (leicht unscharf)rgba(0,0,0,0.78)
--vc-slideshow-bgHintergrund der Slideshow-Seite (nur mit freigeschalteter Slideshow)folgt --vc-bg
--vc-input-bgFläche von Input/Select (inkl. Effekt-Dropdown bei Foto-Produkten)folgt Surface
--vc-input-borderRahmen von Input/Selectfolgt Border
--vc-input-textTextfarbe in Input/Selectfolgt --vc-text
--vc-input-radiusEckenradius von Input/Selectfolgt --vc-button-radius (4px)
--vc-input-iconChevron-Pfeil des Dropdownsfolgt --vc-accent
--vc-input-focusFokus-Ring + Hover-Rahmen von Input/Selectfolgt --vc-accent
--vc-input-selected-bgaufgeklappte Liste: Hintergrund des gewählten Eintragsfolgt --vc-accent
--vc-input-selected-textaufgeklappte Liste: Textfarbe des gewählten Eintragsfolgt --vc-text-on-accent
--vc-input-option-hoveraufgeklappte Liste: Eintrag unter der Mausfolgt --vc-surface-hover
--vc-label-textBeschriftung über den Feldernfolgt --vc-text-muted
  • Kein CORS nötig: Die CSS-Datei wird als <link> geladen (nicht per fetch wie Motive) und darf cross-origin liegen. CSS kann kein JavaScript ausführen.
  • URL-kodieren: Werte für ?theme=/?bg= müssen URL-kodiert sein (z. B. %23 für #, %2F für /).
  • Auch der Lade-Spinner ist themebar: --vc-spinner-color (rotierender Bogen), --vc-spinner-track (Ring) und --vc-spinner-size (Durchmesser) – ohne Override folgt er automatisch --vc-accent bzw. --vc-border.
  • Tipp-Hinweis: --vc-hint-color färbt das einmalige Pulsieren der Platzhalter nach dem Laden. Ohne Override folgt es --vc-accent.
  • Lösch- und Warnfarbe (nur mit Auswahl-Freischaltung): --vc-danger (Default #e5484d) färbt den Mülleimer in der Auswahl-Liste, die Kachel „Leeren“ und das Bestätigen in Lösch-Rückfragen. --vc-warning (Default #f5a524) färbt das Warn-Symbol für leere Werbeflächen in der Auswahl-Liste und den Hinweis im Reiter „Aktuelles Produkt“.
  • Abgerundete Canvas-Ecken: --vc-canvas-radius (Default 0px) rundet den Vorschau-Canvas ab, also Video, Lade-Fläche und Rahmen einheitlich. Zum Beispiel mit --vc-canvas-radius: 16px; in der ?theme=-CSS.
  • Abspielkopf separat einfärben: --vc-playhead-color färbt den Abspielkopf beider Zeitleisten (Haupttimeline + Platzhalter-Mini-Leiste) unabhängig vom Akzent; ohne Override folgt er --vc-accent. Eigene CSS-Regeln auf die Klasse vc-main-playhead greifen nicht (die Elemente sind inline gestylt) – nutzen Sie die Variable.
  • ⚠️ Zeitleisten-Abdunklung bei hellen Themes unbedingt anpassen: Der Bereich außerhalb der In/Out-Marken wird über --vc-timeline-dim (Schleier) und --vc-timeline-dim-text (Timecodes darin) abgedunkelt. Die Defaults sind für dunkle Themes ausgelegt (rgba(0,0,0,0.55) + Weiß). Bei einem hellen Theme beide überschreiben – bewährtes Paar: --vc-timeline-dim: rgba(0, 0, 0, 0.10); und --vc-timeline-dim-text: var(--vc-text-muted);.
  • Dialoge & Eingabefelder themebar: Die Dialoge (--vc-dialog-bg, --vc-dialog-radius, Default 16px, --vc-dialog-backdrop = leicht unscharfe Abdunklung dahinter; statt eines Rahmens tragen sie einen weichen Schatten) und alle Input-/Select-Felder inkl. des Effekt-Dropdowns bei Foto-Produkten (--vc-input-bg, --vc-input-border, --vc-input-text, --vc-input-radius, --vc-input-icon = Pfeil des Dropdowns, --vc-input-focus = Fokus-Ring, --vc-input-selected-bg/--vc-input-selected-text = gewählter Eintrag in der aufgeklappten Liste, --vc-input-option-hover = Eintrag unter der Maus, --vc-label-text) lassen sich gezielt stylen – ohne Override folgen sie den Surface-/Akzent-Variablen; der Eckenradius folgt --vc-button-radius.
  • Nur Platzhalterfarben? Dafür brauchen Sie keine Theme-Datei: ?placeholderBg=/?placeholderText=/?placeholderBorder= setzen die Platzhalter-Box direkt per URL (siehe URL-Parameter oben) und überschreiben Theme + ?theme=.
  • Alle überschreibbaren CSS-Variablen finden Sie in der Tabelle und der Theme-Vorlage unten.
Schritt 7 · Interaktiv

Fernsteuerung live ausprobieren

Links sehen Sie das echte iframe, rechts eine eigene Bedienoberfläche mit Produktbildern, Motiven und Buttons, so wie Sie sie auf Ihrer Seite bauen würden. Jeder Klick schickt eine postMessage an das iframe. Den Code dazu finden Sie direkt darunter.

Echtes iframe von demo.kreativsimulator.com In neuem Tab öffnen

Chrome ein-/ausblenden

Header/Sidebar/Buttons live verstecken → setChrome. „Nur Canvas“ blendet alles aus.

Eigene Wiedergabe

Funktioniert auch ohne sichtbare Buttons. Scrubber via seek, synchron über playbackState.

Eigene Produktauswahl

Diese Karten baut Ihre Seite aus dem ready-Event. Klick → setProduct. Foto-Produkte zeigen per getEffects (vorab, ohne Produktwechsel) ihre Effektzahl.

Eigene Motive

Ihre eigenen Platzhalter-Motive. Klick → setCreative mit der Bild-URL.

Schickt einen öffentlichen HLS-Livestream (.m3u8) per setCreative. Antwort im Log: creativeLoaded mit live: true.

Eigene Datei je Platzhalter

Bild auf A oder B ziehen → setCreative mit loader. In Produktion legen Sie die Datei auf einen öffentlichen Host und übergeben die URL; hier zur Demo als Data-URL.

Export

Auflösung aus ready.render.presets. Klick → export (MP4).

Aus = logo: false an export/exportImage – das Mandanten-Logo oben links fehlt dann im MP4 bzw. JPG.

Lade-Varianten (URL-Parameter)

Lädt das iframe mit Parametern neu – so wirken embed/bg/stage.

Platzhalterfarben (URL)

Färbt nur die Platzhalter-Box ein, direkt per URL und ohne eigene ?theme=-Datei. „Anwenden“ lädt das iframe mit ?placeholderBg/Text/Border neu.

Sprache

Event-Log (iframe → Ihre Seite)

  • Noch keine Events empfangen …

Mit diesem Code läuft die Live-Demo oben. Nehmen Sie ihn gern als Ausgangspunkt:

Code ansehen HTML + JS
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>
Schritt 8 · Referenz

postMessage-API

Bauen Sie eine eigene UI und steuern Sie das iframe per window.postMessage. Das iframe sendet außerdem Events zurück (u. a. die Liste der verfügbaren Produkt-Codes).

Eingehende Befehle (Ihre Seite → iframe)

Produkt, Stage und Ansicht

  • { type: "kreativsimulator:setProduct", code: "ec5472c2", stage? }

    Lädt das Produkt mit diesem Code. Optional setzt stage ("auto", "1024x1024" oder { width, height }) gleichzeitig die Canvas-Form; ohne stage bleibt die aktuelle erhalten. Mit Produkt-Bibliothek funktioniert jeder Code Ihres Katalogs, auch einer, der nicht in ready.products steht.

  • { type: "kreativsimulator:setStage", stage: "auto" | "BxH" | { width, height } }

    Setzt die Stage (Form und Größe des Canvas) zur Laufzeit. "auto" = Film/Clip 16:9, Foto im eigenen Seitenverhältnis; BxH = feste Form + Anzeigegröße für alle Produkte. Antwort: stageChanged. Die Stage bleibt über Produktwechsel hinweg, bis sie erneut gesetzt wird.

  • { type: "kreativsimulator:setEffect", id: "static-5s" }

    Foto-Produkte: wählt einen Eintrag aus ready.effects – ein Statisch-Preset (static-5s/static-10s, immer vorhanden) oder einen benannten Ken-Burns-Effekt. id: null = reines Standbild (1 Frame). Ändert die Timeline-Länge → es folgt ein playbackState.

  • { type: "kreativsimulator:setView", view: "0e23675447" }

    Nur bei freigeschalteter Ansichtsauswahl (sonst wirkungslos): zeigt das aktuelle Foto-Produkt in dieser Ansicht (Key aus ready.views/productChanged.views). view: null = Standard-Ansicht. Die Motive bleiben erhalten. Antwort: viewChanged.

  • { type: "kreativsimulator:getEffects", code: "ec5472c2" }

    Fragt die Effektliste eines beliebigen Produkts ab, ohne es zu aktivieren (Live-Ansicht bleibt unverändert) → Antwort als effects-Event. Für einen eigenen Effekt-Picker je Produkt – noch vor setProduct.

Motive

  • { type: "kreativsimulator:setCreative", url: "https://…/motiv.png" }

    Legt das Bild/Video (jede öffentliche http(s)-URL) als Motiv auf alle Platzhalter.

  • { type: "kreativsimulator:setCreative", url: "…", loader: "Loader A" }

    Belegt nur den genannten Platzhalter.

  • { type: "kreativsimulator:setCreative", url: "https://…/live.m3u8", kind?: "stream" }

    Legt einen Video-Stream als Motiv auf – HLS (.m3u8) oder DASH (.mpd), live oder auf Abruf. kind ("image" | "video" | "stream") ist optional: ohne erkennt der Simulator die Art an Endung bzw. Content-Type. Antwort: creativeLoaded.

  • { type: "kreativsimulator:clearCreative", loader?: "Loader A" }

    Setzt einen (oder ohne loader alle) Platzhalter auf den Default zurück.

Wiedergabe

  • { type: "kreativsimulator:play" } · pause · stop

    Startet / pausiert / stoppt (zurück an den Anfang) die Wiedergabe.

  • { type: "kreativsimulator:stepForward", frames?: 1 } · stepBackward

    Springt frames Bilder vor/zurück (Default 1) und pausiert.

  • { type: "kreativsimulator:seek", frame: 120 }

    Springt zu einem absoluten Bild-Index (0…frameCount-1).

  • { type: "kreativsimulator:getState" }

    Fordert den aktuellen Wiedergabe-Zustand an → Antwort als playbackState (bei freigeschalteter Ansichtsauswahl mit view).

Export

  • { type: "kreativsimulator:export", filename?, width?, height?, watermark?, logo? }

    Startet den MP4-Export. Mit width+height direkt in beliebiger Auflösung; ohne öffnet sich der Export-Dialog. watermark: true schaltet das „Vorschau“-Wasserzeichen zu, logo: false lässt das Mandanten-Logo weg (siehe Hinweise).

  • { type: "kreativsimulator:exportImage", filename?, width?, height?, watermark?, logo?, original? }

    Speichert ein Einzelbild als JPG (direkt, ohne Dialog). Video-Produkte: der aktuell angezeigte Frame in width×height (Default 1280×720, 16:9). Foto-Produkte: das ganze Foto (kein 16:9-Ausschnitt, kein Ken-Burns) mit den aufgelegten Motiven im Seitenverhältnis des Fotos, lange Seite 1280 px (quer z. B. 1280×961, hochkant 720×1280). Mit original: true und freigeschaltetem Foto-Export in Originalgröße in der vollen Auflösung des Fotos (höchstens 8192 px, im Dialog als Auflösung „Original“ wählbar). width/height werden bei Fotos ignoriert. watermark und logo wie bei export.

Oberfläche und Sprache

  • { type: "kreativsimulator:setLanguage", lang: "fr" }

    Schaltet die UI-Sprache (de/en/fr/it).

  • { type: "kreativsimulator:setChrome", header?, sidebar?, playback?, timeline?, export?, effects?, views?, selection?, library?, hint?, editor? }

    Blendet einzelne UI-Bereiche zur Laufzeit ein/aus (jeweils true/false). effects = Effekt-Auswahl (Foto-Produkte, Knopf auf dem Bild), views = Knopf „Ansichten (n)“ auf dem Bild (nur bei freigeschalteter Ansichtsauswahl), selection = Auswahl-Knöpfe (nur bei freigeschalteter Auswahl), library = Knopf „Katalog (n)“ (nur bei freigeschalteter Produkt-Bibliothek). hint = Tipp-Hinweis, editor = "auto", "sidebar" oder "overlay" (wohin ein Tipp auf den Platzhalter führt). Diese beiden stehen nicht in chromeChanged.

  • { type: "kreativsimulator:fullscreen", enabled? }

    Vollbild: true=ein, false=aus, ohne enabled=umschalten (siehe Hinweis).

Zugang (nur mit Token)

  • { type: "kreativsimulator:setAccessToken", token: "vcke_…" }

    Nur bei „Nur mit Token“ (Schritt 9): reicht ein neues Embed-Token nach, z. B. nach accessRequired oder nach einem Login. Die Produkte laden danach ohne Neuladen. Ungültig oder schon benutzt → error mit code: "ACCESS_TOKEN_INVALID".

Code ansehen JS
JS
// Save a specific frame of a video product as JPG: pause/seek first, then exportImage.
send({ type: "kreativsimulator:seek", frame: 120 });
send({ type: "kreativsimulator:exportImage", filename: "poster", width: 1920, height: 1080 });

// Export WITHOUT the tenant logo (top left): the field wins over the URL.
// ?logo=0 on the iframe URL switches it off for every export of this embed.
send({ type: "kreativsimulator:export", filename: "creation", width: 1920, height: 1080, logo: false });
send({ type: "kreativsimulator:exportImage", filename: "poster", logo: false });

Ausgehende Events (iframe → Ihre Seite)

Filtern Sie eingehende Nachrichten immer auf source === "kreativsimulator". v ist die Protokoll-Version (aktuell 1). Neue Nachrichtentypen und Felder kommen dazu, ohne dass v steigt. Ignorieren Sie deshalb Typen und Felder, die Sie nicht kennen, und prüfen Sie nicht auf exakte Objektformen. Eine neue Version (v: 2) gibt es nur bei einer inkompatiblen Änderung, und dann parallel zu v: 1.

Code ansehen JS
JS
{ source: "kreativsimulator", v: 1, type: "ready",
  product: "ec5472c2", locale: "de",
  products: [{ code: "ec5472c2", name: "City-Light …" }, …],
  // product library only: products = the first 30, the total count is here
  productsTotal: 240,
  loaders: [{ name: "Loader A", aspectRatio: 2.96 }, …],
  // shape of the current stage (backing: long side 1280), stage mode and
  // export presets relative to that shape (short side 360/720/1080)
  render: { width: 1280, height: 720, aspectRatio: 1.7778, stage: "auto",
            presets: [{ label: "Small (640×360)", width: 640, height: 360 },
                      { label: "Standard (1280×720)", width: 1280, height: 720 },
                      { label: "Large (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 }, …],
  // the shape can change with the product (a photo is not 16:9) → resize your iframe box
  render: { width: 1280, height: 854, aspectRatio: 1.4988, stage: "auto", presets: [ … ] },
  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.
// views / selection / library are added only when the view picker, the selection or the library is enabled.
{ 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 }

// the stage (canvas shape) changed: after setStage, setProduct{stage} or a product
// switch with a different aspect ratio. Same fields as the render block.
{ source: "kreativsimulator", v: 1, type: "stageChanged",
  width: 1280, height: 1280, aspectRatio: 1, stage: "explicit", presets: [ … ] }

// confirmation after a successful setCreative. kind = detected type,
// live: true = live stream (not seekable).
{ source: "kreativsimulator", v: 1, type: "creativeLoaded",
  url: "https://…/live.m3u8", loader: "Loader A", kind: "stream", live: true }

{ source: "kreativsimulator", v: 1, type: "error", message: "…", code, loader }

// views enabled only: the shown view changed (end customer in the iframe, setView).
// fallback: true = the requested view does not exist (any more) → default view shown.
// Pass `view` as `impressionKey` when you order.
{ source: "kreativsimulator", v: 1, type: "viewChanged",
  code: "ec5472c2", view: "0e23675447", fallback: false }

// selection enabled only: its content after every change and after it is restored
// from the browser. param = the full state as the value for ?sel= ("" = empty).
{ source: "kreativsimulator", v: 1, type: "selectionChanged", count: 2,
  items: [{ code: "ec5472c2", view: null, name: "City-Light …", ready: true }, …],
  param: "1.eyJ…" }

// share link created (copied = on the clipboard). param = the value for ?sel=.
{ source: "kreativsimulator", v: 1, type: "selectionLinkCopied",
  url: "https://demo.kreativsimulator.com/?sel=1.eyJ…", param: "1.eyJ…", copied: true }

// slideshow created (slideshow enabled only).
{ source: "kreativsimulator", v: 1, type: "slideshowCreated",
  url: "https://demo.kreativsimulator.com/s/Abc…", expiresAt: "2026-10-03T12:00:00.000Z" }

// "Token only" access: products are missing because of the access.
// "missing" = no token, "expired" = token/session expired or already used,
// "denied" = no access. Answer: a new token via setAccessToken.
{ source: "kreativsimulator", v: 1, type: "accessRequired", reason: "expired" }

Ansichten (Perspektiven eines Foto-Produkts)

Manche Foto-Produkte sind aus mehreren Perspektiven fotografiert, zum Beispiel Totale und Nahaufnahme desselben Werbeträgers. Ist die Ansichtsauswahl für Ihren Mandanten freigeschaltet (bitte bei uns anfragen), sieht der Endkunde bei solchen Produkten oben links auf dem Bild den Knopf „Ansichten (n)“. Ein Klick öffnet einen Dialog mit Vorschaubildern und Namen, die Bilder laden erst dann. Auf Handy und Touch-Geräten öffnet er im Vollbild wie der Produktkatalog. Die gewählte Ansicht wird geladen, seine Motive bleiben dabei erhalten. Bei wenig Platz zeigt der Knopf nur ein Ebenen-Symbol mit der Anzahl. Ohne Freischaltung ändert sich nichts.

Im Demo-Mandanten ist die Ansichtsauswahl an. In der Live-Demo oben erscheint der Block „Ansichten“, sobald ein Foto-Produkt mit mehreren Perspektiven aktiv ist.

Mit Freischaltung tragen ready und productChanged zusätzlich die Felder views und view:

Code ansehen JS
JS
// ready and productChanged carry two extra fields when views are enabled:
views: [{ key: "6c02edffc6", name: "Wide shot", isDefault: true,
          visibleLoaders: ["Loader A"],            // placeholders visible in this view
          previewUrl: "/api/vc/v1/products/ec5472c2/impressions/6c02edffc6/creative?w=320&kind=preview" },
        { key: "0e23675447", name: null, isDefault: false, … }],
view: "6c02edffc6"                                  // the view shown now

// Products without views (film, classic photo products): views: [], view: null.

// Your own picker: hide the "Views (n)" button (?views=0), show the previews, switch:
send({ type: "kreativsimulator:setChrome", views: false });
const img = new URL(v.previewUrl, iframe.src).href;  // previewUrl is relative to the simulator
send({ type: "kreativsimulator:setView", view: "0e23675447" });  // view: null = default view
  • Jede Ansicht hat eigene Einstellungen: Effekte (Farbe, Blur, Glow, Spiegelung, Rahmen), Ausschnitt und Ken-Burns-Effekte pflegt VirtualCampaign pro Ansicht, zum Beispiel für Tag, Nacht oder Winter. Beim Wechsel der Ansicht lädt der Simulator deren Werte. Motive und ihre Platzierung bleiben. Ändert der Endkunde Effekte, merkt sich der Simulator das pro Ansicht. effects in ready/productChanged gehört darum zur gezeigten Ansicht.
  • Produkte ohne Ansichten (Film, klassische Foto-Produkte) melden views: [] und view: null.
  • Bestellen: Geben Sie die gezeigte Ansicht beim Auftrag an die VirtualCampaign-API als impressionKey mit (POST /v1/orders). Sie steht in view aus ready/productChanged, im letzten viewChanged oder in getState. Ohne impressionKey rendert die Standard-Ansicht.
  • previewUrl kann ein &v=… tragen: die Version des Vorschaubilds. Nach einer Anpassung der Ansicht in VirtualCampaign ändert sie sich. Übernehmen Sie die URL deshalb unverändert.
  • Eigener Picker: den Knopf „Ansichten (n)“ mit ?views=0 bzw. setChrome { views: false } ausblenden, die Vorschaubilder aus previewUrl zeigen und per setView wechseln.

Motive aus der VirtualCampaign-Assetverwaltung

Assets, die im VirtualCampaign-Konto Ihres Mandanten hochgeladen wurden, lassen sich direkt als Motiv verwenden – ohne sie erneut irgendwo zu hosten und ohne CORS-Konfiguration auf Ihrer Seite.

Die Adressen bekommen Sie fertig: In der VirtualCampaign-Weboberfläche die Asset-Vorschau öffnen und auf das Ketten-Symbol klicken – das legt alle Adressen des Motivs samt Größenangaben in die Zwischenablage, direkt zum Einfügen in eine E-Mail.

Die Adressen sehen so aus:

Code ansehen URL
URL
https://virtualcampaign-prod.nbg1.your-objectstorage.com/assets-public/<token>/web_720.webp
Code ansehen JS
JS
// A published asset from the VirtualCampaign asset management — same host as
// the simulator, so no CORS setup on your side. Pick the web_* variant you need.
send({
  type: "kreativsimulator:setCreative",
  url: "https://virtualcampaign-prod.nbg1.your-objectstorage.com/assets-public/7f3a…/web_720.webp",
  loader: "Loader A",
});
VarianteGrößeWann
web_720.mp4 · web_720.webpbis 1280 × 720Standard – beste Qualität
web_640.mp4 · web_640.webpbis 1138 × 640Kompromiss
web_320.mp4 · web_320.webpbis 570 × 320schmale Leitung, viele Motive, Mobil
  • Endung nach Asset-Typ: Video-Assets liefern .mp4, Bild-Assets .webp. Bild-Varianten behalten einen transparenten Hintergrund (freigestellte Motive), was bei JPEG verloren ginge.
  • Die Adressen lassen sich im Browser öffnen – praktisch zum Prüfen, bevor Sie sie einbauen. Sie sind nicht erratbar: der Token in der Adresse ist das Geheimnis. Behandeln Sie sie so vertraulich wie das Motiv selbst.
  • Freigabe zurücknehmen: „Freigabe aufheben“ im selben Menü löscht die veröffentlichten Dateien; die Adressen liefern danach einen Fehler. Wird dasselbe Motiv später erneut freigegeben, entstehen dieselben Adressen – bestehende Einbettungen funktionieren dann wieder.
  • Warum drei Größen? Der Simulator lädt ein Bild- oder Video-Motiv vollständig herunter, bevor es erscheint (nur HLS-/DASH-Streams laufen segmentweise). Dateigröße = Wartezeit. Ein Platzhalter ist im Video meist nur ein Bildausschnitt – oft genügt die kleine Variante. Im Zweifel web_320 für die Live-Vorschau, web_720 erst für den Export.
  • Seitenverhältnis bleibt erhalten: Die Varianten werden proportional in die Box eingepasst, nie verzerrt, nie hochskaliert (hochkant z. B. 404 × 720, ein 2,96:1-Billboard-Motiv 1280 × 432). Kleinere Quelldateien bleiben unverändert.
  • Nur neue Uploads: Die Varianten entstehen beim Hochladen. Ältere Assets zeigen das Ketten-Symbol nicht – einmal neu hochladen.
  • Die Original-Datei ist bewusst nicht abrufbar (kann z. B. ProRes in einer .mov und bis zu 500 MB sein). Nutzen Sie immer eine web_*-Variante.

Beispiel: Livestream als Motiv

Ein Live-Stream (HLS .m3u8 oder DASH .mpd) läuft direkt auf der Plakatfläche. So zeigen Sie zum Beispiel ein DOOH-Programm oder eine Live-Übertragung am Standort. Die URL im Beispiel ist ein öffentlicher Test-Stream, den Sie in der Live-Demo oben mit „Livestream senden“ ausprobieren können.

Code ansehen JS
JS
// A live stream as the creative: HLS (.m3u8) or DASH (.mpd), live or on demand.
// Any public http(s) URL works — no CORS needed, the simulator falls back to its
// own media proxy. Public HLS live test stream (Unified Streaming):
send({
  type: "kreativsimulator:setCreative",
  url: "https://demo.unified-streaming.com/k8s/live/stable/scte35.isml/.m3u8",
  loader: "Loader A", // optional — without it, all placeholders
  // kind: "stream",  // only needed if the URL has no .m3u8 / .mpd ending
});

window.addEventListener("message", (e) => {
  const m = e.data;
  if (!m || m.source !== "kreativsimulator") return;
  if (m.type === "creativeLoaded") console.log(m.kind, m.live ? "LIVE" : "VOD"); // "stream" LIVE
  if (m.type === "error") console.warn(m.message); // e.g. "hls …" if the stream fails
});

Vollständiges Beispiel

Eigene Buttons aus der Produktliste, ein platzhalter-gezieltes Motiv (korrekte Reihenfolge) und ein eigener Auflösungs-Picker aus render.presets:

Code ansehen HTML + JS
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>

Wichtig

Reihenfolge bei Produktwechsel

Nach setProduct ist das neue Produkt erst aktiv, sobald das productChanged-Event eintrifft. Platzhalter-gezielte Befehle (setCreative/clearCreative mit loader) erst danach senden – vorher kennt das iframe nur die Platzhalter des alten Produkts. Ein Produktwechsel setzt zudem alle Motive zurück.

Eigene Bedienelemente

Wiedergabe (play/pause/stop/step…/seek), export und exportImage funktionieren immer – egal ob die eingebauten Buttons sichtbar sind. Blenden Sie sie mit ?embed=kiosk aus und steuern Sie alles selbst.

Export-Auflösung frei wählen

export mit width+height rendert in beliebiger Auflösung. Die aktuelle Render-Form (width/height/aspectRatio der Stage) und drei passende Presets („Klein/Standard/Groß“, kurze Seite 360/720/1080 px) liefert das Feld render in ready, productChanged und stageChanged – damit bauen Sie einen eigenen Auflösungs-Picker. Tipp: render.aspectRatio beibehalten, sonst entstehen Balken.

Stage = Form des Canvas

setStage (oder ?stage=) legt fest, welche Form der Canvas hat. "auto" heißt Film/Clip 16:9 und Foto im eigenen Seitenverhältnis, "1024x1024" erzwingt eine feste Form für alle Produkte. Jede Änderung meldet ein stageChanged-Event mit width/height/aspectRatio. Setzen Sie damit die aspect-ratio Ihrer iframe-Box, dann bleibt keine Letterbox übrig.

Wiedergabe-Modus von Video-Motiven

Ein Video-Motiv läuft standardmäßig in Echtzeit (unabhängig von der Produkt-Timeline). In der Seitenleiste kann der Nutzer je Platzhalter auf Synchroner Start (Motiv startet neu, sobald der Platzhalter erscheint) oder Standbild umschalten, dazu ein eigener Loop-Schalter. Der MP4-Export folgt dem gewählten Modus. Dafür gibt es derzeit keinen postMessage-Befehl – mit ausgeblendeter Seitenleiste (?sidebar=0/?embed=kiosk) bleibt es bei Echtzeit + Loop. Sprechen Sie uns an, wenn Sie das fernsteuern möchten.

Einzelbild speichern (JPG)

Neben dem Video lässt sich bei jedem Produkt ein Standbild speichern – im eingebauten Export-Dialog über die Knöpfe „Video“ und „Bild“ mit frei wählbarem Dateinamen und dem Knopf „Herunterladen“, oder per exportImage-Nachricht. Video-Produkte: der aktuell angezeigte Frame (Playhead) inkl. Motive in der gewählten Auflösung (Default 1280×720). Foto-Produkte: das ganze Foto mit Motiven im Seitenverhältnis des Fotos, lange Seite 1280 px. Im Dialog ist auch hier die Größe wählbar: Klein, Standard und mit Freischaltung „Original“ (volle Auflösung, höchstens 8192 px). Für einen bestimmten Frame erst pause/seek senden, dann exportImage. Logo-Regeln (?logo, Feld logo) und Wasserzeichen-Regeln gelten wie beim MP4.

Logo im Export abschalten

Standardmäßig trägt jedes exportierte MP4 und JPG das Mandanten-Logo oben links. Für manche Mandanten ist „ohne Logo“ als Standard eingestellt (sprechen Sie uns an, wenn Sie das möchten). Zwei Wege: ?logo=0 in der iframe-URL lässt es in allen Exporten dieser Einbettung weg – auch im eingebauten Export-Dialog. Das Feld logo an export/exportImage gilt nur für diesen Export (false = ohne, true = mit, auch wenn ?logo=0 gesetzt ist; weggelassen = Wert aus der URL, sonst der Standard des Mandanten). export ohne width/height öffnet den Dialog. Dort gilt nur ?logo, das Feld der Nachricht nicht. Probieren Sie es in der Live-Demo oben mit dem Schalter „Logo im Export“ aus.

Wasserzeichen zuschalten

export/exportImage mit watermark: true komponiert das diagonal gekachelte „Vorschau“-Wasserzeichen ins exportierte MP4 bzw. JPG – z. B. für unverbindliche Voransichten. Das Flag ist nur zuschaltend: false (oder weggelassen) ergibt das normale Verhalten; beim Demo-Mandanten ist das Wasserzeichen immer an und nicht abschaltbar. Wer alle Exporte einer Einbettung markieren will (auch die aus dem eingebauten Export-Dialog), nutzt stattdessen den URL-Parameter ?watermark=1.

Foto-Produkte & Ken-Burns-Effekte

Manche Produkte sind Standbilder mit benannten Kamerafahrten. ready/productChanged melden dann isPhoto: true und eine Liste effects ({ id, name, durationFrames }). Diese beginnt immer mit zwei Statisch-Presets (static-5s = 5 s, static-10s = 10 s), gefolgt von den benannten Effekten; beim Laden ist automatisch static-5s aktiv (selectedEffect). Mit setEffect { id } wählen Sie einen anderen Eintrag (oder id: null für ein reines Standbild, 1 Frame). Wiedergabe und Export funktionieren wie bei Video; playbackState.frameCount entspricht der Dauer des aktiven Eintrags.

Effekte vorab abfragen (ohne Produktwechsel)

ready/productChanged liefern Effekte nur für das aktive Produkt. Für einen Effekt-Picker zu anderen Produkten, ohne die Live-Ansicht umzuschalten, senden Sie getEffects { code } und lauschen auf das effects-Event (gleiche Felder wie productChanged, plus code). So holen Sie id/name jedes Effekts, die Sie später mit setEffect (nach setProduct auf diesen Code) ansteuern.

Vollbild zuverlässig einschalten

Browser erlauben das Einschalten nur als direkte Reaktion auf eine Nutzeraktion. Eine fullscreen-postMessage erfüllt das meist nicht und kann blockiert werden. Rufen Sie daher auf Ihrer Seite beim Klick iframe.requestFullscreen() auf (iframe braucht allow="fullscreen"); die postMessage eignet sich für Aus/Toggle. Den Zustand liefert fullscreenChanged.

Platzhalter-Namen

Sind nicht frei wählbar – verwenden Sie exakt einen name aus dem loaders[]-Array von ready bzw. productChanged.

CORS ist nicht nötig

Das iframe lädt die URL zuerst direkt. Sendet Ihr Host kein Access-Control-Allow-Origin, lädt der Simulator die Datei bzw. den Stream automatisch über seinen eigenen Medien-Proxy. Mit CORS geht es etwas schneller. Der Proxy lädt nur öffentliche Adressen (Ports 80/443, keine IP-Adressen oder internen Namen) und reicht keine Cookies oder Zugangsdaten weiter – URLs mit Anmeldung funktionieren nicht. Erlaubt sind http(s)- und data:-URLs.

Video-Streams (HLS/DASH)

setCreative nimmt auch .m3u8- und .mpd-Adressen an. Streams auf Abruf verhalten sich wie Video-Dateien. Live-Streams laufen immer in Echtzeit, ohne In/Out, Synchron-Start, Standbild und Loop (die Seitenleiste zeigt „LIVE“); creativeLoaded meldet live: true. Im Export zeigt der Platzhalter das Stream-Bild, das beim Rendern gerade läuft. Das ist nicht frame-genau wiederholbar. Ohne sprechende Endung (z. B. …/stream?id=42) kind: "stream" mitsenden.

Sicherheit

Welche Seiten das iframe einbetten dürfen, ist serverseitig per frame-ancestors festgelegt – nur diese können überhaupt Nachrichten senden.

Schritt 9 · Optional

Zugang nur mit Token (Login oder Bezahlschranke)

Standardmäßig kann jeder den Kreativsimulator Ihrer Subdomain öffnen. Soll er nur für angemeldete oder zahlende Besucher erscheinen, stellt Ihr Mandant die Subdomain in VirtualCampaign unter Einstellungen → Kreativsimulator auf „Nur mit Token“. Danach zeigt der Simulator ohne gültiges Token keine Produkte, sondern einen Hinweis.

Das Prinzip: Der API-Key bleibt auf Ihrem Server. Ihr Server holt für jeden Seitenaufruf ein kurzes Embed-Token und gibt nur dieses an das iframe weiter.

Voraussetzung: API-Key anlegen (einmalig)

  1. In VirtualCampaign: Einstellungen → REST API → Neuer Key.
  2. Typ „Kreativsimulator“ wählen und eine Benutzergruppe zuweisen. Die Gruppe bestimmt, welche Produkte Ihre Besucher sehen.
  3. Den Key kopieren. Er wird nur einmal angezeigt. Legen Sie ihn nur auf Ihrem Server ab (z. B. als Umgebungsvariable), nie in HTML, in JavaScript im Browser oder in einem Repository.

Ist der Key deaktiviert oder gelöscht, stellt die API keine Tokens aus, und alle laufenden Sitzungen enden sofort. Tipp: Legen Sie einen eigenen Key nur für diesen Zweck an.

Schritt 1: Token holen (auf Ihrem Server)

POST https://api.virtualcampaign.dev/v1/kreativsimulator/embed-tokens

Body-FeldPflichtBedeutung
sitenein*Ihre Subdomain, z. B. "demo" für demo.kreativsimulator.com. *Pflicht, wenn Ihr Mandant mehrere Subdomains hat.
ttlSecondsneinGültigkeit des Tokens in Sekunden, 60 bis 900. Standard 300 (5 Minuten).
Code ansehen curl
curl
curl -X POST https://api.virtualcampaign.dev/v1/kreativsimulator/embed-tokens \
  -H "Authorization: Bearer <API-KEY>" \
  -H "Content-Type: application/json" \
  -d '{"site":"<your-tenant>"}'

Antwort (201):

Code ansehen JSON
JSON
{
  "token": "vcke_…",
  "expiresAt": "2026-09-25T12:05:00.000Z",
  "site": "<your-tenant>",
  "embedUrl": "https://<your-tenant>.kreativsimulator.com/#vcToken=vcke_…"
}

Beispiel Node.js (Server-Route, die Ihre Seite beim Ausliefern aufruft):

Code ansehen Node.js
Node.js
// Call this only for signed-in visitors. You check that as usual.
async function kreativsimulatorEmbedUrl() {
  const res = await fetch("https://api.virtualcampaign.dev/v1/kreativsimulator/embed-tokens", {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.VC_KREATIVSIMULATOR_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ site: "<your-tenant>" }),
  });
  if (!res.ok) throw new Error(`Kreativsimulator token: ${res.status}`);
  const { embedUrl } = await res.json();
  return embedUrl;
}

Schritt 2: iframe einbinden

Nutzen Sie embedUrl als src. Weitere URL-Parameter (Schritt 3) kommen vor das #:

Code ansehen HTML
HTML
<iframe src="https://<your-tenant>.kreativsimulator.com/?lang=de&product=ec5472c2#vcToken=vcke_…"
        title="Kreativsimulator" style="width:100%;height:720px;border:0" allow="fullscreen"></iframe>

Der Simulator nimmt das Token sofort aus der Adresse und tauscht es gegen eine Sitzung (Standard 4 Stunden, im Mandanten einstellbar).

Schritt 3 (optional): Sitzung ohne Neuladen verlängern

Läuft die Sitzung ab oder fehlt das Token, sendet der Simulator accessRequired. Ihre Seite holt dann über Ihren Server ein neues Token und schickt es mit setAccessToken:

Code ansehen JS
JS
window.addEventListener("message", async (e) => {
  if (e.data?.source !== "kreativsimulator" || e.data.type !== "accessRequired") return;
  const { token } = await fetch("/my-server/kreativsimulator-token").then((r) => r.json());
  iframe.contentWindow.postMessage({ type: "kreativsimulator:setAccessToken", token }, "*");
});

Wichtig

  • Ein Token gilt 5 Minuten (bzw. ttlSeconds) und nur einmal. Holen Sie es erst beim Ausliefern der Seite, für jeden Seitenaufruf neu. Nicht vorab auf Vorrat holen und nicht cachen. Eine kopierte iframe-Adresse funktioniert danach nicht mehr.
  • Schreiben Sie Tokens und Sitzungen nicht in Logs oder Analytics.
  • Das Token steht bewusst hinter # und nicht hinter ?. So geht es nicht an Server, in Logs oder in den Referer.
  • Die Sitzung sieht genau die Produkte der Benutzergruppe des API-Keys.

Fehler beim Token-Abruf

HTTPcodeBedeutung
401INVALID_API_KEYKey fehlt, ist falsch oder deaktiviert.
403KREATIVSIMULATOR_REQUIREDKey ist nicht vom Typ „Kreativsimulator“.
403GROUP_REQUIREDKey hat keine Benutzergruppe.
403FEATURE_DISABLEDREST-API oder Kreativsimulator ist für den Mandanten nicht freigeschaltet.
404SITE_NOT_FOUNDSubdomain unbekannt, inaktiv oder gehört nicht zu Ihrem Mandanten.
400SITE_REQUIREDMandant hat mehrere Subdomains. Geben Sie site an.
429RATE_LIMITZu viele Anfragen. Beachten Sie Retry-After.
Schritt 10 · Optional

Auswahl, Teilen-Link und Slideshow

Diese Funktionen schaltet VirtualCampaign für Ihre Subdomain frei (bitte bei uns anfragen). Ohne Freischaltung ändert sich am iframe nichts.

  • Auswahl: Der Knopf + „Hinzufügen“ rechts in der Leiste, vor dem Export-Knopf, legt das aktuelle Produkt mit seinen Motiven, seinem Effekt und seiner Länge ab, als fertiges Bild wie beim Export und mit denselben Regeln für Logo und Wasserzeichen. Auswahl rechts in der Leiste (Anzahl als Plakette; mit Produkt-Bibliothek stattdessen der Reiter „Meine Auswahl“ oben mit Gesamtdauer und Anzahl) öffnet den Dialog „Exportieren und teilen“ im Reiter „Meine Auswahl“. Hat die Auswahl Einträge, öffnet auch der Export-Knopf direkt dort. Der Nutzer sortiert die Einträge per Ziehen (Handy: halten, dann ziehen; auch in der Leiste „Meine Auswahl“), teilt die Auswahl per Link, erzeugt ein Auswahl-Video (alle Einträge nacheinander in einem MP4, bis 720p, auf Wunsch mit Musik, die auf dem Simulator unter /music/ liegt) und lädt unter Download „Bilder + PDF“ (ZIP mit Bildern in mittlerer Auflösung und einem A4-PDF mit Name und Produktcode je Seite) oder eine PowerPoint-Datei (16:9, Titelfolie und eine Folie pro Eintrag). Öffnet der Nutzer einen Eintrag und ändert Effekt, In/Out oder Motive, aktualisiert sich der Eintrag von selbst. Mit Produkt-Bibliothek spielt er die ganze Auswahl nacheinander ab. ZIP und PowerPoint folgen ?export: Ohne Export-Knopf gibt es sie nicht. Höchstens 20 Einträge. Der Link enthält nur Produkte, Ansichten und Motive per URL. Motive aus Dateien fehlen beim Empfänger.
  • Zwischenablage im iframe: Damit „Link kopieren“ funktioniert, erlauben Sie es im iframe mit allow="clipboard-write; fullscreen". Ohne diese Freigabe zeigt der Simulator den Link zum Markieren an.
  • Ihre Seite als Ziel des Links: Das Event selectionLinkCopied liefert param, den Wert für sel. Hängen Sie ihn an die src Ihres iframes (…?sel=<param>), dann öffnet sich die Auswahl auf Ihrer Seite. Bei „Nur mit Token“ ist das nötig, weil der Direkt-Link ohne Token nicht öffnet.
  • Slideshow (eigene Freischaltung): Aus der Auswahl entsteht ein teilbarer Link https://<subdomain>.kreativsimulator.com/s/<hash>, auf Wunsch mit Titel und mehrzeiliger Nachricht, gültig 1 bis 30 Tage. Der Nutzer wählt „Bilder-Slideshow“ oder „Video“. Die Bilder-Slideshow öffnet mit einem Startbild (Titel, Nachricht, Knopf „Start“), das stehen bleibt, bis der Empfänger tippt, und spielt die Bilder mit Überblendung ab. Foto-Produkte laufen mit Ausschnitt, Ken-Burns-Effekt und Länge wie im Simulator. Beim Video entsteht das Auswahl-Video, und der Link spielt es ab. Beide Arten können Musik haben, die sich im Dialog vorhören lässt. Die Seite hat einen Stumm-Knopf, beim Video zusätzlich Stopp und eine Zeitleiste zum Spulen. Ein Teilen-Knopf kopiert den Link, damit der Empfänger ihn direkt weiterschicken kann. Vollbild und Wischen gehen auch auf dem Handy. Der Link ist öffentlich, der Hash ist der Zugang, auch bei „Nur mit Token“. Im Dialog gibt der Nutzer seine eigene E-Mail-Adresse und die des Empfängers an. Der Empfänger bekommt den Link mit der Nachricht per E-Mail (Antworten gehen an den Absender), nie den Löschen-Link. Die fertigen Bilder speichert VirtualCampaign bis zum Ablauf. Der Ersteller bekommt eine E-Mail mit Ansehen- und Löschen-Link. Event: slideshowCreated {url, expiresAt}.
  • Die Auswahl bleibt erhalten: Der Simulator merkt sich die Auswahl mit den fertigen Bildern 30 Tage im Browser des Besuchers. Seitenwechsel oder Neuladen Ihrer Seite leeren sie nicht. Sie müssen dafür nichts tun und keine Sitzung weitergeben. Jeder Besucher hat seine eigene Auswahl, und die Browser trennen den Speicher pro einbettender Website. Nichts davon geht an einen Server. Grenzen: nur derselbe Browser auf demselben Gerät, und ein privates Fenster vergisst sie beim Schließen.
  • Auswahl-Events: selectionChanged {count, items: [{code, view, name, ready}], param} kommt nach jeder Änderung und nach dem Wiederherstellen. param ist der volle Stand als Wert für sel. Wollen Sie die Auswahl geräteübergreifend halten, etwa im Nutzerkonto, speichern Sie ihn und hängen ihn später an die iframe-src (…?sel=<param>). Motive aus Dateien fehlen dann, die Einträge werden neu geladen. Knöpfe ausblenden: ?selection=0 bzw. setChrome {selection: false}.
  • Tastatur: Solange ein Dialog offen ist (Auswahl, Slideshow, Ansichten, Export) oder der Nutzer in ein Feld tippt, sind die Tastenkürzel des Simulators aus (Leertaste = Play/Pause, ←/→, i/o).

„Link kopieren“ erlauben und die Auswahl im Nutzerkonto halten:

Code ansehen HTML
HTML
<iframe id="ks" src="https://demo.kreativsimulator.com/"
        title="Kreativsimulator" style="width:100%;height:720px;border:0"
        allow="clipboard-write; fullscreen"></iframe>
<script>
  window.addEventListener("message", (e) => {
    if (e.data?.source !== "kreativsimulator") return;
    // the full selection as the value for ?sel= — store it, e.g. in the user account
    if (e.data.type === "selectionChanged") saveSelection(e.data.param);   // your function
    if (e.data.type === "selectionLinkCopied") console.log("share link:", e.data.url);
    if (e.data.type === "slideshowCreated") console.log("slideshow until", e.data.expiresAt);
  });
  // later, on any device: open the stored selection on your page
  // document.getElementById("ks").src =
  //   "https://demo.kreativsimulator.com/?sel=" + encodeURIComponent(storedParam);
</script>
Schritt 11 · Optional

Produkt-Bibliothek: Katalog mit Suche und Filtern

VirtualCampaign schaltet die Produkt-Bibliothek pro Kunde frei (bitte bei uns anfragen). Ohne Freischaltung ändert sich am iframe nichts.

  • Kopfzeile: Der Knopf „Katalog (n)“ öffnet einen Dialog mit Suche und Filtern: Typ (Foto, Film, Clip), Format (Querformat, Hochformat, Andere), Seitenverhältnis, Werbeträger und Ort. Die Zahlen neben den Filtern zeigen, wie viele Produkte dazu passen. Die Leiste darunter zeigt die Treffer des Filters (ohne Filter alle Produkte) und lädt beim Blättern nach. Der Reiter „Produkte (n)“ nennt die Zahl der Treffer. Mit freigeschalteter Auswahl gibt es zwei Reiter, „Produkte“ und „Meine Auswahl“, und der Auswahl-Knopf unten entfällt.
  • Katalog: Produkte, die schon in „Meine Auswahl“ liegen, tragen die Plakette „In Auswahl“. Auf dem Handy öffnen die Filter eine eigene Fläche mit „n Produkte anzeigen“. Werbeträger und Ort kommen aus den Tags der Produkte in VirtualCampaign.
  • Laden nach Bedarf: Der Simulator lädt die Produkte seitenweise. ready.products enthält die ersten 30 Produkte, die Gesamtzahl steht in ready.productsTotal. setProduct und getEffects funktionieren mit jedem Code Ihres Katalogs. Für eine eigene Produktliste holen Sie die Produkte auf Ihrem Server von der VirtualCampaign-API, so wie im Galerie-Beispiel.
  • Wischen auf Handy und Tablet: Nach links wischen zeigt das nächste Produkt (im Reiter „Meine Auswahl“ den nächsten Eintrag), nach rechts das vorige. Das Bild folgt dem Finger. Das iframe nimmt dafür waagrechte Wischgesten an (touch-action: pan-y), senkrechtes Scrollen Ihrer Seite bleibt möglich.
  • Zusammen mit der Auswahl: Setzt der Nutzer ein Motiv oder bestätigt er Änderungen im Editor auf dem Bild mit ✓, liegt das Produkt danach in „Meine Auswahl“, und die Kopfzeile zeigt diesen Reiter. Motive per setCreative lösen das nicht aus. Im Katalog kreuzt der Nutzer mehrere Produkte an und nimmt sie mit „Zu ‚Meine Auswahl‘ hinzufügen“ ohne Motiv auf. Eine Kachel „Leeren“ leert die Auswahl nach einer Rückfrage. Einträge mit leeren Werbeflächen sind markiert, und vor dem Exportieren oder Teilen fragt der Simulator nach. Im Editor auf dem Bild überträgt ⋯ „Motiv in alle Produkte der Auswahl“ nach einer Warnung das Motiv auf alle Flächen aller Einträge. selectionChanged meldet jede dieser Änderungen.
  • Knopf ausblenden: ?library=0 bzw. setChrome {library: false}. Leiste und Reiter bleiben.

Gut zu wissen

  • Kein API-Key im Browser. Die Authentifizierung gegenüber der VirtualCampaign-API läuft vollständig serverseitig. Ohne „Nur mit Token“ müssen Sie außer dem iframe nichts konfigurieren. Mit „Nur mit Token“ kommt nur der Token-Abruf auf Ihrem Server dazu (Schritt 9).
  • Der Nutzer kann den Simulator direkt im iframe bedienen oder Sie steuern ihn per postMessage von Ihrer Seite. In der Seitenleiste kann er ein Motiv auch per URL einfügen (Feld unter der Upload-Fläche, auch für HLS-/DASH-Streams) oder einen Link aus einem anderen Tab auf die Upload-Fläche ziehen.
  • Der iframe lädt Videomaterial. Eine stabile Internetverbindung ist deshalb von Vorteil.

Troubleshooting

Der iframe bleibt leer / weiße Fläche

Mit hoher Wahrscheinlichkeit ist die einbettende Domain nicht freigeschaltet. Die Browser-Konsole (F12) zeigt dann „Refused to frame … frame-ancestors …“. Tragen Sie die exakte Domain (inkl. https:// und ggf. Subdomain) in VirtualCampaign unter Einstellungen → Kreativsimulator ein, oder schicken Sie sie uns, dann schalten wir sie frei.

Der iframe zeigt keine Produkte, sondern „nur über die Webseite des Anbieters erreichbar“

Die Subdomain steht auf „Nur mit Token“, und das iframe kam ohne Token. Prüfen Sie, dass Ihre Seite embedUrl (mit #vcToken=…) einbindet und nicht die nackte Adresse.

Der iframe zeigt „Ihre Sitzung ist abgelaufen“

Das Token war abgelaufen oder schon einmal benutzt (z. B. Seite mit derselben Adresse neu geladen), oder die Sitzung ist vorbei. Holen Sie für jeden Seitenaufruf ein neues Token. Für lange Sitzungen nutzen Sie accessRequired + setAccessToken (Schritt 9).

Der iframe zeigt keine Produkte / „kein Zugriff“

Der Mandant ist serverseitig noch nicht vollständig eingerichtet (z. B. kein Key für den Kreativsimulator hinterlegt). Schreiben Sie uns kurz, dann holen wir das nach.

Ein Motiv erscheint nicht (error-Event)

Die message im error-Event nennt den Grund. Häufig: die URL braucht eine Anmeldung, liefert einen HTTP-Fehler oder zeigt auf eine interne Adresse – oder der loader-Name passt nicht zum aktiven Produkt. Öffnen Sie die URL testweise in einem privaten Browserfenster und senden Sie loader-gezielte Befehle erst nach dem productChanged-Event.

Bereit zum Einbetten?

Mit Zugang zu VirtualCampaign tragen Sie Ihre Domains selbst ein. Sonst schicken Sie sie uns. Wir schalten die Einbettung frei und sagen Ihnen Bescheid, sobald alles bereit ist.