Help home · Guides · Modules language reference · Official modules · Module samples
Modules give independently written Blitz code stable namespaces and explicit public interfaces. In a larger project they also provide compilation boundaries: unchanged imported Modules can reuse validated native objects from the compiler-managed cache instead of regenerating their implementation every time you run or check the program.
| Without Modules | With Modules |
|---|---|
| Included declarations share one global naming space. | Each Module owns a stable namespace, so libraries may reuse ordinary names. |
| Included source is part of the main compilation unit. | Each imported Module is compiled and cached as a separate unit. |
| Implementation helpers are visible to all included code. | Private keeps implementation details out of the public interface. |
| Sharing code requires naming conventions and careful Include ordering. | Logical names, aliases, and explicit interfaces make code easier to distribute. |
Modules are especially useful for stable subsystems such as mathematics, animation, game-state models, procedural generation, parsers, editor tools, AI, and reusable libraries. A tiny one-file program may not build noticeably faster: cache validation and linking still have a fixed cost.
Keep logical Module names aligned with folders beneath one Module root:
SpaceGame\
main.bb
modules\
SpaceGame\
Math.bb
Actors.bb
Navigation.bb
The declarations inside those files are:
; modules\SpaceGame\Math.bb
Dialect "modern"
Module SpaceGame.Math
Private Global epsilon# = 0.0001
Function NearlyEqual(a#, b#)
Return Abs(a - b) < epsilon
End Function
; main.bb Dialect "modern" Import SpaceGame.Math As Math If Math.NearlyEqual(1.0, 1.00001) Then Print "Close enough"
A program's own modules folder, beside its main file, is a
Module root without any settings, so F5 or blitzcc --mode extended
main.bb finds SpaceGame.Math there. The compiler looks there
first, and a Module it finds there is used even when a search path holds
another copy, so the program always builds with the copy it carries. On
case-sensitive file systems, such as the online IDE's, name the folders and
files in lower case (modules/spacegame/math.bb).
Modules shared between programs can live anywhere: add their root through Program / Module search paths..., or compile from a command prompt with:
blitzcc --mode extended --module-path C:\BlitzLibraries main.bb
A quoted import such as Import "modules\SpaceGame\Math.bb" As
Math is resolved relative to the importing file. Logical imports are
usually better for reusable code because callers do not depend on its physical
installation location.
| Term | Exact meaning |
|---|---|
| Cold build | No imported .bb Module has a valid cache object for its current source/Include closure, dependencies, architecture, Debug/Release kind, and Module ABI. Every source Module generates fresh native code. This commonly occurs on a project's first build or when using a fresh cache partition. |
| Warm build | Every unchanged imported .bb Module has a compatible validated cache object. The entry source still compiles, source cache validity is still checked, and every object is still linked. |
| Partial build | Some imported source Modules are valid cache hits while changed, new, or invalidated Modules rebuild. This is the usual state during active development. |
| Clean source rebuild | The build deliberately bypasses both reading and publishing compiler-managed source cache entries. Every imported .bb Module compiles, but explicit .b3m imports remain precompiled inputs. |
Cold and warm describe the availability of compatible cached source objects, not merely whether the cache directory exists. One project may have a warm build while another project using the same cache root has a cold or partial build.
For an entry program and two imported source Modules, a cold build does the following:
On a warm build the entry program is compiled again. Imported source is read and parsed so the compiler can discover its imports and hash its source and Include closure. For a valid cache hit, however, the compiler replaces the implementation with its compact public interface and reuses the existing native object. It therefore avoids full implementation analysis, translation, native code generation, and assembly for that Module.
Every object is still linked into the program. Modules reduce repeated compilation work; they do not turn the final Blitz program into a collection of runtime DLLs.
| Change | Expected rebuild |
|---|---|
| No source changes | The entry source compiles; unchanged imported Modules use cached native objects. |
| Function body or Private implementation changes | The changed Module rebuilds. Dependants can remain cached when its public and Worker-safety interfaces are unchanged. |
| Public signature, Type field, Enum, Interface, visibility, or dependency contract changes | The changed Module and affected dependants rebuild because the public interface hash changed. |
| An Include used by a Module changes | The owning Module rebuilds because Includes are part of its implementation closure. |
| A UserLib function that a Module calls loses its declaration, or its parameter or result types change | The Module rebuilds against the current declarations, so the compiler reports any call that no longer matches. An explicit .b3m that calls the function stops the build at its Import, naming the function. Other declaration changes, such as a new DLL or parameter name, rebuild nothing, because every build binds UserLib functions to the DLLs its current declarations name. |
| Debug, target architecture, compiler ABI, runtime ABI, or object-format changes | A separate cache partition is used or the incompatible object is rejected. |
The entry source is deliberately not treated as a reusable imported Module object. Keeping a small entry program and moving stable, substantial code into Modules gives the cache more useful work to reuse.
Use --module-stats to inspect deterministic build decisions
instead of judging one noisy wall-clock measurement:
blitzcc --mode extended --module-path modules --module-stats main.bb
For an entry program with two imported source Modules, a typical first and second build reports:
MODULE_STATS resolved=3 cached=0 misses=2 invalidated=0 compiled=3 linked=3 MODULE_STATS resolved=3 cached=2 misses=0 invalidated=0 compiled=1 linked=3
Measure a representative edit/rebuild loop as well as a no-change build. Projects benefit most when large, stable Modules remain cached while a smaller entry or gameplay Module changes frequently.
Include is textual. Use it to split one Module implementation across several files which share its namespace, Private declarations, and cache fate. An Include change rebuilds the owning Module.
Import creates a separate compilation unit and dependency interface. Use it when code has a meaningful API, should avoid naming clashes, can be reused elsewhere, or changes on a different schedule.
Do not turn every tiny helper into its own Module. Very fine-grained graphs increase resolution, validation, and link overhead. Prefer cohesive boundaries which hide several implementation details behind a small public surface.
Private when callers do not need them.The automatic cache is for local incremental compilation. An explicit
.b3m is a distributable, precompiled Module object:
mkdir objects\win64\SpaceGame
blitzcc --mode extended --emit-module ^
-o objects\win64\SpaceGame\Math.b3m modules\SpaceGame\Math.bb
blitzcc --mode extended --module-path objects\win64 main.bb
An explicit object avoids reading the library's implementation source. The compiler validates its metadata, parses its compact public interface, resolves its recorded dependencies, and merges its relocatable native code.
A .b3m is specific to one platform, Debug or Release, and the
compiler/runtime Module ABI. Build and distribute each variant your users need.
Debug objects embed their source and Include closure for portable debugger
tabs; add -d when both producing and consuming one.
| Platform | Module objects |
|---|---|
| Win32 | Create them with the Win32 compiler. |
| Win64 | Create them with the Win64 compiler. |
| macOS, iOS, and the iOS Simulator | Create them on a Mac, with
Program > Create Module… or bin/blitzcc. One object,
holding the Module's AArch64 assembly text, serves all three. A Module that uses
GUI windows or gadgets works only on macOS: iOS has only the GUI requesters, and
an iOS build that imports such a Module stops with an error that names the
command. |
| Web | None. Web builds (--target web) compile every
Module from source: they cannot import a .b3m, and
--emit-module refuses the web target. A library meant for the web
must include its source. |
Importing an object built for another platform stops the build with an error that names both, for example:
Module 'spacegame.math' was built for Win32; this compiler targets Win64
Within each Module root, a logical import looks for Math.bb
before Math.b3m, so an object beside its source is never used.
Give each platform's objects a root of their own, apart from the source:
SpaceGame\
modules\SpaceGame\Math.bb source, for development and web builds
objects\win32\SpaceGame\Math.b3m for the Win32 compiler
objects\win64\SpaceGame\Math.b3m for the Win64 compiler
objects\apple\SpaceGame\Math.b3m for the Mac compiler
Point each build at one root for the library: the source, or its own
platform's objects. Do not configure two roots containing the same logical
identity: that is an ambiguous import. The program's own modules
folder is the exception: a Module found there wins over every other root.
The Modules tab beside Help (View / Modules) is the module library members of Blitz3D+ Online share, in the Windows, Mac and online IDEs. Sign in with your Blitz3D+ Online account; a desktop IDE opens your browser, where you sign in with your passkey and allow the computer.
modules folder, records them in
modules/modules.json, and adds the Import line, which
Undo takes back. The program then builds anywhere, offline and as a web build,
because its Modules are ordinary files beside it.modules/yourname/thing.bb, starting Module yourname.thing,
with anything else it brings in the folder modules/yourname/thing/.
Open its main file and choose Program / Upload this module.... The
version is saved to your account, private; the server compiles it for each
platform, and once it passes, Publish makes it public, after asking for
your passkey. Public versions never change; withdraw one, with a reason, to
stop new programs adding it.LoadImage("modules/yourname/thing/spark.png").modules folder beside its
main file, or open Program / Module search paths... and add the root
beneath which the dotted namespaces of shared Modules are laid out.The IDE stores Module search paths in its user preferences and forwards them to every compiler invocation. No project file or manual cache switch is required.
Program / Run in browser and Program / Create web build... use
the same search paths. Web builds compile every Module from source, so for them
the paths must lead to each Module's source: a root holding only
.b3m objects stops a web build with an error.
Program / Rebuild all source modules performs a one-off error-check
build with --no-module-cache. It neither consumes nor publishes
source cache entries, does not delete the existing cache, and does not run the
program. The next ordinary build can therefore use the previously validated
cache again. Explicit .b3m dependencies are not rebuilt; recreate
them from their source when a new object is required.
On a Mac, the Program menu has the same commands: Module Search Paths… (one folder per line), Create Module…, and Rebuild All Source Modules. ⌘R runs the program and ⌘B checks it. Create Module… makes one object for macOS, iOS, and the iOS Simulator, whichever Run On target is chosen.
Memory reporting adds a memory-names-v1 subfolder to each partition below. Compilers targeting an older runtime without Type-name registration use memory-unnamed-v1 instead. Existing ABI-5 source-free modules remain supported; unnamed descriptors receive a run-local identity in memory reports. A named module object requires a runtime that supplies Type-name registration.
The default cache root depends on the platform:
| Platform | Cache root |
|---|---|
| Windows | %LOCALAPPDATA%\Blitz3D+\ModuleCache |
| Mac | ~/Library/Caches/Blitz3D+/ModuleCache |
| Linux | $XDG_CACHE_HOME/Blitz3D+/ModuleCache, or ~/.cache/Blitz3D+/ModuleCache when unset or relative |
Beneath it, each platform, Debug/Release, and Module ABI version uses a separate partition:
| Compiler | Release partition | Debug partition |
|---|---|---|
| Win32 | win32\release\abi-5.1 | win32\debug\abi-5.1 |
| Win64 | win64\release\abi-5.1 | win64\debug\abi-5.1 |
| Mac (macOS, iOS, and the iOS Simulator) | apple-arm64/release/abi-5.2 | apple-arm64/debug/abi-5.2 |
| Linux x86-64 | linux-x64/release/abi-5.2 | linux-x64/debug/abi-5.2 |
| Linux ARM64 (reserved for Phase 2) | linux-arm64/release/abi-5.2 | linux-arm64/debug/abi-5.2 |
| Android, built on the Mac | android-arm64/release/abi-5.2 | android-arm64/debug/abi-5.2 |
Web builds do not use the cache. Every web build compiles each Module from
source, so --module-stats always reports cached=0 for
them.
Advanced command-line controls are:
--module-cache directory uses an isolated cache root;--no-module-cache ignores and does not publish cached source
Modules for that build; and--module-stats prints the build counters described above.Normally there is no reason to clear the cache: entries are content-addressed
and compatibility-checked. To diagnose storage or permission problems, close
active builds and remove only the relevant target/build partition in Explorer
or the Finder, or point one command-line build at a fresh directory with
--module-cache.
Use ordinary source Modules during active development: edits are detected
automatically and the cache handles unchanged code. Use explicit
.b3m objects when distributing a library without its implementation
source, pinning a tested dependency in CI, or removing library implementation
work from consumer builds. Web builds cannot use objects, so a library that
should also run in a browser must include its source.
Precompiling every frequently edited local Module usually adds packaging work without helping iteration. The transparent source cache is intended for that case.