Your First Construct 3 itch.io Subdomain WASM Smoke Before Custom Domain - 2026
You enabled a custom domain on itch before the game loaded on yourstudio.itch.io/demo. Preview in Construct was fine. Press kit went out with play.brand.com. Facilitators sent blank canvas screenshots and CORS console lines—you spent the evening re-zipping exports that were never wrong.
July 2026 HTML5-first teams need one green URL on itch’s subdomain before DNS vanity. This Tutorials & Beginner-First evening pipeline proves wasm MIME, zero CORS errors, and a 60-second golden path on *.itch.io—then files cors_smoke_receipt_v1.json so custom domain work follows hosting strategy, not panic uploads.
Non-repetition note: The trend playbook owns subdomain vs custom policy. itch CDN MIME help owns post-upload octet-stream. This URL owns hands-on subdomain smoke steps for Construct. Planned help Construct CORS custom domain fix complements traceback fixes.
Why this matters now (July 2026)
- Custom domain rush — Marketing wants branded URLs before engineering proves wasm on subdomain.
- Triple-channel receipts — BUILD_RECEIPT needs
facilitator_url_canonicalon a host that actually runs Construct wasm. - CORS vs MIME — Teams fix MIME locally; production fails on origin split when skipping subdomain.
- Construct fest velocity — Roguelite seed ledger and NW.js freeze assume HTML5 itch lane is GREEN first.
- Facilitator Discord — Playtest links must be subdomain until
cors_smoke_receiptpasses.
Direct answer: Upload HTML5 zip to itch subdomain only tonight → curl -I wasm → DevTools CORS check → 60s play → cors_smoke_receipt_v1.json → then discuss custom domain with decision tree.
Who this is for
| Audience | Outcome |
|---|---|
| Beginner first itch HTML5 ship | One known-good URL |
| Producer | Pin facilitator link in Discord |
| Technical artist | MIME + CORS proof table |
| Solo dev | Stop re-uploading identical zips |
Time: ~2–3 hours first pass; 20 minutes per weekly demo refresh.
Prerequisites: Construct 3 project, itch.io account, browser with DevTools, curl (or PowerShell Invoke-WebRequest).
Before you start
- [ ] Read trend playbook split pattern—custom domain not required tonight
- [ ] Disable itch custom domain temporarily if already on
- [ ] Folder
release-evidence/html5/cors-smoke/ - [ ]
build_labelstring for title screen (e.g.html5-smoke-2026-05-25-rc1) - [ ] Pair with Wednesday smoke after receipt GREEN
Evening overview (five blocks)
| Block | Minutes | Output |
|---|---|---|
| 1 — HTML5 export | 30 | demo-html5.zip |
| 2 — Subdomain upload | 25 | Live studio.itch.io/... URL |
| 3 — MIME curl proof | 20 | wasm_mime_proof.md |
| 4 — DevTools CORS + play | 40 | Screenshots + golden path |
| 5 — Receipt | 15 | cors_smoke_receipt_v1.json |
Block 1 — Construct HTML5 export (Gate S1)
| Setting | Recommendation |
|---|---|
| Export | Web (HTML5) |
| Minify | On |
| Worker scripts | Note filenames for receipt |
| Version bump | Match build_label in project |
| Compression | Default; do not hand-edit wasm after export |
| Scirra arcade | Off for fest demos unless you intend Arcade lane |
Export zip; unzip locally once—confirm layout:
demo-html5/
index.html
data.json (or data.bin)
data.wasm # name may vary—log exact name
scripts/ # worker + engine JS
media/ # if referenced relatively
Common S1 failures:
| Mistake | Symptom on itch |
|---|---|
| Exported NW.js instead of HTML5 | Wrong runtime entirely |
Missing index.html at zip root |
404 shell |
Nested folder export/export/ |
itch upload shows empty |
| Forgot version text object | Cannot match facilitator bug reports |
Add a Text object BuildLabel on loader layout showing html5-smoke-2026-05-25-rc1—facilitators screenshot proof.
S1 pass: Zip lists wasm + worker; build_label visible in preview.
Outbound: Construct HTML5 export.
Block 2 — Upload to itch subdomain only (Gate S2)
- Create itch project → Kind: HTML
- Upload zip → wait upload complete
- Set visibility restricted for smoke if needed
- Copy URL:
https://YOURNAME.itch.io/GAME_SLUG— not custom domain - Do not enable custom domain until Block 5 receipt GREEN
S2 pass: Game page loads itch subdomain; no play.brand.com in address bar.
Block 3 — WASM MIME proof (Gate S3)
Open DevTools → Network → reload → click .wasm → copy Request URL.
curl -I "PASTE_WASM_URL_HERE"
| Header | Pass |
|---|---|
Content-Type |
application/wasm (not application/octet-stream) |
| HTTP status | 200 |
Log in wasm_mime_proof.md:
# wasm MIME — build html5-smoke-2026-05-25-rc1
- URL: https://...
- Content-Type: application/wasm
- pass: true
If octet-stream, follow itch CDN MIME help before continuing—MIME failure mimics “broken Construct.”
S3 pass: application/wasm on live CDN URL, not local server.
Block 4 — CORS + golden path (Gates S4–S5)
S4 — Console CORS (step-by-step)
- Open incognito window (reduces extension noise).
- Paste only
https://yourname.itch.io/your-demo— confirm hostname. - F12 → Console → clear log → hard refresh (Ctrl+Shift+R).
- Filter log level Errors only first pass.
- Expand any red line—note whether filename ends in
.wasmorworkermain.js. - Open Network → sort by Status — failed wasm rows are 200 with wrong type OR blocked.
| Result | Action |
|---|---|
| Zero CORS errors | S4 pass |
| CORS on wasm/worker | Stop—do not enable custom domain; read trend playbook |
| MIME error | Return to S3 |
| 404 on worker | Re-export zip; check nested folder |
Screenshot console with URL bar visible. Save HAR export if engineering needs async review.
Why incognito: Ad blockers and dev extensions sometimes mask real CORS/MIME errors—or inject false positives. Facilitators should use clean profiles too; document that in README snippet.
S5 — 60-second golden path
| Step | Pass |
|---|---|
| Loader reaches menu | Yes |
| Start / first input | Yes |
| One gameplay action | Yes |
| No freeze 60s | Yes |
Archive short screen recording optional; screenshot of gameplay sufficient for receipt.
Block 5 — cors_smoke_receipt_v1.json (Gate S6)
{
"schema": "cors_smoke_receipt_v1",
"build_id": "html5-smoke-2026-05-25-rc1",
"engine": "construct-3",
"host_lane": "itch_subdomain",
"page_url": "https://yourname.itch.io/your-demo",
"wasm_url": "https://cdn.itch.io/.../data.wasm",
"gates": {
"S1_export": "pass",
"S2_subdomain_upload": "pass",
"S3_wasm_mime": "pass",
"S4_no_cors_errors": "pass",
"S5_golden_path_60s": "pass",
"S6_receipt": "pass"
},
"wasm_content_type": "application/wasm",
"custom_domain_tested": false,
"facilitator_url_canonical": "https://yourname.itch.io/your-demo",
"promotion_allowed": true
}
Commit under release-evidence/html5/cors-smoke/. Set Discord pin to facilitator_url_canonical.
S6 pass: JSON committed; producer confirms pin matches.
Gates S1–S6 summary
| Gate | Fail blocks |
|---|---|
| S1 | Upload |
| S2 | All remote tests |
| S3 | Golden path (streaming) |
| S4 | Custom domain experiment |
| S5 | Fest facilitator link |
| S6 | BUILD_RECEIPT html5_host row |
PowerShell variant (Windows)
Invoke-WebRequest -Uri "WASM_URL" -Method Head | Select-Object -Expand Headers
Look for Content-Type key—same pass rules as curl.
Local preview trap
| Test | Proves |
|---|---|
| Construct preview | Export not corrupt |
python -m http.server |
Zip layout |
| itch subdomain | What players get |
Never file S6 pass from local server alone.
BUILD_RECEIPT row
{
"build_id": "html5-smoke-2026-05-25-rc1",
"channel": "itch_public",
"html5_host": "itch_subdomain",
"cors_smoke_receipt": "release-evidence/html5/cors-smoke/cors_smoke_receipt_v1.json",
"wasm_mime_receipt": "release-evidence/html5/wasm_mime_proof.md"
}
Align build_label with triple-channel help if GX lane exists.
When custom domain is allowed next
Only after S6 GREEN:
- Read CORS vs subdomain playbook decision tree.
- Choose split pattern (marketing custom + play subdomain) unless header plan documented.
- Run separate custom-domain smoke—do not overwrite
cors_smoke_receiptsubdomain pass. - File
cors_hosting_decision_receipt_v1.jsonwhen custom experiment completes.
Troubleshooting
| Symptom | Fix lane |
|---|---|
| White screen, no console | S3 MIME |
| CORS wasm blocked | Stay subdomain; playbook |
| Works once, fails later | CDN cache MIME help |
| Works desktop, fails mobile | Re-run S5 on phone browser |
| Custom domain only broken | Expected—finish this tutorial first |
Facilitator README snippet
## Playtest link (Construct HTML5)
- Use: https://yourname.itch.io/your-demo (subdomain)
- Do not use custom domain until producer posts cors_smoke GREEN
- Bug reports: attach Console screenshot + build_label
Pair with NW.js later
7-day Construct NW.js freeze is Steam lane—HTML5 subdomain smoke still required for itch/GX parallel demos.
Weekly refresh (20 minutes)
- Bump
build_label - Re-export zip
- Re-upload itch
- Re-run curl on wasm URL
- Spot-check S4 console
- Update receipt
build_idonly—keephost_lane: itch_subdomainuntil hosting decision changes
FAQ
Can I skip subdomain if custom is already live?
Disable custom for smoke night, or you cannot prove S4/S5 on canonical itch host.
Is GX.games the same steps?
Similar wasm/MIME discipline—add separate receipt row per GX tools resource.
Does this fix Steam builds?
No—NW.js/Steam is different export. HTML5 receipt still required for browser fest lane.
One wasm file name only?
Log actual name in receipt wasm_url—Construct versions differ.
Key takeaways
- Subdomain proof first—custom domain is not step one.
- MIME then CORS—order matters for Construct wasm.
cors_smoke_receipt_v1.jsonpins facilitator URL.- 60s golden path catches hangs MIME curl misses.
- Pair with hosting playbook before branded URLs ship.
Evidence folder layout
release-evidence/html5/cors-smoke/
cors_smoke_receipt_v1.json
wasm_mime_proof.md
devtools_console_subdomain.png
golden_path_screenshot.png
export_manifest.txt # unzip -l output
Thursday row review should list cors_smoke path beside wasm_mime—prevents promoting GX-only proofs while itch lane RED.
Browser smoke matrix (minimum)
| Browser | S4 CORS | S5 golden path |
|---|---|---|
| Chrome desktop | Required | Required |
| Firefox desktop | Required | Spot-check |
| Chrome Android | Recommended | Menu tap test |
| Safari iOS | If iOS is fest target | Loader + one input |
One browser GREEN is not enough for October facilitators—log matrix in wasm_mime_proof.md footer.
itch.io upload UI checklist (producer-friendly)
- Dashboard → Create new project (or open existing).
- Kind of project: HTML (not downloadable if you need browser play—match your fest intent).
- Uploads → Upload new file → select zip (under itch size limits).
- Wait until This file has been processed (not only uploaded).
- Embed options: fullscreen recommended for demos.
- Visibility: Draft/Restricted while smoke; Public only after S6.
- Copy View game link from subdomain—verify hostname is
*.itch.io.
Do not open Custom domain tab tonight.
MIME vs CORS decision table (beginners)
| Console message | Lane |
|---|---|
Incorrect response MIME type |
S3 → MIME help |
blocked by CORS policy |
S4 → stay subdomain; read playbook |
WebAssembly.instantiate failed |
Often MIME; sometimes corrupt wasm |
Failed to fetch network |
URL typo or adblock rare on itch |
Teaching facilitators this table reduces duplicate GitHub issues.
Integration with playtest isolation
Playtest isolation playbook separates Steam surfaces—HTML5 itch is its own surface_id:
{
"surface_id": "itch_html5_public",
"facilitator_url_canonical": "https://yourname.itch.io/your-demo",
"build_label": "html5-smoke-2026-05-25-rc1"
}
Do not paste Steam depot instructions into itch playtest threads.
Advanced: cors_origin_map stub (optional tonight)
If you already read the trend playbook, draft minimal map for later custom experiment:
{
"schema": "cors_origin_map_v1",
"lanes": [
{
"lane_id": "itch_subdomain",
"page_origin": "https://yourname.itch.io",
"same_origin": true,
"status": "production"
}
]
}
Custom lane stays out until subdomain S6 GREEN.
Producer calendar (fest week)
| Day | Action |
|---|---|
| Mon | Blocks 1–3 (export, upload, MIME) |
| Tue | Blocks 4–5 (CORS, receipt, Discord pin) |
| Wed | Demo smoke on canonical URL |
| Thu | Row review html5_host + cors_smoke |
| Fri | Evaluate custom domain per playbook |
Common Discord myths (correct gently)
| Myth | Truth |
|---|---|
| “Re-export fixes CORS” | Hosting origin fix; zip may be identical |
| “itch is down” | Often MIME or custom domain |
| “Works in editor” | Irrelevant to S6 |
| “Ad blockers only” | Check MIME/CORS first on clean profile |
Security note
Subdomain URLs are still public when visibility Public—smoke Restricted limits accidental traffic during MIME debug. Rotate build_label when promoting to fest so old bugs map to old builds.
Stretch goals (if time remains)
- Automate
curl -Iinscripts/check_wasm_mime.ps1 - Add itch URL to playtest tools form as required field
- Pair with Construct save migration if saves fail after HTML5 proof
wasm_mime_receipt_v1.json (pair with cors smoke)
When MIME was ever in doubt, also file:
{
"schema": "wasm_mime_receipt_v1",
"build_id": "html5-smoke-2026-05-25-rc1",
"wasm_url": "https://cdn.itch.io/.../data.wasm",
"content_type_observed": "application/wasm",
"post_upload_mime_ok": true,
"curl_timestamp_utc": "2026-05-25T20:15:00Z"
}
cors_smoke_receipt can reference this path in paired_receipts[]—auditors see both gates.
Two evening scenarios (pick yours)
Scenario A — First HTML5 ship (never uploaded)
Follow blocks 1→5 in order. Expect 2.5 hours. Custom domain tab stays untouched. Success = Discord pin to subdomain URL only.
Scenario B — Custom domain already broke production
- Disable custom domain in itch settings (or stop sharing custom URL).
- Confirm subdomain loads—if still broken, MIME lane (Scenario A blocks 1–3).
- Only after S6, re-read playbook before re-enabling custom.
- Do not delete receipts—add new
build_idfor fixed pass.
Scenario B teams often discover subdomain was always fine—press kit caused 100% of facilitator pain.
Godot teams reading this (cross-engine)
Godot Web exports use different file names but same gate order: subdomain proof → MIME → CORS → receipt. See Godot wasm memory playbook for runtime limits after hosting is GREEN.
Validation script sketch (optional)
# check_cors_smoke_receipt.py — run from repo root
import json, sys
r = json.load(open("release-evidence/html5/cors-smoke/cors_smoke_receipt_v1.json"))
assert r["gates"]["S6_receipt"] == "pass"
assert r["custom_domain_tested"] is False
assert "itch.io" in r["facilitator_url_canonical"]
print("cors_smoke_receipt OK")
Fails CI if someone flips custom_domain_tested early.
What not to do tonight
- Do not tune gameplay balance during smoke evening.
- Do not enable Steam Cloud on HTML5 lane.
- Do not compare to NW.js exe—different receipt family.
- Do not post “fixed” in Discord without
build_labelscreenshot. - Do not merge custom domain marketing PDF until producer signs S6.
Receipt reviewer checklist (producer)
- [ ]
facilitator_url_canonicalhostname is*.itch.io - [ ]
custom_domain_testedisfalse - [ ] All S1–S6 gates say
pass - [ ] Screenshot files exist in evidence folder
- [ ]
wasm_content_typeisapplication/wasm - [ ] Discord pin updated to match canonical URL
After S6 — custom domain experiment (separate evening)
Schedule second session for hosting playbook—new receipt cors_hosting_decision_receipt_v1.json, not a silent overwrite of tonight’s smoke pass. Marketing may publish brand landing same day only if split pattern links subdomain for Play button.
FAQ (extended)
itch says processing—can I test?
Wait for processed state; partial uploads fail S3 with confusing errors.
Multiple wasm files?
curl each; all must be application/wasm.
Embed on external site?
Out of scope—another origin; finish itch subdomain first.
Construct beta channel?
Log exact editor version in receipt notes field.
Whisper / OBS playtest same night?
Finish cors smoke first—facilitators need a stable HTML5 URL before VOD batch (OBS concat tutorial).