← Back to Dashboard

Playlink Developer Guide

Everything you need to get your game on Playlink — from technical requirements to optimization tips.

👋 This guide helps your clip succeed

Most clips that perform poorly have simple issues that are easy to fix. Here are the top 5 things to get right:

📱 Design for portrait — players hold their phone upright

👆 Add touch controls — most players are on mobile

🔧 Set iframe headers — so your clip loads inside Playlink

🎯 Keep buttons in the center — edges are hard to reach

⚡ Stay under 7MB — fast loading = more plays

Takes about 5 minutes to read. Each section has examples and code snippets.

1. What is a Playable Clip?

A Playable Clip is NOT your full game.

It's a small, self-contained web build of the best 10–60 seconds of your game. Think of it as a movie trailer — you pick the most exciting moment, cut it down, and make it stand on its own.

Players see your clip, play it instantly in their browser with zero downloads, and if they like it — they tap the action button to open your store page or play the full game. Your clip runs inside a secure sandbox on Playlink. You host it on your own server (Vercel, Netlify, etc.) — we just load the URL.

Duration

10–60 seconds

You choose. Default 30.

Format

Web page (HTML/JS/CSS)

Loaded in an iframe

Hosting

Your server

Vercel, Netlify, own server

Cost

Free

Upload and reach players for $0

✦ Build your clip with AI

Already have a playable ad, or a Unity/Godot/Construct web export?

You already have your clip — skip everything below and go straight to hosting.

Where to host it (free)

Drag your file to app.netlify.com/drop for an instant HTTPS link. Vercel and GitHub Pages work too. Your URL must be https:// and must not block iframe embedding — our upload check will flag it if there's a problem.

Track 1: No-code

Rosebud AI

Games — text → browser game

Free tier (limited) / ~$19.99 for commercial rights

PlayableMaker

HTML5-focused

Single file, under 5MB

Track 2: Claude + asset tools (MCP)

In Claude.ai: Settings → Connectors → Add custom connector.

Higgsfield

mcp.higgsfield.ai/mcp

Images, backgrounds, mockups — games AND apps — Free / OAuth

Ludo.ai

mcp.ludo.ai/mcp

Sprites, animation, SFX — games only — ⚠️ Requires $50/mo Pro plan

🎮 Games

Describe a clip: "hold to fly, release to fall, collect coins, dodge obstacles"

📱 Apps

Describe the moment: "enter a number, tap calculate, see an animated result" — the "aha" moment your user should feel in 30 seconds.

A "game loop" is code that runs many times per second — checking input, moving things, redrawing the screen. You don't need to understand it to build one — Claude writes it — but when something "feels off" (slow, jerky), that's almost always where the problem is.

⚠️ Don't ask the tool to generate the whole clip

Both tools can generate video, which creates something to watch, not play. The interactive logic (touch/input/calculation) must be real code. These tools are for assets only.

Track 3: Your building workflow (7 steps)

1

Describe the core — the mechanic for a game, the moment for an app — and let Claude write the code.

2

⚠️ Test it standalone first

Open the file directly in its own browser tab, not just in the preview. Some problems only show up when you compare a standalone run to running inside an iframe.

3

Add assets (Ludo.ai or Higgsfield, depending on what you're building) — embed them, then re-check the file size (the 7MB limit).

4

⚠️ Test on a real phone, not just desktop

A lot of problems (sizing, speed, orientation) only show up there.

5

A precise description = a fast fix. "The obstacles are small and slow in landscape" helps far more than "it's broken." Tell Claude exactly what you're seeing.

6

⚠️ Common trap

If sizes/speed are derived from screen height but hit-distances are fixed absolute numbers, switching orientation (portrait↔landscape) breaks the balance. If it "feels different" between modes, this is probably why.

7

Repeat steps 2–6 until it actually works — not "it compiled," but tested and working.

2. Technical requirements

HTTPS only — HTTP URLs are rejected automatically
Must be a web page (HTML/JS/CSS) loadable in a browser
Must be embeddable in an iframe (see section 5)
Touch and mobile controls required (see section 7)
Loads in under 5 seconds on a 4G connection
Game must start INSTANTLY — no "Start Game" or "Press to Play" screen. The timer runs from second 1, every second of splash screen is a wasted second
Game must RESET on every load — do not use localStorage / cookies to save progress or scores. Every new player gets a fresh game
No redirects on load — no login walls, no age gates, no splash screens that navigate away
No popups or window.open() calls
No attempts to access parent page or top-level navigation

Performance target

Faster is better — every second compounds

Keep it under 7MB. Every extra second of loading time means fewer players reach the fun part before they tap away — and every dropped player is a missing store tap. A fast, focused clip converts better.

You don't need a hard cutoff to aim at. Focus on the one thing that matters: show something on screen — a character, a button, a moving element — as fast as possible, even on a 4G phone. The faster, the more reach you get.

  • Defer non-critical assets (audio, large textures) until after first interaction
  • Show a placeholder/sprite immediately, then swap in the real asset
  • Lazy-load levels: don't bundle 50 levels into the initial download
  • Compress textures (WebP, AVIF) and audio (Opus, ~64kbps)

3. Iframe sandbox — what works and what doesn't

Playlink loads your clip inside a sandboxed iframe with sandbox="allow-scripts". This is the most restrictive mode that still allows JavaScript to run. Your clip gets a unique opaque origin — it cannot access the parent page, other frames, or any storage tied to your real domain.

✓ Works

JavaScript execution

Canvas 2D and WebGL

Web Audio API (after user tap)

Touch, pointer, keyboard events

CSS animations and transitions

requestAnimationFrame

setTimeout / setInterval

fetch() to your own server (with CORS)

WebAssembly (Wasm)

✗ Blocked

localStorage / sessionStorage

IndexedDB

Cookies (document.cookie)

Opening new tabs (window.open)

Redirecting parent page

Camera / microphone access

Geolocation

File downloads

Clipboard access

Form submissions

Important: No local storage

Because the sandbox assigns your clip a unique opaque origin, localStorage, sessionStorage, IndexedDB, and cookies are all blocked. If your game uses any of these to save progress, scores, or settings — it will throw errors. Since clips are 10–60 seconds, persistent storage is rarely needed. If you must store temporary state, use JavaScript variables in memory.

4. CORS — when and why you need it

Because your clip runs on an opaque origin, any fetch() or XMLHttpRequest from your clip to your server is a cross-origin request. Your server must respond with the CORS header:

Access-Control-Allow-Origin: *

When do you NOT need CORS?

If your clip is a single HTML file with all assets inlined (base64 images, inline CSS, inline JS) — it makes no network requests, so CORS is irrelevant. CORS is only needed when your game loads external files (images, audio, JSON, fonts) from your server at runtime.

5. Iframe embedding headers

Some servers and hosting platforms block iframe embedding by default. If your server sends any of these headers, your game will not load on Playlink:

X-Frame-Options: DENY
X-Frame-Options: SAMEORIGIN
Content-Security-Policy: frame-ancestors 'self'
Content-Security-Policy: frame-ancestors 'none'

To explicitly allow embedding, either remove these headers entirely, or set:

Content-Security-Policy: frame-ancestors *

Playlink automatically checks for these headers when you submit your clip. If embedding is blocked, you'll see an error before upload.

6. Hosting platforms — what works

Vercel

Works

Add vercel.json config

Netlify

Works

Add _headers file

Cloudflare Pages

Works

Add _headers file

Firebase Hosting

Works

Add firebase.json headers

Your own server

Works

Configure CORS + CSP

AWS S3 + CloudFront

Works

Configure CORS in bucket

itch.io (embed URL)

Partial

Only the html-classic.itch.zone embed URL works, not the game page URL

GitHub Pages

Not supported

Blocks iframe embedding — cannot be changed

Vercel — vercel.json

{
  "headers": [
    {
      "source": "/(.*)",
      "headers": [
        { "key": "Access-Control-Allow-Origin", "value": "*" },
        { "key": "Access-Control-Allow-Methods", "value": "GET, OPTIONS" }
      ]
    }
  ]
}

Netlify / Cloudflare Pages — _headers file

/*
  Access-Control-Allow-Origin: *
  Access-Control-Allow-Methods: GET, OPTIONS

Nginx

location / {
    add_header Access-Control-Allow-Origin *;
    add_header Access-Control-Allow-Methods "GET, OPTIONS";
}

Apache — .htaccess

Header set Access-Control-Allow-Origin "*"

7. Mobile and touch controls

The majority of players are on mobile. Your clip must work with touch controls.

Tap targets at least 44×44px — fingers are bigger than cursors
On-screen controls visible — don't assume a keyboard exists
Game scales to portrait (9:16) and landscape
Viewport meta tag: width=device-width, initial-scale=1
Test on a real phone, not just browser DevTools
Don't rely on hover states for gameplay
Don't rely on right-click or middle-click
Don't assume a physical keyboard

Portrait first — your clip must work vertically

Most Playlink pages are portrait, like a TikTok clip. If your game is landscape, that's fully supported too — visitors on mobile will see a 'rotate your phone' prompt automatically.

Design your clip's UI for portrait (tall) orientation
If your game is landscape, create a special "vertical mode" for the clip
Use CSS media queries: @media (orientation: portrait) for adaptive layout
Place controls at the bottom-center (thumb-friendly zone)
Don't just shrink a landscape game into a portrait frame — it looks bad
Landscape clips work too — visitors on mobile automatically see a 'rotate your phone' prompt, and can expand to fullscreen. Just make sure your clip is actually built for a fixed 16:9 layout, not squeezed from portrait.
Don't use letterboxing (black bars) — it wastes half the screen

Example: Your racing game is landscape. For the Playable Clip, create a top-down view where the car drives upward (portrait-friendly). The full game stays landscape — the clip is just a 30-second taste.

Leave room for the store button

Playlink shows a store button fixed at the bottom of the screen. Keep your own buttons and controls above the bottom ~80px so nothing overlaps.

✅ SAFE ZONE
⚠️ RESERVED FOR STORE BUTTON (bottom ~80px)
Place all buttons and controls in the top or middle of the screen
Leave the bottom strip clear — that's where visitors tap to get your full game
Don't put "Start" or "Play" buttons at the very bottom
Don't put navigation, menus, or controls in the bottom ~80px

8. Audio in the browser

Browsers block autoplay audio

All modern browsers (Chrome, Safari, Firefox) block audio that plays without user interaction. Inside a sandboxed iframe, this is even stricter. Your game must only start audio after the player taps or clicks — not on page load. If you call audioContext.resume() or audio.play() on load, it will silently fail.

Best practice: Add a "Tap to start" screen. When the player taps, initialize audio in that same event handler. This guarantees the browser allows sound.

⚠️ Howler.js / XHR audio will fail

Libraries like Howler.js load audio files via XMLHttpRequest by default. Inside our sandbox, the origin is null, so XHR to external URLs will be blocked by CORS — even your own server.

Fix options:

1. Use Howler with html5 mode: new Howl({ src: ['sound.mp3'], html5: true }) — uses HTML5 Audio instead of XHR
2. Use native Audio API: new Audio('sound.mp3') — no CORS needed
3. Inline audio as base64: embed audio directly in HTML — no network requests at all

9. Performance — speed compounds

Keep your clip under 7MB. The longer it takes to become playable, the more visitors drop off before they even see the fun part — and every dropped visitor is a missed store tap. A fast first frame is the single biggest lever you control.

< 1s to interactive

Excellent. Most players make it past the loading bar.

1-3s to interactive

Good. Most clips fit here.

3-5s to interactive

OK, but engagement starts to dip — fewer plays reach completion.

5s+ to interactive

Workable, but you&apos;ll see noticeably less reach over time.

How to load faster

Cut your build — Don't ship the full game. Export only the level/scene you want to show.

Lazy-load assets — Show a sprite or placeholder first, swap in real assets after first interaction.

Compress textures — Use WebP instead of PNG. Reduce resolution for mobile.

Compress audio — Use OGG/MP3 at 64-128kbps. Remove background music if it's heavy.

Minify JS — Most build tools do this automatically (Vite, Webpack).

Lazy load — Load assets as needed, not all upfront.

Remove unused code — Tree-shaking, dead code elimination.

10. What makes a clip convert

There's no hidden algorithm and no ranking system. Every clip gets its own page, and you see exactly how it performs in your dashboard at /creator/stats/[id]. Three numbers tell you everything:

Retention

Where in your clip do visitors leave? The retention curve shows exactly which second loses people. If everyone drops at second 3, your opening is too slow. If they make it to second 20 but leave before the end, the pacing needs work.

Sources

Which channel or creator is sending the most plays? See if your traffic comes from a shared link, a specific social post, or direct visits. Double down on what works.

Tap rate

What percentage of plays end with a tap to your store page? This is the number that matters most — it tells you whether your clip is actually converting visitors into potential customers.

The bottom line

Make a clip that visitors play to the end. That's the single biggest lever on your tap-to-store rate. Check your Stats page to see exactly where people drop off — then fix that moment.

11. Building your clip — by engine

Already have a web game? — You might be ready. Just paste the URL and test with section 12.

Unity — Export as WebGL build. Uncheck "compression" for faster loading (or use Brotli if your server supports it). Host the build folder on Vercel/Netlify. Set the scene to the exact moment you want to show.

Godot — Export as HTML5. Godot produces a lightweight build. Upload to Vercel or Netlify.

Unreal Engine — HTML5 export (UE4) or a Pixel Streaming demo page. UE5 doesn't officially support HTML5, but community plugins exist.

Phaser / PixiJS / Three.js — Native web. Bundle with Vite and deploy.

Native / Console games — Build a small HTML5 demo that captures the feel of your game. Even a simple interactive scene or mini-game works. It doesn't have to be the actual game — just something that represents it.

Free hosting: Vercel, Netlify, and Cloudflare Pages all work and are free.

12. Test before you submit

This is the most important step

If you test your game in a sandboxed iframe before uploading, you'll catch every issue yourself. This is the exact same environment Playlink uses.

Save this as test.html and open it locally:

<!DOCTYPE html>
<html>
<body style="margin:0; background:#000;">
  <iframe
    src="https://YOUR-GAME-URL-HERE"
    width="390"
    height="844"
    sandbox="allow-scripts"
    style="border:none;"
  ></iframe>
</body>
</html>

Open DevTools (F12) → Console. Check that:

Game loads without errors
No CORS errors in console
No CSP or sandbox errors
All assets load (images, audio, fonts)
Sound plays after first tap
Touch controls work on mobile
No blank screen or broken layout
Game fits in the 390×844 viewport (iPhone size)

13. Common errors and fixes

Failed to read 'localStorage' from 'Window'

Cause: Your game uses localStorage which is blocked in the sandbox.

Fix: Replace localStorage with in-memory JavaScript variables. For a 30-second clip, you don't need persistent storage.

Blocked by CORS policy: No 'Access-Control-Allow-Origin' header

Cause: Your server doesn't send CORS headers.

Fix: Add Access-Control-Allow-Origin: * to your server config (see section 4 and 6).

Refused to display in a frame because it set 'X-Frame-Options'

Cause: Your server blocks iframe embedding.

Fix: Remove X-Frame-Options header or set Content-Security-Policy: frame-ancestors * (see section 5).

The AudioContext was not allowed to start

Cause: Audio started without user interaction.

Fix: Only call audio.play() or audioContext.resume() inside a click/tap event handler (see section 8).

Access to XMLHttpRequest at '...mp3' from origin 'null' has been blocked by CORS

Cause: Audio library (like Howler.js) loads sound files via XMLHttpRequest. In our sandbox, origin is null so XHR is blocked.

Fix: If using Howler.js, add html5: true — new Howl({ src: ['sound.mp3'], html5: true }). Or use native new Audio('sound.mp3') instead. Or inline audio as base64 (see section 8).

Blank white screen, no errors in console

Cause: Game might be redirecting, or it requires allow-same-origin.

Fix: Check if your game redirects on load. If it needs cookies/localStorage, refactor to use in-memory state.

Game works locally but not on Playlink

Cause: Most likely CORS or CSP headers missing.

Fix: Test with the iframe test page in section 12. The console will show the exact error.

14. Clip best practices — avoid common mistakes

These are the most common reasons clips get skipped or perform poorly. Follow these guidelines to maximize engagement.

🎮 Multiplayer games

Players are alone in your clip — there's no matchmaking or chat. If your game is multiplayer, make sure the clip has a single-player mode or AI opponent. A player who sees "waiting for opponent..." for 30 seconds will skip immediately.

🔇 Start with sound off

People open your link on the bus, at night, in class. Loud sound on load = instant close. Start muted and let the player turn on sound themselves. Most browsers block autoplay audio anyway, but some don't — be safe.

👆 Touch controls are mandatory

Most Playlink users are on mobile. If your game needs a keyboard (arrows, WASD, spacebar), add touch controls — on-screen buttons, virtual joystick, or swipe gestures. A keyboard-only clip is desktop-only, missing most of the audience.

⚡ Keep it light and fast

Your clip is 30 seconds. If it takes 10 seconds to load, a third of the experience is wasted on a loading bar. There's no upload size limit, but lighter clips become playable faster. Compress textures, lazy-load audio, minify your JS, and trim anything you don't need for the clip.

🚫 No ads in your clip

Remove all ads (AdMob, Unity Ads, etc.) from your clip version. The clip is your trailer — not a revenue source. A 5-second ad in a 30-second clip is a terrible first impression. Revenue comes from the full version.

📱 Portrait-first design

Most Playlink pages are vertical (portrait). If your game is landscape, that's supported - just make sure it looks right in a 16:9 frame, since we show a rotate prompt on mobile. Most responsive games adapt automatically. If yours doesn't, add width: 100%; height: 100% to your container CSS.

Ready to upload?

Test your clip with section 12, then upload it.

Upload Your Clip