Appendix D: Troubleshooting
The recurring traps, in one place. Most "it doesn't work" moments are one of these.
The page is blank / the wasm won't load
- You opened it via
file://.fetch/instantiateStreamingof the.wasmis blocked underfile://with a CORS error. Serve over HTTP:./run.sh, thenhttp://localhost:9000. (Chapters 3โ4.) - The
.wasmisn't served asapplication/wasm. Streaming instantiation validates the MIME type and refuses anything else. Check your server'sContent-Typefor.wasm. Our Chapter 4 server sets it; some static hosts need configuring. (Chapter 4.) - A missing import. The browser console shows
LinkError: ... function import requires ...when the JS import object is missing a function Odin expects (anodin_envhook, or anapp_enventry you added in Odin but not in JS). Add it to the import object.
Crashes, garbage, or silent misbehaviour after a boundary crossing
- You forgot
context = runtime.default_context(). It must be the first line of every@(export) proc "c". Without it, anything touching the context (allocation, somecore:calls) reads uninitialised state. Symptoms are weird and far from the cause. (Chapter 3.)
A string from JS turns to garbage
- The scratch-buffer lifetime.
batch.get(...)returns a string backed by one shared global buffer, valid only until the next call that touches scratch. Consume it (or copy it) before the nextget. Same rule forstr_concat/grey's buffers. (Chapters 6, 10.)
The renderer draws nothing, or draws nonsense
- The opcode ends are out of sync. The Odin
OP_*constants and the JSswitchare one wire format in two files. If you added or changed an op on one side only, the JS interpreter reads an argument as an opcode and the buffer desyncs (often:default: return, so the frame just stops early). Edit both ends together. (Chapters 5, Appendix C.) - You didn't
resetthe buffer. Callbatch.reset(&cmd)at the top of each frame, then record, thenbatch.flush. (Chapter 5.)
No sound
- The AudioContext is suspended. Browsers keep it suspended until a user gesture. Call
getCtx()(which resumes it) on the first keypress. (Chapter 11.) - The
SND_*ids don't match. The Odin constants and the JSplaySoundswitch are one list in two files. (Chapter 11.)
Build & tooling
odin checksays "Undefined entry point procedure 'main'". You're checking WASM/library code without-no-entry-point. Useodin check src -target:js_wasm32 -no-entry-point. (Chapter 3.)-out:servesays the output path is a directory. The binary name collides with theserver/package directory; that's why we name the package dirserver/and the binaryserve. (Chapter 4.)- Port 9000 is already in use. Another instance (or another game) is bound to it. Stop it, or change
PORTinserver/serve.odin. Find it withlsof -i :9000(macOS/Linux). - A
core:call fails to compile on a newer Odin. The standard library evolves. The two most likely to drift here areos.read_entire_file_from_path(return shape) andcore:net(Endpoint/listen_tcpsignatures). Read the compiler's suggested overloads; it names the fix.