The official Godot integration for prototypes hosted on Prototir. The addon connects browser and native exports to lifecycle signals, analytics events, scores, and feedback. Browser exports also use persistent SDK storage, managed text generation, and screenshot feedback. Native releases pair with a tester's account and report over HTTP. The editor dock checks browser export settings so unsupported profiles are caught before upload.
- Godot 4.3 or newer
- GDScript integration
- Browser: Compatibility renderer, single-threaded Web export using the standard runtime profile
- Native: Windows, macOS, and Linux release exports, with device pairing
Download the addons/prototir directory from the
v0.2.1 release and copy it into your
project. Then:
- Enable Prototir SDK under Project Settings > Plugins.
- Open the Prototir dock.
- For browser exports, apply safe fixes and resolve every browser-profile blocking item.
- Use the appropriate Export for Prototir command for a browser or native release.
Native builds do not need
index.html; see Native builds.
The addon installs Prototir as an autoload. Pin a published release tag for reproducible projects.
Browser exports use the host connection. Native releases must pair before sending sessions or text feedback; see Native builds. Storage and AI in this example are browser features; native calls use local mocks.
Prototir.ready()
Prototir.event("level_complete", {"level": 2})
Prototir.score(1200)
var storage_request := Prototir.storage_get("difficulty")
var difficulty = await storage_request.completedManaged AI requests expose explicit success and failure signals:
var request := Prototir.ai_generate("Give the player a short quest hook.", 80)
request.completed.connect(func(text): print(text))
request.failed.connect(func(code, message): push_warning("%s: %s" % [code, message]))Call ready() after the first genuinely interactive frame. Event names are normalized to lowercase
and accept letters, numbers, _, ., :, and -. Keep payloads small and free of personal data.
The dock starts with Building for: Web | Native. The choice is saved per project in the editor's project metadata, and until you pick it follows your export presets.
- Every build: Godot version and main scene.
- Web: Compatibility renderer, Web export preset, threads, GDExtension, PWA output, adaptive canvas resizing, initial focus, entry filename, and mobile texture compression.
- Native: an export preset for the machine you are on. None of the Web rules apply.
Blocking issues prevent a supported release; recommendations remain visible when a choice depends on the project. Safe fixes never enable mobile texture compression automatically because its size and quality tradeoff must be tested by the creator.
After export, the addon copies the project's root prototir.json beside index.html. If no source
manifest exists, it generates valid defaults from the project name and exact Godot version.
Run the structural validator outside Godot with:
node tools/export-validator.mjs /path/to/exportThe Prototir sandbox supplies a CSP-safe JavaScript interface. The addon retrieves that interface
with JavaScriptBridge.get_interface() and never executes inline JavaScript or requests
unsafe-eval. The Editor emits mock signals and can test pairing, but never reports development
play as a real session. Native releases report Ready, events, scores, sessions, and text feedback
after pairing. SDK storage remains in memory outside Web exports, and managed AI requires an
explicit local mock_ai_handler; use your own save files for persistent native state.
Call review_enable to give testers a floating feedback button in Web exports. They capture the
current view, drop a pin on that screenshot and write a comment.
func _ready() -> void:
Prototir.review_visibility_changed.connect(_on_review_visibility)
Prototir.review_enable("orbit-garden", "v1.4.0", "bottom-left")
func _on_review_visibility(open: bool) -> void:
get_tree().paused = openreview_enable(project, build, corner, launcher, theme) takes a stable project identifier (reviews exported from
another project are refused on import), a build recorded with the feedback, and a corner of
bottom-left (default), bottom-right, top-left, or top-right. review_disable() removes the
overlay. launcher is auto (Prototir draws the control on its own surfaces), watermark (always
show the Prototir mark and its menu) or host (draw nothing). theme is auto, light or dark.
Off Prototir the mark opens an icon menu with Screenshot and Review files while offline, or Screenshot and Comments when connected to Prototir. Open on Prototir appears when configured. Choosing Screenshot captures the frame and opens the focused composer; Review files contains import and export.
Connect review_visibility_changed and pause while the panel is open, otherwise the game keeps
consuming the input the tester is typing into their comment.
Screenshots come from the viewport after RenderingServer.frame_post_draw, so they match the
rendered frame. The export plugin bundles the browser runtime with the export, which is what lets
this work off Prototir; re-export with the addon enabled after upgrading.
For browser exports hosted elsewhere, review_enable accepts api_base and slug for the
browser panel's connection. Without that connection, the browser panel saves review files offline.
review_enable warns and returns on non-Web exports. On Prototir the feedback becomes an ordinary
comment on the prototype, after Prototir's own confirmation dialog. In a Web export you host
yourself the panel saves a feedback.prototir-review.json file that the tester sends you and you
reload with Import review. See the
Web SDK README for the file format and its
limits.
A native export pairs through a code approved at prototir.com/link, receiving authorization for
one prototype. After approval, call Prototir.send_feedback(text) from your own comment UI.
The browser review_enable screenshot overlay does not run in a native export.
The addon reuses the pairing until it is revoked or expires. Keep the tester's draft on send failure and offer a retry. Testers can disconnect a build from their Prototir account settings.
A Web export takes everything from the page around it: the visitor is already signed in, and the shell watches the prototype and reports for it. A native build has none of that, so the addon does it itself.
You do not configure the slug. Prototir writes it into the .zip as you upload the build, into a
prototir-prototype.json beside the executable, and the addon reads it from there. The slug does
not exist until the prototype does, so there was never a value you could have put in your first
export.
Override it in Project Settings > Prototir > Prototype Slug when you need to: a build you ship
outside Prototir, or an installer Prototir cannot write into. Call Prototir.configure("your-slug")
if your game decides it at runtime. The injected slug wins over the project setting, because it
travelled with that exact download. Then:
The v0.2.1 release includes a built-in pairing screen:
func _ready() -> void:
Prototir.ready()
if not Prototir.is_paired():
Prototir.show_pairing_screen()It draws the code large, rasterises the QR, offers Open in browser and Copy code, and closes itself once the tester approves. Use it as it is, restyle it, or ignore it entirely.
Drawing your own is still fully supported, and is the right answer as soon as your game has a look of its own. The addon hands you everything and takes no opinion:
func _ready() -> void:
Prototir.pairing_started.connect(_show_code)
if not Prototir.is_paired():
await Prototir.begin_pairing()
func _show_code(request: Dictionary) -> void:
# request.code, request.verification_url, request.qr_svg, request.prototype_title
$Code.text = request.codeGive the tester a way to act on it. A game window has no selectable text, so a printed URL on its
own leaves them retyping it off a screen: offer OS.shell_open(request.verification_url) on
desktop, and DisplayServer.clipboard_set(request.code) or the QR where a browser on this machine
helps nobody, such as a headset. request.qr_svg is SVG text, which
Image.load_svg_from_string() turns into a texture.
examples/pairing/ builds the same flow with unstyled controls, if you would rather start from
something plain than restyle the shipped screen.
ready(), event() and score() accumulate one session rather than one request each, and the
addon reports it for you every 30 seconds while the game runs, and again when the window loses
focus. You do not have to call anything. Prototir.flush_session() is there for a natural break,
such as the end of a run, if you want the numbers to land sooner.
Repeating costs nothing: the first report returns an id the rest carry, so the server updates one
row rather than counting a play per report. When the window closes there is no time left to send,
so whatever the last report missed is written to user://prototir/pending and goes out the next
time the build starts. That covers the tester playing on a train as well. Prototir.send_feedback("...") posts a comment as the tester who approved the
build; no session is needed first, because approving the pairing is the stronger signal.
Pairing works when you run from the editor, so you can build the screen without exporting every time. Reporting does not: an F5 run is not a play, and counting it would put your own testing in your own numbers.
Nothing here reaches a Web export. Prototir > Export for Prototir (Web) adds
addons/prototir/native/* to that preset's exclude filter, and the Native button excludes
addons/prototir/web/*, so each build carries only the transport it can use. You can see and change
both in Project > Export > Resources > Exclude.
Two entries under Project > Tools:
- Prototir: Export for Prototir (Web) runs the Web preflight, exports, and zips the result.
- Prototir: Export for Prototir (Native) exports for the machine you are on.
Both produce a ZIP ready to drop on the upload page, and both write prototir-build.json beside the
build. Prototir records that id from the archive, and a running build reports the same id when it
pairs; a match shows the build running is the build that was uploaded, and nothing more. Both sides
come from a file you control, so it is not verification, security or anti-cheat. It catches an old
build being run against a new upload.
Exporting for the other desktop platforms needs their export templates, so those stay in the normal Export dialog rather than behind a button that would produce a build nobody can run.
node tools/test.mjs
node tools/test-godot-runner.mjs
node tools/run-godot.mjs --headless --path . --import
node tools/run-godot.mjs --headless --path . tests/validate_scripts.tscn --quit-after 600
node tools/run-godot.mjs --headless --path . tests/run_tests.tscn --quit-after 600The first checks structure. The project scenes validate every script with the Prototir autoload
available, then run the pairing flow, session recorder, and built-in pairing UI against fake HTTP,
a fake clock, and fake signals. Nothing requests a real pairing code. Set GODOT_BIN to the engine
executable if it is not on PATH. The runner fails on script errors even if Godot exits zero.
A tagged release must additionally open without script errors in Godot 4.3+, produce a release Web export, pass the structural validator, and play in the real Prototir sandbox.
Call Prototir.show_feedback_screen() from your game's Feedback button. The desktop screen retains unfinished comments during this run and failed posts, and offers browser pairing when needed. After pairing the tester reviews the draft and presses Post. A retry reuses the submission ID, and comments pass the existing API text checks. The returned screen emits closed for custom pause handling.
These flat overlays are not headset UI. VR projects should use their own interface and the pairing signals / send_feedback. Native screenshots are not provided.