Seven scanners for your Godot 4 project

Node scripts, one question each. No install, no dependencies, no network, and they never write to your project — every one of them only reads. Exit code is 0 when there is nothing to report and 1 when there is, so the same file works on your machine and as a CI gate.

Download

One zip, 47 KB, MIT. No email, no account, nothing to sign up for.

Contains the seven scripts, the four shared files they require, a README and both MIT licences. Every file is byte-identical to its source in the two public repos — the zip is a shortcut, not a different version. Needs Node 18 or newer; one of the seven also needs your own Godot editor binary.

What they printed against Godot's own demo projects

Claims about a scanner are cheap, so here is every one of them run today — 19 August 2026, Node 22, from a freshly extracted copy of the zip above — against code nobody here wrote: godotengine/godot-demo-projects pinned at 34fc995. The last line of output and the exit code, unedited:

scannerwhat it printedexit
scan_project_glyphs.js
whole repo
81 character occurrences across 5 files are outside the built-in font.1
scan_translation_glyphs.js
gui/translation CSV
20 distinct characters (ja) are outside the built-in font.1
find_frozen_translations.js
gui/translation
no frozen translations: 2 .gd/.cs file(s) scanned, no tr() result stored in an auto-translated property.0
find_tile_seam_causes.js
2d/dynamic_tilemap_layers
4 cause(s) and 0 note(s) in project.godot plus 2 scene/resource file(s).1
find_tile_collision_gaps.js
2d/dynamic_tilemap_layers
1 file(s) with a TileSet, 1 painted tile(s), 0 finding(s).0
predict_terrain_paint.js
2d/skeleton tileset.tres
all 9 cells have an exact tile in your set.0
verify_pot_generation.js
Godot 4.7-stable, --selftest
59/61 claims — selftest OK, the 2 seeded errors were both caught.0

Three of the seven come back clean on this corpus, and that is the useful half of the table: a scanner that finds something everywhere is a scanner that has learned to shout.

The seven, and the question each one answers

1. Characters your exported build cannot draw

The string is translated correctly and the player sees boxes. Godot's built-in font carries a fixed set of glyphs, and in the editor your machine quietly lends the rest — a font that is not in your export.

node docs/scan_project_glyphs.js /path/to/project
node docs/scan_translation_glyphs.js translations/text.csv

The first reads every scene and script in the project; the second reads one CSV and reports per locale. Both name the file, the line and the exact codepoint. The page with the measurements and an in-browser version.

2. Strings frozen in the language they were built in

The player switches language and half the UI follows. The half that does not is usually label.text = tr("KEY") — that stores the translated value where the engine expects the key, so there is nothing left to re-translate.

node docs/find_frozen_translations.js /path/to/project

Reads .gd and .cs, reports line and column, --json for CI. The 13 claims behind it, each asserted against Godot 4.7.

3. Translatable strings Generate POT leaves out

This one runs your Godot binary headless and compares what the editor's POT generation extracted against what is really translatable in the project.

node docs/verify_pot_generation.js /path/to/godot --selftest

The --selftest above builds a throwaway project with known traps and seeds two errors on purpose, to prove the checker still catches them. What it misses and why.

4. Which of the four causes opens the line between your tiles

Tiles touch in the PNG and the running game draws a one-pixel seam. There are four different causes with four different fixes, and the answer repeated most often online — Rendering → Quality → 2D → Enable Pixel Snap — is a Godot 3 setting that does not exist in Godot 4.

node docs/find_tile_seam_causes.js /path/to/project

Each finding carries the id of a claim measured on a real engine. The 26 claims, and the same scanner running in the page.

5. Painted tiles with no collision polygon

The hole the player falls through, listed before a playtest finds it.

node docs/find_tile_collision_gaps.js /path/to/project

It says the honest thing when it finds nothing: every tile carrying a polygon is not the same as collision working — layer bits, collision_enabled and the body mask still have to line up.

6. What terrain painting will actually put down

From your TileSet alone, with no engine and no project: the mask each cell asks for, and the cells your set cannot answer. Godot never leaves such a cell empty — it places the tile that disagrees with the fewest peering bits, which is why a missing tile looks like a bug in the algorithm.

node docs/predict_terrain_paint.js tileset.tres --rect 8x8

The 24 claims, including the sweep showing the score never names one tile. Related: why a blob autotile is 47 tiles and not 256.

What these are not

They do not fix anything. Every one of them reads your files and prints what it found; not one writes a byte back. Nothing here phones home, and nothing here has a trial.

They are also not demos. These scripts are the whole thing — they are the tools we wrote to measure our own claims, and the numbers on the pages linked above come out of these exact files. The products they came from answer different questions: LocGuard is a Godot 4 editor dock and CLI that fails a build on the translation table itself — missing keys, empty translations, placeholder drift, unbalanced BBCode (blobsmith.itch.io/locguard, free CLI: locguard-lite). Blobsmith turns 6 hand-drawn tiles into a paint-ready 47-tile Godot 4 tileset (blobsmith.itch.io/blobsmith, free Lite: blobsmith-lite).

How this download stays honest

The zip is not assembled by hand. A build script copies each file from its repo path, and the site's own verification rebuilds the zip from those same sources and compares — if a script gains a rule and the zip is not rebuilt, the check fails instead of serving a stale download quietly.

That discipline earned its keep the day this page was written: extracting the zip into an empty folder and running all seven from there is part of the check, and the first run failed. Three of the scripts require shared files that were sitting next to them in the repo and were not in the zip, and a fourth requires LocGuard's CLI core. A zip that is correct in the folder it was built from is exactly the kind of broken that a reader discovers and the author never does.

Every measured page these came from