Quickstart

Convert your first playable ad in 5 minutes

Playable Kit turns a Unity Luna or Playworks export ZIP into validated, ready-to-submit creatives for every major mobile ad network — automatically. No manual repackaging, no per-network scripts.

💡

This guide covers the web dashboard workflow. For the CLI, jump to CLI reference.

Prerequisites

Installation

Web dashboard (recommended)

No installation needed. Log in and upload your ZIP directly in the browser.

CLI (local / CI)

Terminal
# Install globally
npm install -g playable-kit

# Or use npx without installing
npx playable-kit --help

Upload a build

1

Export from Unity Luna

In your Unity project with the Luna plugin installed, go to Luna → Build and select Playable Ad. Choose Development or Release and click Build. This produces a .zip containing index.html, engine scripts, and asset files.

2

Create an app in Playable Kit

In the dashboard, click New App, give it a name (e.g. Car Dealer Q2), and select your target networks. You can change the network selection later.

3

Upload your ZIP

Drag and drop your Luna export .zip onto the upload area or click Browse. Playable Kit inspects the archive, detects shader profiles, and validates the entry file automatically.

⚠️

The ZIP must contain index.html at the root level. Nested zips are not supported.

4

Add store URLs

Enter your Android URL (Google Play) and iOS URL (App Store). These are injected into each network adapter's CTA handler. You can set a single URL and leave the other blank — the platform auto-selects by user agent.

5

Generate & download

Click Generate. Playable Kit converts and validates your build for every selected network in parallel. Each network produces a .zip you can submit directly to the ad platform. Check marks and warnings appear per network once processing completes.


Supported networks

Playable Kit targets all 12 major mobile ad networks from a single upload. Each output is fully self-contained — no external CDN dependencies.

Google Ads Meta Ads AppLovin TikTok Mintegral Liftoff Chartboost ironSource Unity Ads Snap Ads InMobi Digital Turbine
Network Output format Max size Entry file
Google AdsSingle HTML or ZIP5 MBindex.html
Meta AdsZIP2 MBindex.html
AppLovinSingle HTML2 MB{name}.html
TikTokSingle HTML5 MBindex.html
MintegralZIP4 MBmintegral.html
Liftoff / VungleZIP5 MBindex.html
ChartboostZIP5 MBindex.html
ironSourceZIP5 MBindex.html
Unity AdsZIP5 MBindex.html
Snap AdsZIP5 MBindex.html
InMobiZIP5 MBindex.html
Digital TurbineZIP5 MBindex.html

Output modes

Each network supports two output modes. Choose based on what the ad platform requires.

Folder mode (default)

Outputs a ZIP containing a standard directory structure — index.html at the root with separate JS, asset, and cache folders alongside it. Use this when the network accepts a multi-file creative package.

Single HTML mode

Inlines all scripts, fonts, audio, images, and videos directly into a single .html file. The file is entirely self-contained with no external dependencies. Required for Google Ads single-file submissions and AppLovin.

Single HTML mode automatically applies the WebGL2 upgrade guard, AudioContext resume guard, and Google ExitAPI injection for compliant Google Ads submissions.


CLI options

Run conversions from your terminal or CI pipeline without opening the dashboard.

Basic usage
npx playable-kit \
  --input       MyGame_build.zip \
  --out         dist/ \
  --networks    google,applovin,tiktok \
  --android-url "https://play.google.com/store/apps/details?id=com.example" \
  --ios-url     "https://apps.apple.com/app/id123456789"
OptionTypeDefaultDescription
--input required Path to the Luna/Playworks export .zip
--out optional dist/ Output directory. Created if it doesn't exist.
--networks optional all Comma-separated list: google, mintegral, applovin, tiktok
--mode optional folder folder or single-html
--android-url optional Google Play Store URL injected into CTA handlers
--ios-url optional App Store URL injected into CTA handlers
--package-name optional network id Base name for the output ZIP and HTML files
--cache-id optional auto Specific Luna cache folder to use in single-HTML mode
--orientation optional portrait,landscape Comma-separated: portrait, landscape, or both

CLI examples

Google Ads — single HTML, portrait only

Terminal
npx playable-kit \
  --input       build.zip \
  --networks    google \
  --mode        single-html \
  --orientation portrait \
  --android-url "https://play.google.com/..." \
  --ios-url     "https://apps.apple.com/..."

All networks, custom output folder

Terminal
npx playable-kit \
  --input  MyGame_v3.zip \
  --out    releases/v3/ \
  --android-url "https://play.google.com/..."

Inspect a build without converting

Terminal
npx playable-kit inspect --input build.zip

Build checks

After conversion, Playable Kit runs a suite of automated checks on each output. A failed check blocks submission to that network.

Google Ads checks

CheckWhat it verifies
clickTagA var clickTag or window.clickTag assignment is present
ExitApiGoogle's exitapi.js is loaded from the approved URL
CTA handlerCTA routes through window.clickTag or ExitApi.exit()
Direct mediaNo inline data-URI media tags (base122-encoded only)
WebGL2 guardWebGL upgrade shim is present for zero-dimension canvases
Audio guardAudioContext.resume() is deferred until user gesture
Shader profileReports WebGL1/2 shader counts from shaders.json

Mintegral checks

CheckWhat it verifies
CTA callwindow.install, mraid.open, or pi.logCta is called
Game readygameReady is signalled after Luna starts
Game startgameStart or luna:start event is present
Game endgameEnd is exposed or called
Game closegameClose wrapper is present

TikTok checks

CheckWhat it verifies
TikTok SDKPlayable-sdk.js is loaded
openAppStoreCTA uses TikTok's openAppStore()

WebGL & audio compliance

WebGL2 upgrade guard

Some mobile browsers only expose WebGL1 even when WebGL2 is available. Playable Kit automatically patches HTMLCanvasElement.prototype.getContext to transparently upgrade webgl and experimental-webgl requests to webgl2 — improving shader compatibility on Android Chrome without any code changes in your Unity project.

AudioContext resume guard

Mobile browsers block AudioContext playback until a user gesture occurs. Playable Kit injects a lightweight guard that queues all AudioContext.resume() calls and drains them on the first interaction (click, touchstart, pointerdown, or keydown). This prevents silent audio failure on iOS Safari and Android Chrome.

💡

Both guards are injected automatically when using single-HTML mode for Google Ads. No changes to your Unity project are required.


FAQ

Which Unity Luna versions are supported?

Playable Kit supports all Luna SDK versions that produce a standard ZIP with index.html at the root. Both WebGL1 and WebGL2 shader profiles are supported.

Can I use Playworks builds instead of Luna?

Yes. Any Playworks export ZIP with the standard structure works identically to a Luna export.

What happens if a check fails?

The conversion still completes and you can download the output, but the failed check is listed as an error in the report. Submitting a creative with a failed check may cause rejection by the ad network.

How does single-HTML inlining handle large assets?

Audio and video are compressed using fflate and stored as base122-encoded data attributes on hidden elements. They are decoded and attached via URL.createObjectURL at runtime, avoiding inline data URIs which are not permitted by Google Ads.

Can I automate conversions in CI?

Yes. Install the CLI (npm install -g playable-kit) and add a step to your GitHub Actions or GitLab CI pipeline. The CLI exits with code 0 on success and non-zero if any network has a failing check.

What is the maximum ZIP input size?

There is no hard limit on the input ZIP. Output size is constrained by the target network (e.g. 5 MB for Google Ads). Playable Kit will warn you if the output exceeds the network's limit.


Contact support

Can't find what you're looking for? Our team responds within one business day.

Email us at [email protected] or open a support ticket from inside the dashboard.