Build and publish a browser-native prototype.

Use HTML, CSS, and JavaScript directly, or export a client-side project built with Three.js, Phaser, React, Svelte, Vue, Vite, or other browser tooling. Choose how you build below: setup, export, manifest, SDK and testing follow your choice.

Web documentation selected

01 / Web setup

Build with Web

Selected: Web

This section changes with the platform selector. Shared packaging, publishing, page, and community guidance stays below it.

01 · Build

Create a static client

Bundle application code, fonts, images, audio, models, and data. Use relative paths for local files.

02 · Integrate

Add platform features

Load the browser SDK for readiness, analytics, scores, storage, seeded randomness, and managed AI.

03 · Package

ZIP the built files

Place index.html at the ZIP root, or set entry in a root prototir.json. Upload only the files a browser needs at runtime.

Good fit

Choose Web when the browser is your native target

This is the most direct path for interface prototypes, browser games, visualizations, interactive articles, and projects already built with JavaScript or a client-side framework.

Current boundary

Use an engine when the project already depends on one

Do not rebuild an existing Unity or Godot project as hand-written Web code just for Prototir. Select that engine above and follow its supported Web export profile.

Install and connect

Prepare the Web integration

Load the small browser SDK before your application module. Prototir serves it from /prototir.js, so a published prototype follows the current SDK instead of freezing on whichever one it shipped with. Hosting the prototype yourself? Point at https://cdn.prototir.com/sdk/v0/prototir.js, which follows the newest 0.x release within minutes, or pin https://cdn.prototir.com/sdk/v0.4.0/prototir.js to stay on one version. Using a bundler such as Vite? Run npm install @prototir/web-sdk and import { Prototir } from it, and keep the script tag: on Prototir the import uses the current SDK the tag loads. Starters for Three.js, Phaser, PixiJS and Babylon.js are in github.com/prototir/web-examples. Making it in Construct 3? Install the Prototir addon from github.com/prototir/construct-addon and export for Web (HTML5).

index.html
<script src="/prototir.js"></script>
<script type="module" src="./app.js"></script>

First integration

Call ready only after the first meaningful interaction is possible.

app.js
Prototir.ready();
Prototir.event('scene_ready');

Supported profile

Design for the browser sandbox

  • Use local assets and relative URLs
  • Declare curated CDN modules in prototir.json
  • Keep JavaScript in external files when possible
  • Use Prototir storage instead of localStorage
  • Request pointer lock from a visitor action
  • Test touch, keyboard, Escape, resize, and fullscreen
  • Do not depend on arbitrary external APIs or sockets

Input and player behavior

Capture intentionally and always give the cursor back

Request pointer lock from a click or pointer action, react to pointerlockchange, and pause when lock is lost. Escape releases pointer lock and restores the cursor automatically.

Test keyboard, touch, focus, Escape, and fullscreen in the Prototir player
Never imitate pointer capture with cursor: none
Disable selection and touch gestures only on a game surface, never across an app by default
Pointer lock lifecycle
const canvas = document.querySelector('canvas');

canvas.addEventListener('click', () => canvas.requestPointerLock());

document.addEventListener('pointerlockchange', () => {
  const locked = document.pointerLockElement === canvas;
  game.setPaused(!locked);
});

// Escape releases pointer lock and restores the cursor automatically.
Optional game-surface CSS
.game-surface,
.game-surface * {
  user-select: none;
  -webkit-user-select: none;
  -webkit-touch-callout: none;
  -webkit-tap-highlight-color: transparent;
}

.game-surface {
  touch-action: none;
}

Text selection and standard page gestures remain enabled by default. Apply this to the gameplay surface only; keep instructions, inputs, and readable app content selectable and scrollable. This styling does not require the Prototir SDK.

02 / Start

Export first, then package the files people will run

Prototir hosts finished client files, not a source repository, editor project, or development server. Browser builds need an HTML entry and their runtime assets in a ZIP. Native builds need the executable and its runtime files, packaged for each supported platform and architecture. You can publish either format or both on one prototype page.

Example · Web build output

Web archive root
my-prototype/
|-- index.html
|-- app.js
|-- styles.css
|-- assets/
|   `-- cover.webp
`-- prototir.json
  • Build or export the release for the platform you are publishing.
  • ZIP the web output folder's contents, keeping the HTML entry at the archive root or declaring it in prototir.json.
  • Keep browser assets local and paths relative. Arbitrary sockets and undeclared services are blocked by the sandbox.
  • Use the runtime-specific Prototir SDK for session signals. Native builds also need a pairing UI; browser builds use the page's session.
  • Each uploaded file must fit the plan limit: Free 250 MB, Pro 500 MB, Team and Enterprise 1 GB. The same limits cover browser ZIPs and native builds.

Web is selected. Changing the platform above updates this export, setup, manifest, SDK syntax, and testing guidance together.

03 / Build

Describe the browser export and its device needs

A root-level prototir.json lets the upload pipeline validate the correct runtime profile, entry, devices, permissions, thumbnail, and platform features. The Unity and Godot SDKs generate it during export; Web creators can write it directly.

Web prototir.json
{
  "entry": "index.html",
  "thumbnail": "auto",
  "devices": ["desktop", "mobile"],
  "orientation": "any",
  "runtime": { "engine": "web", "profile": "standard" },
  "modules": ["three@0.170.0"],
  "permissions": [],
  "ai": { "mode": "disabled" }
}
fieldaccepted valuespurpose
runtimeweb, unity, or godot · standard profileDeclares the engine and exact engine version so Prototir can validate the exported shape.
entryrelative HTML pathUse a generated entry outside the root, which Prototir copies to the served root.
thumbnailauto, or a bundle-relative image pathauto captures the first available visual frame after upload, including an intro or cutscene. A bundle-relative PNG, JPEG, or WebP travels with the ZIP and prototir.json, bypasses runtime capture, and is the deterministic choice when the representative scene appears later. Alternatively, upload a Prototir-hosted custom image (up to 2 MB); it overrides the portable source until you switch back in Studio. The last source you save wins. Use 1280×800 (16:10) where possible. Cards center-crop it to 16:10 and 16:9.
modulesWeb builds · exact name@version specsInjects a validated import map for browser-code projects. Unity and Godot dependencies must be included by their exporter.
permissionscamera, microphoneLets the player ask the visitor before granting device access.
devicesarray of desktop, mobile, xrDeclares every supported device target. Legacy desktop, mobile, and both strings remain accepted.
xrmodes and WebXR featuresRequired when devices contains xr; controls explicit WebXR capability delegation.
orientationany, portrait, landscapeShows the intended viewport orientation.
aidisabled, managedEnables Prototir's provider-neutral managed gateway. API keys, provider names, and model ids do not belong in the manifest.

The curated Web module catalog appears below the SDK section on this page.

Desktop

Supported by every browser build path. Test keyboard, pointer, focus, audio, resize, and fullscreen.

Mobile

Supported by every browser path when the experience has touch controls, responsive layout, and a tested orientation.

XR

Use only a target guide that explicitly supports your WebXR stack. Declare xr devices, modes, and features before requesting a session.

04 / Build

Report what happened

Web, Unity, and Godot use APIs shaped for their runtime. Capabilities differ between browser and native builds. The selector above is currently showing Web. Call Ready only after the first meaningful interaction is possible. For browser builds, preview generation does not wait for Ready. Native-only publications require a cover image.

JavaScript quickstart
Prototir.ready();
Prototir.event('level_complete', { level: 2 });
Prototir.score(1200);

const random = Prototir.rng('daily-2026-08-24');
CapabilityKindWhat it can do
ReadyfunctionStarts a real session after the prototype becomes interactive.
EventfunctionRecords a small, non-personal milestone. Its normalized name, sessions reached, and total triggers appear in creator analytics. Use stable names made from letters, numbers, _, -, ., or :.
ScorefunctionReports the current numeric score.
StoragememberContains asynchronous get, set, and remove functions for browser-local strings scoped to the prototype and player, with up to 64 keys and 64 KiB of UTF-8 data per value.
Managed AImemberProvider-neutral generation when managed AI is enabled for the browser build.
Seeded RNGfunctionReturns deterministic local randomness. Currently exposed by the browser SDK.

Managed AI

Managed AI is a Pro-and-above feature, and it runs on your plan's daily allowance, never a visitor's. Creators and players do not supply provider keys or pick a vendor. Prototir selects the provider and model behind the tier you choose, automatically falling back to another provider if one is briefly unavailable, scans prompts and replies, and shows an AI-powered disclosure in the player shell. Prompt text leaves the device.

JavaScript storage
await Prototir.storage.set('difficulty', 'hard');
const difficulty = await Prototir.storage.get('difficulty');
await Prototir.storage.remove('difficulty');
JavaScript managed AI
try {
  const answer = await Prototir.ai.generate({
    prompt: 'Give the player a short quest hook.',
    maxTokens: 80
  });
} catch (error) {
  console.warn(error.code, error.message);
}
  • prompt is required and limited to 32,000 characters.
  • maxTokens is optional, must be positive, and is capped at 4,096 output tokens or the remaining allowance.
  • The call rejects with { code, message }. Handle sign_in_required, quota_exceeded, team_fair_share_exceeded, prototype_allowance_exceeded, ai_rate_limited, ai_blocked, moderation errors, provider_error, and timeout.
  • Calls are stateless. Prototir does not maintain a conversation, tools, retrieval, or long-term memory for the prototype.
  • Browser-local inference is not part of this capability yet. Use managed AI for consistent device support.

The rest is configured on the prototype's own page, not in code, under "AI (Prototir.ai)": mode, model tier (fast, cheap and low-latency, or quality, higher-capability and costlier), whether a visitor must sign in to use it, and an optional daily token cap per visitor on top of your own budget. Defaults (managed off, fast tier, sign-in required, your full daily budget per visitor) are chosen to be safe and cheap out of the box. Turning off sign-in lets anonymous visitors use AI too, metered per visitor by IP instead of by account. You still pay either way; there's just no account to individually rate-limit, so it's a frictionless-demo versus coarser-abuse-protection trade.

Fast and Quality each have their own included daily allowance, sized for what they actually cost to run, not one shared number. Once a day's allowance for the tier you're using is spent, AI keeps working out of any purchased AI credits (one-time top-ups you buy from the same panel). Once both are spent, managed AI pauses until the next daily reset, or more credits. A Team org's daily allowances and credits are shared across every prototype the org owns.

Useful events

First action, onboarding completion, important tool use, level completion, creation/export, retry, and a clearly named abandonment point.

Keep telemetry safe

Use stable short names and small values. Never place names, email addresses, free-form messages, secrets, or other personal data in event payloads.

Web only / Curated modules

Reuse browser dependencies without rebundling them

Add exact name@version specs to prototir.json. Prototir validates them, injects the import map, and serves shared files from its CDN. Unity packages and Godot addons belong in their engine projects instead.

Starter modules

Small Prototir-built scaffolds imported into Web code.

modulesizeneedsstatuswhat it does
prefab-audio@0.1.1 2KB-availableGame/app audio over raw Web Audio: load samples from your bundle, play with volume/rate/loop, zero-asset beeps. Replaces maintenance-mode audio shims.
prefab-audio-features@0.1.1 2KBmicavailableAudio-reactive features from a native AnalyserNode: rms, energy, spectral centroid + raw bins. Mic (with consent) or any AudioNode/MediaStream.
prefab-sketch@0.1.1 1KB-availableThe byte-light creative-coding loop: full-window 2D canvas, dpr scaling, resize, draw(ctx, {t, dt, mouse}), p5 ergonomics at ~1KB.
prefab-hand-controls@0.1.2 7.6MBmediapipe-vision · cameraavailableHand landmarks → pointer/pinch events; camera + MediaPipe HandLandmarker + its model weights bundled in, no separate model pack needed. Reported pointer position is smoothed (configurable) to cut per-frame detector jitter without adding lag to pinch detection.
prefab-voice-input@0.1.0 -model-whisper-tiny · micplannedPush-to-talk → local transcript events.
prefab-i18n@0.1.1 3KB-availableLocalized strings and voice-over from your own bundle: Intl plurals/number/date, per-locale lazy loading, audio fallback chain.
prefab-input@0.2.0 --availableOne input API for desktop and mobile: keyboard, or fully customizable virtual controls, any number of buttons/joysticks/d-pads with position, size, shape and short/long-press semantics; same axes/pressed()/onPress() reads either way.
prefab-shader-canvas@0.1.1 --availableFull-screen fragment shader boilerplate: uniforms for time/mouse/resolution.
prefab-map@0.1.6 3640.0MB on CDN, streamedmaplibre-glavailableEmbeddable vector map: markers, popups, GeoJSON overlays, light/dark/grayscale themes, click/fly-to/pan-and-zoom. Real OpenStreetMap-derived roads, water, and towns worldwide, up to zoom 10 anywhere on Earth - no external tile host or API key, no CSP change, mandatory OSM/Protomaps attribution. The basemap is served on demand over ranged CDN fetches, like a shared image or model asset, not downloaded in full by every prototype that uses it.

Capabilities

Platform features brokered by the SDK rather than import-map downloads.

modulesizeneedsstatuswhat it does
capability-ai@sdk SDK-availableProvider-neutral managed AI generation through Prototir.ai. The sandbox has connect-src 'none'; requests hop through the shell to a metered, moderated gateway. Prototir owns credentials and routing, while the prototype owner's paid plan and AI credits fund usage; signed-in visitors never spend their own allowance.
capability-storage@sdk SDK-availablePer-prototype key-value persistence. Opaque-origin iframes have no reliable localStorage; the shell stores device-local values per prototype and isolates signed-in players on shared browsers.
capability-permissions@sdk SDK-availableCamera/microphone: declared in prototir.json, granted via iframe allow= after an explicit shell-level user consent prompt.

Libraries

Third-party browser libraries served from the Prototir CDN and cached across prototypes.

modulesizeneedsstatuswhat it does
three@0.185.1 404KB-available3D rendering, the workhorse for game/art prototypes. (r185 splits into module+core files, both platform-served; r170 stays available.)
pixi@8.6.0 180KB-availableFast 2D WebGL renderer.
phaser@4.2.1 1.4MB-availableBatteries-included 2D game framework (scenes, tilemaps, arcade physics). (Phaser 4; the 3.87 API stays available as phaser@3.87.0.)
p5@2.0.2 309KB-availableCreative coding (p5 2.x). For byte-light sketches see prefab-sketch.
rapier3d@0.19.3 1.4MBwasmavailable3D physics (WASM). Pairs with three. (0.19 fetches its .wasm beside the module instead of embedding it; 0.14 stays available.)
matter@0.20.0 26KB-availableLightweight 2D physics (no WASM).
tone@15.0.4 81KB-availableWeb Audio synthesis & scheduling.
lil-gui@0.20.0 14KB-availableTiny tweak-panel for exposing prototype parameters.
motion@12.42.2 46KB-availableAnimation library (vanilla Motion One API).
chart@4.4.7 69KB-availableCharts for dashboard/tool prototypes.
d3@7.9.0 93KB-availableData-driven documents for bespoke viz.
maplibre-gl@6.0.0 138KB-availableInteractive vector maps (WebGL). The base for prefab-map; import it directly for custom map styling/layers beyond what the prefab exposes.
simplex-noise@4.0.3 3KB-availableProcedural noise.
onnxruntime-web@1.27.0 3.5MBwasmavailableONNX inference runtime (WASM + WebGPU), the base for model packs.
transformers@3.2.0 800KBwasmplannedPipelines over ONNX: detection, segmentation, depth, ASR, embeddings.
mediapipe-vision@0.10.18 9.8MBcamera · wasmavailableHands / pose / face landmarks, realtime body-driven interaction.

Model packs

Weights and data for a runtime library, shared across compatible Web prototypes.

modulesizeneedsstatuswhat it does
model-rfdetr-nano@1.0.0 30.0MBonnxruntime-webplannedRF-DETR nano object detection weights, real-time DETR in the browser. (YOLOv8 excluded: AGPL.)
model-whisper-tiny@1.0.0 40.0MBtransformers · micplannedLocal speech-to-text, voice-controlled prototypes with zero API cost.
model-depth-anything-small@1.0.0 50.0MBtransformersplannedMonocular depth from the webcam, parallax/AR-ish effects.

05 / Build

Sessions, events, and scores

A session is one play. Prototir counts it as real once it lasts at least three seconds and reports at least one event, and almost everything you care about is built on real sessions rather than on opens.

That threshold is also the reason a prototype can look unplayed while people are clearly playing it. A build that never calls Ready and never reports an event has no real sessions, so its analytics stay near zero and it has no real use to rank with. Browser comments require real play; native feedback from an approved device uses its pairing authorization.

Ready

Starts the clock. Call it on the first frame the visitor can actually do something, not when loading finishes: a two minute cutscene before any input is not two minutes of play. Calling it twice does not restart anything.

Event

Marks a milestone. One event is what turns an open into a real session, so report the first meaningful action early rather than only at the end of a level nobody reaches.

Score

Reports a number. The best score of the session is the one kept, so a run that ends badly does not erase what the player achieved. Challenge leaderboards are built from scores on real sessions.

Naming events

Names are lowercased and must be 1 to 64 characters of letters, numbers, _, -, . or :. Anything else is dropped rather than corrected, so Level Complete records nothing while level.complete records what you meant.

Pick names once and keep them. A name that changes between builds splits one behaviour across two rows and makes the before and after impossible to compare. Prototir keeps up to 50 distinct names per session; past that the events still count towards the total, but the per-name breakdown stops growing, which is what stops one runaway loop from becoming your entire analytics page.

What you get back

Your prototype's analytics shows each event by name with two numbers: how many sessions reached it, and how many times it fired in total. The two together are the useful part. A step reached by few sessions is a wall; a step fired many times within few sessions is a retry loop. Duration buckets sit beside them as a rough drop-off curve.

In a native build this works differently

A web build runs inside the shell, which watches it and reports for it. A native build has no shell, so the SDK keeps its own count and sends one session for the play instead of a request per event. The SDK reports periodically and when the window loses focus. You can also flush at a natural break; unsent data is retained for the next launch.

A native build also reports nothing until a tester has paired it, because until then there is nobody to attribute the play to. Running from the editor never reports: your own testing does not belong in your own numbers.

06 / Build

One prototype, several builds

A prototype is one page with one set of comments, and it can be reached in more than one way: played in the browser, run as a native build on Windows, macOS or Linux, or both at once. Each of those is a build, they sit side by side, and none of them is the main one.

There is no mode to choose

The publish form starts empty with one Add a build button, and the browser build is the first option in the same list as the rest. What you add decides how the prototype is delivered: a browser build alone plays on the page, platform builds alone are native only, both gives you both. Nothing is a one-way door, and the rest can be added later from Studio.

A cover image is required

For a web prototype we take a screenshot of it running. A native build never runs on our machines, so there is nothing to capture. Your cover is the only picture the prototype has, on its page and everywhere it is listed.

Naming the architecture

Each platform build says which processor it is for, because a visitor handed the wrong one gets an app that will not start. The words differ per platform, on purpose: they are the words owners of those machines use for their own hardware.

Windows and Linux. x86-64 for the usual desktop processor, ARM64 for machines like a Snapdragon X laptop, and Any 64-bit for one file that runs on either.

macOS. Intel and Apple Silicon, which is how Apple splits its own machines and what About This Mac says, plus Universal for a binary holding both.

We do not say "Intel or AMD" for the desktop processor. That names two companies for what is really an instruction set: other vendors make chips that run it, and a Snapdragon X laptop is a Windows machine with neither company inside it.

What Prototir does not do

A web prototype runs inside our sandbox and we are accountable for containing it. A native build runs on the visitor's own computer, where we have no such control, and the prototype page says so plainly rather than leaving people to assume otherwise.

We do not sign or notarize your build, and we do not broker anyone else doing it. Expect Windows SmartScreen and macOS Gatekeeper to warn people before opening an unsigned app, and expect the occasional antivirus false positive on engine builds. Signing is yours to arrange if you want those warnings gone.

What Prototir checks when you upload

As the file arrives, Prototir looks for the SDK in it and writes your slug beside the executable. If it cannot find the SDK, it says so on the prototype page straight away, because a build without it can download perfectly and still report nothing, and there is no later moment when you would find that out.

Both of those are checks against a mistake, not against someone determined. A native build runs on someone else's machine where we can check nothing, and anyone who wanted to fake the reports could. We would rather say that than imply a guarantee we cannot make.

The slug can only be written into a .zip. An installer or a disk image gets uploaded as it is, and you set the slug in the SDK yourself.

The build id. Both export buttons write a prototir-build.json into the build, and Prototir records that id from the archive you upload. A running build reports the same id when it pairs, so a mismatch tells you a tester is playing an older build than the one on the page. That is all it does, and nothing depends on it: both sides come from a file you control, so it is not verification, not security, and not anti-cheat.

Adding builds also changes how people find you. Discover asks two separate questions: what device someone is holding, and how they want a prototype to reach them. A build for Windows, macOS or Linux puts you in the second one, under How you play › Native, and again under the operating systems you actually ship. Drop a platform in a later release and you leave that filter, because it matches the build on offer now rather than one you used to have.

Managing builds after publishing

Everything lives under Studio › your prototype › Builds: one collapsible panel per platform, the browser build among them rather than above them. Add a platform you did not ship at first, replace one, or remove one, at any time. If a browser build remains, removing the last native build leaves a browser-only prototype. Native builds have independent versions; replacing a browser build does not remove them.

Each build carries its own version

Platforms drift apart in practice: a Windows crash gets fixed the same evening while macOS waits for the next build. One version for the whole prototype would have to be wrong about one of them, so each keeps its own and the page shows which is which.

Replacing your browser build does not touch the others, and neither does rolling back to an earlier one.

Replacing one needs a new version

Comments and plays are recorded against the build they happened on, so "this crashes on launch" stays attached to the build it was true of. Two different files both called v1.2 would make that a lie, which is why Prototir asks you to move the number.

The build you replace is kept, so you can put it back at any time. Anything older than that is deleted for you, and there is nothing to decide or tidy up: once several platforms each have their own history, choosing per replacement means nobody can tell which builds are still taking up space. Records outlive the files either way, so the feedback on a build you replaced stays attached to it.

Because feedback knows which build it came from, the comments on a prototype page can be narrowed to one platform, and every comment says whether it came from the browser or from a particular build. A crash report from Windows v1.2 is a different fact from the same words typed in a browser, and the page no longer makes you guess which you are reading.

07 / Publish

Design for an unfamiliar visitor

First ten seconds

Show what can be touched, clicked, typed, dragged, or controlled. Avoid an unexplained blank canvas or loader with no progress.

Viewport

Fill the available player, react to resize and fullscreen changes, avoid fixed desktop-only dimensions, and honor the declared orientation.

Input

Test keyboard, pointer, touch, focus, Escape, and visible controls using the APIs or input system of your selected runtime. Pointer lock must start from a visitor action and release cleanly.

Recovery

Give visitors a restart path, useful empty states, and a clear response when an optional permission, storage call, or AI request is denied.

Performance

Compress exported assets, defer secondary content, cap rendering cost, and test the first load on a real phone and ordinary connection.

Accessibility

Use readable contrast, semantic or accessible controls, labels, keyboard access, reduced-motion handling, and alternatives to audio-only or color-only information.

The selected platform's interaction details stay in context

The Web input, device compatibility, packaging, and testing checks are in the setup section above. Change the platform selector to replace them without opening another guide.

08 / Publish

Help the right people understand it

  • Title: identify the idea, not its internal build number.
  • Category: choose game, app, art, or showcase as the broad format.
  • Prototype tags: add up to five descriptors for mechanics, audience, technology, or subject. Existing normalized tags are reused despite case, spacing, or underscore differences.
  • Description: explain what to try, what is unfinished, intended device/input, and the question you need answered. Links open through Prototir's external-site warning.
  • Feedback tags: these are separate from prototype tags. They structure what commenters observed, such as confusing, delightful, slow, or would-use.
A useful description: “Try creating a two-stop route on mobile. The map is mock data. I want to learn whether the order controls are understandable without a tutorial.”

09 / Publish

Make reuse traceable

Only publish files you own or are allowed to distribute. Keep third-party license and attribution files in each build. Browser source download and templates apply to the browser ZIP; compiled native builds do not provide an editable engine project. If you enable browser source download, choose a license that actually grants the permissions you intend.

Browser source

Visitors can download the original browser ZIP under your stated license. Platform-generated provenance is added to the downloaded manifest.

Remix

Declare the source slug or re-upload a Prototir browser source download. The origin remains linked and conflicting lineage is rejected.

Template

A template is a complete browser starter prototype with source download enabled. Its downloads carry template provenance into later uploads.

Explore templates

10 / Publish

Protect the master, publish the runtime copy

Anything a browser renders is delivered to that browser. An authorized visitor can inspect network requests and save JavaScript, models, textures, audio, video, WebAssembly, and data. Private visibility prevents anonymous access, but it cannot stop an invited viewer from capturing files they are allowed to run. This is equally true for direct Web code, Unity WebAssembly, and Godot Web exports.

What browser build protection does

The browser publisher can rewrite served .js files into compact, less-readable code. This deters casual copying; it is not encryption or DRM. It does not transform WebAssembly, engine data files, .mjs, inline code, source maps, models, textures, or other assets. Unity and Godot already compile/package much of their runtime output, but that output is still downloadable. The retained original ZIP remains unchanged. Native builds are delivered as uploaded; arrange code signing or notarization yourself when required by the operating system.

How to publish valuable 3D work

Keep production masters outside the ZIP. Export a runtime derivative: remove editor data and source maps, reduce geometry and texture resolution, use formats such as GLB with Draco or Meshopt and KTX2 where appropriate, and consider a visible or forensic watermark. Compression and renamed files add friction, but do not make client-rendered assets secret.

Rule of thumb: if disclosure would cause serious harm, do not include that file in a browser-delivered prototype. Use a reduced derivative, a watermark, or a server-side rendering approach instead.

11 / Publish

Run the release checklist

  1. 1. Build locally. Open the production web output through a local static server, not directly from the filesystem.
  2. 2. Test the runtime. Check sandbox restrictions, externalize scripts, and remove undeclared network dependencies.
  3. 3. Package the build. ZIP the web output with its HTML entry, relative assets, manifest, and license files. Check the size of each file against your plan limit.
  4. 4. Complete the public page. Choose the publishing identity, category, tags, description, lineage, and rights confirmation.
  5. 5. Set publication behavior. Review visibility, comments, tips, and release details. Browser builds also have source/template, embed, badge, and JavaScript protection settings. Native-only builds require an uploaded cover image.
  6. 6. Verify after publishing. Prototir runs the browser candidate in the same opaque-origin sandbox and keeps it unlisted if startup throws an uncaught error. A failed Studio row opens a prefilled repair editor on the same prototype: it identifies the failed check with a stable diagnostic code, explains what to change, and lets you edit metadata, upload a replacement ZIP, or delete the prototype. New manifest values are shown as highlighted changes before the replacement is queued. Runtime-failing builds remain available to their owner and collaborators for testing while forced unlisted. After a build passes, try the player on supported devices and check permissions and analytics events.

Replace without spending another slot

A replacement is an internal build of the same prototype. It keeps the stable URL, comments, and aggregate analytics, and does not count as another Free, Pro, or Team prototype. For browser builds, the current version stays live while the candidate is tested. Native versions are managed independently for each platform and architecture.

Manage each build's history

Browser builds support rollback and cleanup after a replacement passes. Native builds keep their own versions, and replacing the browser build does not remove native builds. Comment attribution remains tied to the build used. Publish a separate public release only when you want another listing, URL, and prototype slot.

Open the publisher

12 / Grow

Feedback & tools for your testers

Every build that uses the SDK gets a Feedback & tools control, with nothing for you to add: Screenshot, Comment, Console and Performance. Testers point at what they mean and send what they saw with their comment.

Screenshot

Captures the moment without the control on it. The tester places a pin on the image and writes what they mean, because a seed and a timestamp do not reproduce a procedural scene.

Comment

A plain comment on the prototype, with the same moderation and wall controls as any other.

Console

Recorded from the moment the build starts, so the error a tester saw before opening the panel is already there. They can copy it or attach it to a comment, where it shows collapsed and capped to its last 200 lines.

Performance

A frame-rate chart with the slowest frame and memory, recorded only while the panel is open. A summary can be copied or attached to a comment.

A screenshot, a console log or a performance summary is always sent with a message: it is what the comment is about, never a comment on its own. Recording costs almost nothing while the panels are closed.

Where testers find it

Inside the Prototir player, Feedback & tools sits beside Restart and Fullscreen. In embeds it sits in the Prototir badge, which stays visible while feedback is on. On your own site, set project and the SDK draws the control itself.

Prototir.review.enable({ project: 'my-prototype' });

Turn tools off

Every tool is on by default. Switch any of them off, or pass false for none:

Prototir.review.enable({ tools: { console: false, performance: false } });

Call review.capture() from a key, or review.compose({ context }) when your game notices its own failure, and hand the tester a report already written.

Turning comments off for a prototype turns feedback off with it.