My name is Roman, today in this thread I’m very excited to share my open-source project related to Go and game development which I released a few days ago. It’s still under active development, but is ready for a first public release.
This project called PdGo. It allows you to write games for Playdate in Golang.
Finally,cute yellowPlaydate handheldfrom Panic Inc meets the Golang programming language, thanksfully to TinyGo!
Finally the new architecture for the project! Thanks for the guys from TinyGo community, they were surprised that TinyGo can’t use CGO, since that’s what I’d claimed, and I’m really glad they pointed it out - now the project relies entirely on CGO!
refactor: Unified CGO architecture for device and simulator builds.
Major refactoring of pdgo library to use direct CGO calls instead of //go:linkname bridging mechanism.
Key changes:
Single C source file (pd_cgo.c)
All Playdate SDK C wrappers in one file
Conditional compilation with #ifdef TARGET_PLAYDATE for device-specific code (ARM interrupts, TinyGo runtime)
Device entry point (eventHandler) included under TARGET_PLAYDATE
What? The guys from the TinyGo community were “surprised that TinyGo can’t use CGO”? Gonna need more explanation here. Where exactly did this conversation take place? Can you link me an issue on the TinyGo project?
Hi! I think I wrote that a bit unclearly. The first versions of PdGo before architecture refactoring didn’t use CGO directly because I couldn’t get TinyGo working with CGO for Playdate… I even wrote it in the README - “unfortunately TinyGo doesn’t support CGO for Playdate and due to this reason we need to use workaround.” The TinyGo folks saw it, told me that’s not true, and were pretty surprised by my claim. After that, I did CGO end up working finally for Playdate, and now the project relies entirely on it This conversation was on the Slack.
it was my mistake and I just didn’t know how to set it up at first, but then I figured it out.
The dreaded runtime_init error is gone!
If you’ve ever seen undefined reference to 'runtime_init' after updating TinyGo - yeah, that was annoying. Turns out we needed to explicitly export this function from the Go runtime. Fixed now, builds work smoothly again.
macOS disk eject actually works now
Some of you with CleanMyMac (or similar tools) might have noticed -deploy failing with “Unmount was dissented by PID…”. The fix? If the normal eject doesn’t work, we now force it.
New Features
-run flag got way more flexible!
Build it, run it - in one command:
-sim -run → build + launch simulator
-device -deploy → build + push to device + run
-sim -device -run -deploy → do it all at once
No more manual steps. Just add -run or -deploy and watch it go.
Docs Cleanup
Merged all the setup instructions into Quick Install - one place, one command: curl | bash
Removed that old build-tinygo-playdate.sh script -install.sh handles everything now
TinyGo version is locked to 0.40.1 - stability first
As always, feedback and PRs are welcome. Happy coding!
Finally got JSON working on the Playdate! But man, this was NOT straightforward. Here’s the story.
The Problem
Playdate SDK has a JSON API, but it’s callback-based. Not like Go’s nice json.Unmarshal() - nope, you get bombarded with callbacks: “hey I found a key!”, “hey I’m entering an object!”, “hey here’s a value!”. Fun times.
My first instinct? Just use Go’s encoding/json. One line, done, right?
WRONG.
Turns out encoding/json uses reflection internally. Reflection can panic. Panic needs longjmp. TinyGo on bare metal doesn’t have that. Game over.
The Journey
Attempt 1: Use encoding/json anyway
Result: Linker error. No tinygo_longjmp symbol.
Why: Go’s reflection-based JSON uses panic/recover which TinyGo can’t do on Cortex-M7.
Attempt 2: Wrap Playdate’s callback API
Challenge: C callbacks calling Go functions. On bare metal. Without proper stack.
Solution: CGO trampolines. Go exports functions, C calls them. Somehow it works.
Attempt 3: Build a tree from callbacks
This was fun. Playdate calls you with events like:
“Starting object called ‘web-app’”
“Found key ‘servlet’, it’s an array”
“Array element 1 is an object”
… 200 more callbacks …
“Done with ‘web-app’”
Had to maintain a stack, track parent nodes, attach children at the right time.
Spent way too long debugging why web-app wasn’t found. Turns out Playdate wraps everything in _root. Classic.
Attempt 5: Why does it crash on device but work on simulator?
Simulator: Works perfectly, logs everything.
Device: Instant crash. No logs. Nothing.
The culprit? fmt.Sprintf(). Uses reflection. REFLECTION AGAIN.
Solution: Remove ALL fmt usage. Use string concatenation like cavemen.
The Secret Sauce
Added this little gem to make maps and slices work:
Here’s a quick look at what I’ve made over the past three weeks for the PdGo project…
v0.4.0 → 0.5.0 → 0.6.0 → 0.7.0 → v0.7.1
Three new official SDK examples ported to Go, plus implemented new sprite collision and tilemap APIs.
v0.5.0-TilemapSupport
NewAPI: Full Tilemap API implementation for drawing tile-based maps.
NewExample: examples/tilemap - Official Playdate SDK Tilemap example
rewritten in Go. Demonstrates loading tilesets, creating tilemaps, and
rendering scrolling backgrounds.
v0.6.0-SpriteCollisions&QuerySystem
NewAPI: Comprehensive sprite collision and spatial query system:
MoveWithCollisions - Move sprites with collision detection
User interaction (crank and buttons control particle properties)
v0.7.1-BugFixes&Improvements
Fixed player movement in Sprite Collisions example
Added accessor functions for Sprite, Collision, and QueryInfo subsystems
Summary: 3 official SDK examples ported (Tilemap, Sprite Collisions,Particles), new Tilemap and Sprite Collision/Query APIs, and improved accessor
patterns. The roadmap progress continues toward full SDK example coverage.