Best Practices beim Einbetten von Spielen per Iframe
Viele Plattformen (Itch.io, Newgrounds, Cinevva, deine eigene Seite) betten Spiele in Iframes ein. Dieses Tutorial zeigt, wie dein Spiel eingebettet gut funktioniert.
1) Grundaufbau für Einbettung
Dein Spiel sollte laufen, ohne die volle Kontrolle über die Seite zu brauchen:
html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
* { margin: 0; padding: 0; }
html, body { width: 100%; height: 100%; overflow: hidden; }
canvas { display: block; width: 100%; height: 100%; }
</style>
</head>
<body>
<canvas id="game"></canvas>
<script src="game.js"></script>
</body>
</html>2) Den Iframe-Kontext erkennen
js
const isEmbedded = window.self !== window.top
if (isEmbedded) {
// Adjust behavior for embedded context
hideExternalLinks()
adjustUIForSmallSize()
}
function hideExternalLinks() {
document.querySelectorAll('a[target="_blank"]').forEach(link => {
link.style.display = 'none'
})
}3) Nötige Iframe-Berechtigungen erlauben
Die übergeordnete Seite bestimmt, was dein Iframe darf. Übliche Berechtigungen:
html
<iframe
src="https://game.example.com"
allow="fullscreen; autoplay; gamepad; pointer-lock"
sandbox="allow-scripts allow-same-origin allow-pointer-lock allow-popups"
></iframe>Dein Spiel sollte mit fehlenden Berechtigungen sauber umgehen:
js
// Check if fullscreen is available
const canFullscreen = document.fullscreenEnabled || document.webkitFullscreenEnabled
if (!canFullscreen) {
document.getElementById('fullscreen-btn').style.display = 'none'
}4) Vollbild aus dem Iframe
Vollbild verlangt das Attribut allow="fullscreen":
js
async function requestFullscreen() {
const elem = document.documentElement
try {
if (elem.requestFullscreen) {
await elem.requestFullscreen()
} else if (elem.webkitRequestFullscreen) {
await elem.webkitRequestFullscreen()
}
} catch (err) {
// Fullscreen not allowed - show message
showMessage('Fullscreen not available when embedded')
}
}5) Mit der übergeordneten Seite kommunizieren
Nutze postMessage für sichere Kommunikation über Origins hinweg:
In deinem Spiel:
js
// Send message to parent
function notifyParent(type, data) {
if (window.parent !== window) {
window.parent.postMessage({ type, data, source: 'game' }, '*')
}
}
// Receive messages from parent
window.addEventListener('message', (event) => {
// Validate origin if needed
// if (event.origin !== 'https://trusted-host.com') return
const { type, data } = event.data
switch (type) {
case 'pause':
pauseGame()
break
case 'resume':
resumeGame()
break
case 'setVolume':
setVolume(data.volume)
break
}
})
// Notify when game is ready
window.addEventListener('load', () => {
notifyParent('ready', { width: 800, height: 600 })
})
// Notify on game events
function onGameOver(score) {
notifyParent('gameover', { score })
}In der übergeordneten Seite:
js
const iframe = document.getElementById('game-iframe')
iframe.addEventListener('load', () => {
// Listen for messages from game
window.addEventListener('message', (event) => {
if (event.source !== iframe.contentWindow) return
if (event.data.source !== 'game') return
const { type, data } = event.data
if (type === 'ready') {
console.log('Game ready:', data)
}
if (type === 'gameover') {
showScoreModal(data.score)
}
})
})
// Send commands to game
function pauseGame() {
iframe.contentWindow.postMessage({ type: 'pause' }, '*')
}6) Mit dem Fokus umgehen
Iframes können den Fokus verlieren, was die Tastatureingabe kaputt macht:
js
// Auto-focus canvas when clicked
canvas.addEventListener('click', () => {
canvas.focus()
})
// Make canvas focusable
canvas.tabIndex = 1
// Handle focus loss
window.addEventListener('blur', () => {
// Reset held keys
input.left = input.right = input.up = input.down = false
if (isEmbedded) {
// Optionally pause
// pauseGame()
}
})
// Request focus from parent
function requestFocus() {
notifyParent('requestFocus', {})
}7) Auf verschiedene Einbettungsgrößen reagieren
Rechne mit unterschiedlichen Maßen:
js
function handleResize() {
const width = window.innerWidth
const height = window.innerHeight
// Adjust UI based on size
if (width < 400 || height < 300) {
enableCompactUI()
} else {
enableFullUI()
}
// Scale game appropriately
resizeCanvas(width, height)
}
window.addEventListener('resize', handleResize)
handleResize()8) Ladeanzeige für Embeds
Zeig sofort irgendetwas:
js
// Inline in HTML for instant display
const loadingHTML = `
<div id="loading" style="
position: fixed;
inset: 0;
display: flex;
align-items: center;
justify-content: center;
background: #1a1a2e;
color: #fff;
font-family: sans-serif;
">
<div>
<div class="spinner"></div>
<p>Loading...</p>
</div>
</div>
`
// Remove when game is ready
function hideLoading() {
const loading = document.getElementById('loading')
if (loading) {
loading.style.opacity = '0'
loading.style.transition = 'opacity 0.3s'
setTimeout(() => loading.remove(), 300)
}
}9) Plattformeigene SDKs
Manche Plattformen haben eigene APIs:
Itch.io:
js
// No SDK required, but can use postMessage for achievements
window.parent.postMessage({ type: 'itch-achievement', data: { id: 'first-win' }}, '*')Newgrounds:
js
// Include Newgrounds.io SDK
const ngio = new Newgrounds.io.core('APP_ID', 'AES_KEY')
ngio.callComponent('Medal.unlock', { id: 12345 })Cinevva:
js
// Signal game is ready
window.parent.postMessage({ type: 'cinevva:ready' }, '*')
// Signal playable state
window.parent.postMessage({ type: 'cinevva:playable' }, '*')10) Sicherheitsaspekte
js
// Validate message origins when sensitive
window.addEventListener('message', (event) => {
const trustedOrigins = [
'https://itch.io',
'https://cinevva.com',
'https://yoursite.com'
]
if (!trustedOrigins.includes(event.origin)) {
return // Ignore untrusted messages
}
// Process message...
})
// Don't expose sensitive operations via postMessage
// Only allow whitelisted commands
const allowedCommands = ['pause', 'resume', 'setVolume', 'mute']
window.addEventListener('message', (event) => {
const { type } = event.data
if (!allowedCommands.includes(type)) return
// Handle command...
})Testcheckliste
- Lokal testen: Nutz einen einfachen HTTP-Server, nicht
file:// - Cross-Origin: Teste mit echter Iframe-Einbettung
- Berechtigungen: Teste mit eingeschränkter Sandbox
- Fokus: Teste die Tastatur, nachdem du außerhalb des Iframes geklickt hast
- Größenänderung: Teste verschiedene Einbettungsgrößen
- Mobil: Teste Touch im eingebetteten Kontext
html
<!-- Test embed page -->
<!DOCTYPE html>
<html>
<body style="background: #333; padding: 20px;">
<h1 style="color: #fff;">Embed Test</h1>
<iframe
src="http://localhost:8000"
width="800"
height="600"
allow="fullscreen; autoplay; gamepad"
></iframe>
</body>
</html>Verwandt
- Ein Web-Spiel ausliefern, das schnell lädt
- Mobilfreundliche Web-Spiele
- Für Creator
- COOP/COEP und SharedArrayBuffer — Cross-Origin-Header, die die Iframe-Einbettung beeinflussen
- Wie du dein Spiel auf itch.io veröffentlichst — itch.io bettet Browser-Spiele nativ in Iframes ein
Externe Ressourcen
- MDN: iframe element — vollständige Referenz zu den Iframe-Attributen
- MDN: Permissions Policy — steuern, welche Funktionen Iframes nutzen dürfen
- MDN: postMessage API — Kommunikation zwischen Iframe und übergeordneter Seite