Portal Readiness Checker for HTML5 game builds
Check a zipped web game build in your browser: zip structure, size against each portal's published limits, SDK calls, external URLs, threads, mobile input and audio.
Drop a .zip of your web build here
The zip should contain your exported files with index.html at the top level.
Runs in this browser tab. Your build is not uploaded anywhere.
No build checked yet. Requires JavaScript; every check is listed below the tool.
Report
Size against published limits
- CrazyGamesfirst load up to 50 MB, total up to 250 MB
- Pokifirst load: 5 MB suggested, total: 8 MB suggested
- GameDistributionnot published
- GameMonetizenot published
- Y8not published
- itch.iototal up to 500 MB
- YouTube Playablesfirst load up to 30 MiB, total up to 250 MiB
Per portal
What it does
Export your game for the web, zip the output folder’s contents, and drop the zip above. The checker lists every file with its uncompressed size, looks inside the HTML, JavaScript, CSS and JSON files, and produces two things: a list of findings grouped by topic, and a per-portal table that compares your build with the limits and SDK requirements each portal publishes.
Every finding has a status (pass, warning, problem, note or unknown), the files and line numbers that triggered it, links to the documentation the check is based on, and a sentence on what the check cannot know. “Unknown” appears when a portal does not publish a limit: the checker never assumes one.
How it works
The page uses JSZip to read the archive in memory through the browser’s File API. File sizes come from the zip’s central directory, so large binary files such as .wasm, .pck or .data are measured without being decompressed. Text files are decoded and scanned with regular expressions. No part of the build is sent anywhere, and the tool reports nothing about your files to any analytics service. You can confirm this in your browser’s Network panel while a check runs.
The work runs in a background thread of the page (a Web Worker), so the page stays responsive, and Cancel check stops it at any time. Choosing another zip cancels the running check, so a slow, earlier archive can never replace the report of a later one.
Everything happens in your browser, so very large builds are limited by your device’s memory, and the tool sets limits: it refuses archives above 1 GiB or with more than 20,000 files, decodes at most 256 MiB of text in total, and measures but does not scan text files above 32 MiB or files whose stored size does not match their data. When any file could not be scanned, checks that depend on finding nothing (for example “no service worker”) report unknown instead of pass, and name the files.
What it checks
The full list with sources is below the tool. In short:
- Archive structure.
index.htmlat the top level of the zip, not inside a folder; no macOS__MACOSXfolders,.DS_Storefiles, source maps or source art; no root-relative paths like/assets/x.pngthat break when a portal serves the game from a sub-path. - Size. Total uncompressed size, file count and the largest files, compared with each portal’s published limits, including whether a limit means “under” or “at most”. For Godot and Unity exports it also estimates the files the engine loads before the first frame. Portals measure the initial download themselves while the game loads (YouTube Playables after HTTP compression), so those results are estimates to confirm in the Network panel, never failures. One published rule is applied exactly: without its SDK, CrazyGames counts the whole build as the initial download.
- SDKs. Whether the CrazyGames SDK v3, Poki SDK, GameDistribution SDK, GameMonetize SDK, Y8 SDK, Google Ad Placement API or YouTube Playables SDK is present, which of their documented calls appear in the code (calls inside comments do not count), whether SDKs for several portals are mixed in one build, and whether the Ad Placement API’s test mode is still on. CrazyGames is judged per launch stage: the SDK is optional for Basic Launch and required for Full Launch.
- External requests. Hard-coded URLs (including IP addresses and localhost) grouped by host, separated into SDK hosts, hosts only mentioned in comments, and everything else.
- Service workers and threads. Service worker registration, and builds that need
SharedArrayBuffer. Browsers only allowSharedArrayBufferon cross-origin isolated pages, and the Godot documentation notes that multi-threaded web exports need specific server headers, while its single-threaded export (Godot 4.3 and later) is more compatible with web publishers (Godot docs). - Mobile and input. A viewport meta tag, pointer or touch handlers, and
touch-actionCSS. - Audio. Autoplaying media elements and
AudioContextobjects that are never resumed. Browsers block audible playback until the user interacts with the page (Chrome autoplay policy). - Saving data.
localStorage, IndexedDB and cookie writes, with a note on how storage behaves inside a portal’s iframe.
What it cannot know
A static scan does not run your game. It cannot see requests built at runtime, the order in which SDK events fire, whether an ad break pauses your audio, how the game feels on a phone, or how a portal’s reviewers will judge quality and content. It also cannot measure the bytes players actually download, which depend on HTTP compression and on which files your loader fetches first.
Treat the report as a pre-flight list. Fix the problems, read the warnings, then test the build in each portal’s own QA tool or preview environment before you submit. The portal pages link to those tools where the portals document them.
Reading the per-portal table
Each portal row shows the checks that depend on that portal’s rules: total size, initial download (when the portal publishes a limit), file count, largest file, its SDK, other portals’ SDKs, and thread support. When a portal writes “MB” without saying whether it means 1,000,000 or 1,048,576 bytes, a build that falls between the two readings gets a warning instead of a pass. Each item links to the page the limit comes from, with the date we last checked it on the comparison page.
Every check, its sources and its limits
This list is generated from the same definitions the checker runs, so it always matches the tool. Portal-specific limits and SDK names come from the portal data, where each value links to the portal’s own documentation.
Archive structure
index.html at the zip root
Finds the entry file. Flags a build zipped together with its parent folder, a wrongly cased Index.html, or no HTML entry at all.
Based on: each portal’s published upload rules and size limits, linked from the per-portal results.
Cannot know: Checks file names only. A portal may also need a specific entry file name or folder layout in its upload form; follow the portal docs linked here.
Archive structure
Files players never need
Lists __MACOSX folders, .DS_Store, Thumbs.db, .git, source maps and source art files (.psd, .aseprite, .blend…), which add bytes and count toward file limits.
Based on: MDN: What is a URL? (absolute and relative URLs)
Cannot know: Name-based only. Source maps can be intentional; remove them if you do not want your source readable.
Archive structure
Root-relative asset paths
Finds src/href attributes starting with "/" in HTML. Portals serve builds from a sub-path or CDN folder, where such paths point at the wrong place.
Based on: MDN: What is a URL? (absolute and relative URLs)
Cannot know: Scans HTML attributes only. Root-relative URLs assembled in JavaScript are not detected.
Size
Total size, file count, largest files
Sums uncompressed sizes from the zip directory, lists the largest files, and for Godot and Unity builds estimates the files loaded before the first frame. Each portal row compares file-size limits exactly (respecting "under" versus "at most"); initial-download limits are measured by the portals at runtime, often after compression, so the checker reports them as estimates to confirm in the Network panel, never as failures. Exception: CrazyGames counts the whole build as the initial download when its SDK is absent, which the checker applies. CrazyGames is checked per launch stage: the SDK is optional for Basic Launch and required for Full Launch.
Based on: each portal’s published upload rules and size limits, linked from the per-portal results.
Cannot know: Sizes are uncompressed bytes from the zip directory. What players download depends on HTTP compression and on which files your loader requests; the checker cannot measure transfer size.
Portal SDKs
Portal SDK calls
Detects the CrazyGames SDK v3 (and older versions), the Poki SDK, the GameDistribution SDK, the Google Ad Placement API (adBreak/adConfig) and the YouTube Playables SDK, then lists which documented calls appear in the code. Warns when SDKs for several portals share one build. Calls inside comments do not count, and leftover test settings (Ad Placement API test mode) are flagged.
Based on: CrazyGames HTML5 SDK v3, Poki SDK, GameDistribution HTML5 SDK, GameMonetize HTML5 SDK, Y8 SDK, Google Ad Placement API (H5 Games Ads), YouTube Playables SDK
Cannot know: Static text search. It shows whether documented SDK calls appear in your code, not whether they run at the right time. Minified code renames the SDK object, so some calls are matched by method name alone and could match another library’s method of the same name. Obfuscated or engine-plugin integrations may use different names.
External requests
Hard-coded external requests
Extracts http(s), ws(s) and protocol-relative URLs (domain names, IP addresses and localhost) from HTML, JS, CSS and JSON, groups them by host, and separates SDK hosts and URLs that only appear in comments or licence headers.
Based on: Poki: Requirements (external requests, localStorage, ads), MDN: Cross-Origin Resource Sharing (CORS), MDN: <iframe> (sandbox, allow)
Cannot know: A URL in the code does not prove a request is made, and requests built at runtime (string concatenation, config files, inside .wasm/.pck) are invisible to a text scan. Test with the browser Network panel as well.
Service workers and threads
Service worker
Finds navigator.serviceWorker.register() calls, sw.js-style files and the service worker Godot adds when its PWA option is on.
Based on: MDN: Using Service Workers, MDN: ServiceWorkerContainer.register(), Godot docs: Exporting for the Web (thread support, PWA)
Cannot know: Detects registration calls and typical file names. Whether a portal allows a service worker depends on how it hosts and frames the game; most portals do not publish this.
Service workers and threads
SharedArrayBuffer and threads
Reads GODOT_THREADS_ENABLED from Godot 4 exports and looks for new SharedArrayBuffer(), Atomics.wait and Emscripten pthread markers. Threaded builds need a cross-origin isolated page (COOP + COEP headers).
Based on: MDN: SharedArrayBuffer, security requirements, MDN: Window.crossOriginIsolated, MDN: Cross-Origin-Embedder-Policy, Godot docs: Exporting for the Web (thread support, PWA)
Cannot know: SharedArrayBuffer only works when the page that runs the game is cross-origin isolated (COOP + COEP headers). The portal controls those headers, not your zip. A mention of SharedArrayBuffer in feature-detection code is not counted as usage.
Mobile and input
Viewport meta tag
Checks index.html for <meta name="viewport"> with width=device-width, and notes when zoom is disabled.
Based on: MDN: <meta name="viewport">
Cannot know: Reads index.html only. A viewport tag added at runtime by a loader script is not seen.
Mobile and input
Touch and pointer input
Looks for pointer or touch event handlers, mouse-only handlers, and touch-action CSS. Engines that map touch input themselves are reported as such.
Based on: MDN: Pointer events, MDN: Touch events, MDN: touch-action
Cannot know: Looks for event names in code. It cannot tell whether every control works with touch, or whether on-screen controls exist for keyboard-only actions. Test on a real phone.
Audio
Audio after a user gesture
Flags <audio>/<video> autoplay attributes and AudioContexts that are never resumed. Browsers keep audio suspended until the player interacts with the page.
Based on: Chrome for Developers: Autoplay policy, MDN: Autoplay guide for media and Web Audio APIs, MDN: AudioContext.resume(), MDN: User activation
Cannot know: A static scan cannot see when code runs. It flags autoplay attributes and AudioContexts without any resume() call; it cannot prove that sound starts only after a tap or key press.
Saving data
localStorage and other browser storage
Counts localStorage, indexedDB.open() and document.cookie writes, and explains how storage behaves inside a portal iframe.
Based on: MDN: Window.localStorage (exceptions), MDN: Storage quotas and eviction criteria, MDN: State partitioning, Poki: Requirements (external requests, localStorage, ads)
Cannot know: Counts storage API references. It cannot tell whether each access is wrapped in try/catch or what size your save data reaches.
Questions
- Is my game uploaded when I use the checker?
- No. The page reads the zip with JSZip inside your browser tab and runs every check there. The page makes no network request with your file, and closing the tab discards it.
- Does a clean report mean a portal will accept my game?
- No. The checker only inspects files statically. Portals also review gameplay, quality, content and how SDK events behave at runtime, which a file scan cannot see.
- Which engines does it understand?
- Any web build that is a zip of HTML, JavaScript and assets. It recognises Godot, Unity, Construct 3, GDevelop, Defold, Phaser, PixiJS, three.js, PlayCanvas and Babylon.js from their file names or code markers, and adjusts some checks when an engine handles input and audio itself.