Web Export Guide
Web Export Guide
Grid Building supports web export for all three demo types: top-down, platformer, and isometric. This guide covers what you need to know to get your project running in a browser.
Requirements
- Godot 4.4+ with Web export template installed
- GDScript builds only (C# / Mono does not support web export in Godot 4.x)
export_filter="all_resources"or explicit inclusion of rules and template directories
How Resource Loading Differs on Web
Resource deserialization in exported builds (including web) can behave differently from the editor. Fields that are initialized in _init() may still be null if they are not explicitly saved in the .tres file, because:
- Deserialization order —
@exportvariables are set after_init()runs (godot#70575). - Typed array fragility —
Array[ExtResource("...")]([...])syntax in.tresfiles can deserialize as empty in exported builds (godot#97782, godot#72489). - Missing subresources — nested resources not explicitly serialized in the
.tresremain at engine defaults (null for Objects).
What this means for placement rules
CollisionsCheckRule has a messages field of type CollisionRuleSettings. In the editor, _init() creates this automatically:
In exported builds, messages may still be null if the .tres does not explicitly serialize it. The rule now includes _ensure_messages() which lazy-loads a default CollisionRuleSettings whenever messages is accessed:
This safeguard runs in validate_placement() and get_editor_issues(), so exports do not crash on null messages regardless of why it is null.
Best Practices for Web-Compatible Resources
Save base rules as standalone .tres files
Base placement_rules in GBSettings should reference external .tres files, not embedded SubResource(...) entries:
Good — external rule resource:
Avoid — embedded subresource:
Use plain arrays for placement_rules
Avoid the typed-array wrapper syntax in .tres files. It can silently break in exported builds.
Good:
Avoid:
See godot#97782 for the upstream bug report.
Externalize GBSettings from GBConfig
Avoid embedding GBSettings as a subresource inside GBConfig. Save it as its own .tres file:
Serialize nested resources explicitly
Save important nested resources directly in the .tres file rather than relying on _init() to create them:
CollisionsCheckRule.messagesCollisionsCheckRule.fail_visual_settings- Tile rule
fail_visual_settings
Verify indicator scenes serialize collision flags
RuleCheckIndicator scenes should explicitly set:
These defaults are safe in the editor but should be written explicitly so exported builds do not depend on editor-only defaults.
Collision Masks by Demo Type
Different demos use different physics layers. Do not copy collision masks between demos blindly:
| Demo | Rule collision_mask | Rule apply_to_objects_mask |
|---|---|---|
| Top-down | 1 or project-specific | 1 |
| Platformer | 1 or project-specific | 1 |
| Isometric | 2560 | 2561 |
Verification
Automated tests
Run the web export compatibility test suite:
This validates:
.tres-loaded rules lazy-load messages on first usevalidate_placement()does not crash when messages is null- Demo config chains load rules correctly
Pre-export checklist
- Base rules in
GBSettingsare external.tresfiles -
GBConfig.settingsreferences an externalGBSettings.tres - Placeable
packed_scenereferences a real.tscnfile - Collision rules serialize
messagesandfail_visual_settings - Indicator scenes serialize
collide_with_bodiesandcollide_with_areas -
placement_rulesuses plain array syntax (noArray[ExtResource(...)](...)) - Collision masks match your project’s physics layer setup
- Export filter includes
res://templates/and any custom rules directories
Troubleshooting
Placement indicators do not appear
- Check browser console for
Failed to load resourceerrors - Verify
placement_rulesis non-empty by adding a temporary print inGBCompositionContainer.get_placement_rules() - Ensure base rules are external
.tresfiles, not embedded subresources - Check that
placement_rulesdoes not use typed-array syntax (Array[ExtResource])
Placement always succeeds (no red tiles)
- Verify
CollisionsCheckRule.collision_maskoverlaps your target objects’collision_layer - Check that indicator scenes have
collide_with_bodies = true - For isometric projects, verify masks are
2560/2561, not top-down defaults
Rules load but validation is wrong
- Check
messagesis serialized in the rule.tresor that_ensure_messages()is being triggered - Verify
IndicatorFactoryreceives a valid logger (it falls back to a defaultGBLoggerautomatically)
References
- godot#97782 —
@export Array[ResourceCustom]empty in exported builds (typed array deserialization bug) - godot#72489 — Typed arrays break resource deserialization when some elements are
null - godot#70575 —
@exportvariables set after_init()runs (deserialization order) - Godot Docs: Exporting for the Web
Last updated: 2026-05-02