{"id":41987901,"url":"https://github.com/playdate-go/pdgo","last_synced_at":"2026-04-27T16:00:27.580Z","repository":{"id":333349394,"uuid":"776858049","full_name":"playdate-go/pdgo","owner":"playdate-go","description":"🟡 Golang for Playdate. Compiler, SDK Bindings, Tools and Examples ⚒️","archived":false,"fork":false,"pushed_at":"2026-04-24T23:34:37.000Z","size":79133,"stargazers_count":61,"open_issues_count":5,"forks_count":2,"subscribers_count":3,"default_branch":"main","last_synced_at":"2026-04-25T01:35:17.399Z","etag":null,"topics":["api","armv7","bare-metal","bindings","c","cgo","embedded","embedded-systems","game-development","gamedev","gamedev-library","golang","playdate","playdate-console","playdate-sdk","tinygo"],"latest_commit_sha":null,"homepage":"https://devforum.play.date/t/golang-support-for-playdate-compiler-sdk-bindings-tools-and-examples/24919","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/playdate-go.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2024-03-24T16:35:05.000Z","updated_at":"2026-04-24T23:34:42.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/playdate-go/pdgo","commit_stats":null,"previous_names":["playdate-go/pdgo"],"tags_count":15,"template":false,"template_full_name":null,"purl":"pkg:github/playdate-go/pdgo","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/playdate-go%2Fpdgo","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/playdate-go%2Fpdgo/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/playdate-go%2Fpdgo/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/playdate-go%2Fpdgo/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/playdate-go","download_url":"https://codeload.github.com/playdate-go/pdgo/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/playdate-go%2Fpdgo/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32343571,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-26T23:26:28.701Z","status":"online","status_checked_at":"2026-04-27T02:00:06.769Z","response_time":128,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["api","armv7","bare-metal","bindings","c","cgo","embedded","embedded-systems","game-development","gamedev","gamedev-library","golang","playdate","playdate-console","playdate-sdk","tinygo"],"created_at":"2026-01-26T00:22:00.986Z","updated_at":"2026-04-27T16:00:27.548Z","avatar_url":"https://github.com/playdate-go.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"\n\u003cimg src=\"assets/pdgo-logo.png\" alt=\"Logo\" width=\"270\"  \u003e\n\n## Menu\n\n- [Overview](#overview)\n- [Quick Install](#quick-install)\n- [Usage](#usage)\n- [Internals](#internals)\n- [Why Not Go But TinyGo](#why-not-go-but-tinygo)\n- [Flow](#flow)\n- [Known Issues](#known-issues)\n- [API Bindings](#api-bindings)\n- [Examples](#examples)\n- [A Tour Of Go](#a-tour-of-go)\n- [Roadmap](#roadmap)\n- [Contribution](#contribution)\n- [Community](#community)\n- [Attribution](#attribution)\n- [License](#license)\n---\n\n## Quick Install\n\n```bash\ncurl -fsSL https://raw.githubusercontent.com/playdate-go/pdgo/main/install.sh | bash\n```\n\nThis installs **everything** you need:\n- `pdgoc` - the build tool\n- Custom `TinyGo` with Playdate support (for device builds)\n- Configures your PATH automatically\n\n\n\u003e [!IMPORTANT]\n\u003e The install script will automatically compile LLVM from source (in example ~9 minutes on Apple Silicon M5 Pro (15 CPU) and ~25-30 minutes on Apple Sillicon M1 (8-CPU), only needed once).\n\n### Pre-install LLVM (Linux only - speeds up build)\n\n#### Ubuntu/Debian\n```bash\nwget https://apt.llvm.org/llvm.sh\nchmod +x llvm.sh\nsudo ./llvm.sh 20\nsudo apt install clang-20 llvm-20-dev lld-20 libclang-20-dev\n```\n\n#### Fedora\n```bash\nsudo dnf install llvm20-devel clang20-devel lld20-devel\n```\n\n#### Arch Linux\n```bash\nsudo pacman -S llvm clang lld\n```\n\n### ARM Toolchain\n\n- **macOS**: Included with Playdate SDK - no separate installation needed\n- **Linux**: Install via package manager: `sudo apt install gcc-arm-none-eabi`\n\n### Options\n\n```bash\n# Skip TinyGo build (simulator-only, instant install)\nSKIP_TINYGO=1 curl -fsSL https://raw.githubusercontent.com/playdate-go/pdgo/main/install.sh | bash\n\n# Custom parallel jobs\nJOBS=8 curl -fsSL https://raw.githubusercontent.com/playdate-go/pdgo/main/install.sh | bash\n```\n\n### Installation Modes\n\nThe install script automatically detects how it's being run and adjusts accordingly:\n\n| Mode | How to Run | pdgoc Source | Playdate Patches | Use Case |\n|------|-----------|--------------|------------------|----------|\n| **Local** | `./install.sh` from repo root | Local `cmd/pdgoc/` | Local `cmd/pdgoc/tinygo-patches/` | Development \u0026 testing |\n| **Remote** | `curl ... | bash` | GitHub tarball | GitHub raw URLs | Production installation |\n\n**Local Mode Benefits:**\n- Build from local source - test your changes before committing\n- Use local patch files - faster, no network needed\n- Get accurate version info from local git\n\nThe installer automatically detects which mode to use by checking for `cmd/pdgoc/` directory and `go.mod` file in the current directory.\n\n### What the installer does\n\n1. **Installs pdgoc** - `go install github.com/playdate-go/pdgo/cmd/pdgoc@latest`\n2. **Clones TinyGo** - downloads TinyGo v0.40.1 to `~/tinygo-playdate`\n3. **Sets up LLVM** - uses system LLVM (Linux) or builds from source (macOS)\n4. **Adds Playdate support** - injects custom files into TinyGo:\n   - `playdate.json` - target config (Cortex-M7, custom GC, no scheduler)\n   - `playdate.ld` - linker script (memory layout, entry point)\n   - `runtime_playdate.go` - platform runtime (time, console output via SDK)\n   - `gc_playdate.go` - leaking GC type (heap allocations never reclaimed unless manually freed)\n5. **Builds TinyGo** - compiles with Playdate support\n6. **Configures PATH** - adds `pdgoc` and `tinygo` to your shell\n\nResult: `~/tinygo-playdate/build/tinygo` - a TinyGo compiler that accepts `-target=playdate`\n\n\u003e [!WARNING]\n\u003e Windows is not currently supported. Linux and macOS only.\n\n---\n\n## Overview\n\n\u003e[!NOTE]  \n\u003e This project is currently under active development, all API covered but not all features have been fully tested or implemented yet, PRs are always welcome.  \n\u003e Tested on macOS, tinygo 0.40.1 darwin/arm64, go1.25.6, LLVM 20.1.1, Playdate OS 3.0.2.\n\n\u003e[!NOTE]  \n\u003e Playdate SDK \u003e= 3.0.2 is required  \n\u003e Go version must be ≥ 1.21 and ≤ the version used to build TinyGo. For example, if TinyGo was built with Go 1.25, you cannot use Go 1.26.\n\nHi, my name is Roman Bielyi, and I'm developing this project in my spare time as a personal initiative.\nThis project is an independent effort and is neither endorsed by nor affiliated with [Panic Inc](https://panic.com/).\n\nAs a Go developer, I immediately wanted to bring Go to the [Playdate](https://play.date/). It wasn’t straightforward, but I got it working -- hope you’ll enjoy experimenting with it.\n\n\u003e[!IMPORTANT]  \nThe main objective now is to release a stable 1.0.x version.\nTo achieve this, we need to rewrite all official Playdate SDK examples from C/Lua into Go and ensure that the Go API bindings are mature, stable, and provide complete coverage of all subsystems.\n\n## Usage\n\n`pdgoc` is a command-line tool that handles **everything** for building Go apps for Playdate, both Simulator and Device builds.\n\n\u003e [!IMPORTANT]  \n\u003e **Always use `pdgoc` for building.** Do not try to run `go build` or `tinygo build` directly because `pdgoc` handles all the complexity: SDK paths, CGO flags, temporary files, etc.\n\n\u003e [!TIP]  \n\u003e The `sim` and `device` flags can be combined to build for both Simulator and Device simultaneously.\n\n| Flag     | Description                                                   |\n|----------|---------------------------------------------------------------|\n| `sim`    | Builds project for the Playdate Simulator only                |\n| `device` | Builds project for the Playdate console only                  | \n| `run`    | Builds and runs project in the Playdate Simulator             |\n| `deploy` | Deploys and runs on connected Playdate device (requires `-device`) | \n\n\n| Flag                | Description                                     |\n|---------------------|-------------------------------------------------|\n| `name`              | Sets the `name` property for pdxinfo            |\n| `author`            | Sets the `author` property for pdxinfo          |\n| `desc`              | Sets the `description` property for pdxinfo     | \n| `bundle-id`         | Sets the `bundleID` property for pdxinfo        | \n| `version`           | Sets the `version` property for pdxinfo         |\n| `build-number`      | Sets the `buildNumber` property for pdxinfo     |\n| `image-path`        | Sets the `imagePath` property for pdxinfo       |\n| `launch-sound-path` | Sets the `launchSoundPath` property for pdxinfo | \n| `content-warn`      | Sets the `contentWarning` property for pdxinfo  |\n| `content-warn2`     | Sets the `contentWarning2` property for pdxinfo |\n\n\n\u003e [!NOTE]  \n\u003e To use the `pdgoc` CLI tool, navigate to the project root directory -- the one containing the `Source` folder with your `.go` source files, `go.mod`, `go.sum`, and any assets.  \n\u003e Simply execute `pdgoc` from there. It will detect the 'Source' directory automatically.\n\u003e\n\u003e **Example:**  \n\u003e If your structure looks like this:\n\u003e ```\n\u003e your-project/\n\u003e ├── Source/\n\u003e │   ├── main.go\n\u003e │   ├── go.mod\n\u003e │   ├── go.sum\n\u003e │   └── assets/ (images, sounds, etc.)\n\u003e └──\n\u003e ```  \n\u003e\n\u003e Then `cd your-project/` and run `pdgoc`.\n\nExample:\n```bash\npdgoc -device -sim \\\n  -name=MyApp \\\n  -author=YourName \\\n  -desc=\"My App\" \\\n  -bundle-id=com.yourname.myapp \\\n  -version=1.0 \\\n  -build-number=1\n```\n\nThe `main.go`:\n\n```go\npackage main\n\nimport (\n\t\"github.com/playdate-go/pdgo\"\n)\n\n// A global pointer to the Playdate API. \n//Initialized automatically when the game starts. \n//All SDK calls go through this variable: pd.Graphics.DrawText(...), pd.System.DrawFPS(...), etc.\nvar pd *pdgo.PlaydateAPI\n\n\n// Called once when the game launches (during kEventInit).\n// Use this to load images, sounds, fonts, and initialize your game state. The Playdate API (pd) is fully available here.\nfunc initGame() {\n\t\n}\n\n// The main game loop. Called every frame (~30 FPS by default). Here you:\n// Handle input (pd.System.GetButtonState())\n// Update game logic\n// Draw graphics (pd.Graphics.DrawText(), pd.Graphics.DrawBitmap())\n// Return value: 1 to tell Playdate the display was updated and needs refresh. Return 0 if nothing changed (saves battery).\nfunc update() int {\n\t\n}\n\n// Must exist but remains empty. \n//Playdate doesn't use Go's normal main() entry point, instead, the SDK calls eventHandler which is generated by pdgoc\nfunc main() {}\n\n```\n\n# Internals\n\n### Device:\n\nFor Playdate hardware `pdgoc` uses a custom [TinyGo](https://tinygo.org/) build with full Playdate hardware support instead of standard Go build tools.\n\n**Custom GC**:  \nCustom TinyGo build for Playdate currently uses \"leaking\" GC type–heap allocations from both Go and the Playdate C API are never reclaimed unless you free them manually.\n\nThis isn't the goal. We're building a conservative mark-and-sweep GC designed for Playdate's constraints:\n- Tracks Go-level objects conservatively, integrated with the SDK allocator.\n- Uses a finalizers pattern to automatically free C-level API objects (bitmaps, sprites, sounds) when they become unreachable – managing both heaps in one system.\n- Lightweight stop-the-world pauses, lighter than Go's stock tri-color GC, tuned for a constrained system.\n\nThe priority is making it work reliably. Concrete specifications will follow. Once stable, the headache shifts from manual frees to what every Go developer already handles on constrained systems: keeping heap churn low. A much better problem to have.\n\nFollow this link to the progress:\n\n\u003cdetails\u003e\n\u003csummary\u003eclick to see: gc_playdate.go\u003c/summary\u003e\n\n```go\n//go:noinline\nfunc alloc(size uintptr, layout unsafe.Pointer) unsafe.Pointer {\n    size = align(size)\n    gcTotalAlloc += uint64(size)\n    gcMallocs++\n\n    ptr := _cgo_pd_realloc(nil, size)\n    if ptr == nil {\n        runtimePanic(\"out of memory\")\n    }\n\n    // Zero the allocated memory\n    memzero(ptr, size)\n    return ptr\n}\n\nfunc GC() {\n    // No-op - leaking GC, manual free required\n}\n\nfunc SetFinalizer(obj interface{}, finalizer interface{}) {\n    // No-op\n}\n```\n\n\u003c/details\u003e\n\n**No Static Heap**:  \nStandard TinyGo embedded targets reserve heap space in BSS section. Our runtime configuration eliminates this by setting `needsStaticHeap = false`.\nAs a result, BSS is reduced from approx. 1MB to approx. 300 bytes.\n\n\u003cdetails\u003e\n\u003csummary\u003eclick to see: gc_playdate.go\u003c/summary\u003e\n\n```go\nconst needsStaticHeap = false\n\nfunc initHeap() {\n    // No initialization needed - Playdate SDK handles it\n}\n```\n\n\u003c/details\u003e\n\n**Minimal Runtime Configuration**:  \nNo scheduler, no threading, no dynamic stack management. A fixed stack size of ~60KB is used instead of Go's traditional growable stacks.\n\n\u003cdetails\u003e\n\u003csummary\u003eclick to see: playdate.json\u003c/summary\u003e\n\n```json\n{\n    \"inherits\": [\"cortex-m\"],\n    \"llvm-target\": \"thumbv7em-unknown-unknown-eabihf\",\n    \"cpu\": \"cortex-m7\",\n    \"features\": \"+armv7e-m,+dsp,+hwdiv,+thumb-mode,+fp-armv8d16sp,+vfp4d16sp\",\n    \"build-tags\": [\"playdate\", \"tinygo\", \"gc.playdate\"],\n    \"gc\": \"playdate\",\n    \"scheduler\": \"none\",\n    \"serial\": \"none\",\n    \"automatic-stack-size\": false,\n    \"default-stack-size\": 61800,\n    \"cflags\": [\"-DTARGET_PLAYDATE=1\", \"-mfloat-abi=hard\", \"-mfpu=fpv5-sp-d16\"]\n}\n```\n\n\u003c/details\u003e\n\n**LLVM Optimization:**\n* Target: `thumbv7em-unknown-unknown-eabihf`\n* CPU: `cortex-m7` with FPU (`-mfpu=fpv5-sp-d16`, `-mfloat-abi=hard`)\n* Features: Thumb-2, DSP, hardware divide, VFP4\n* Dead Code Elimination: Unused functions stripped via linker script\n* Link-Time Optimization: Cross-module inlining via LLVM\n\n\u003cdetails\u003e\n\u003csummary\u003eclick to see: playdate.json \u0026 playdate.ld\u003c/summary\u003e\n\n**Target configuration:**\n\n```json\n{\n    \"llvm-target\": \"thumbv7em-unknown-unknown-eabihf\",\n    \"cpu\": \"cortex-m7\",\n    \"features\": \"+armv7e-m,+dsp,+hwdiv,+thumb-mode,+fp-armv8d16sp,+vfp4d16sp\",\n    \"cflags\": [\"-DTARGET_PLAYDATE=1\", \"-mfloat-abi=hard\", \"-mfpu=fpv5-sp-d16\"]\n}\n```\n\n**Linker script (dead code elimination):**\n\n```ld\nENTRY(eventHandlerShim)\n\nSECTIONS\n{\n    .text : ALIGN(4) {\n        KEEP(*(.text.eventHandlerShim))\n        KEEP(*(.text.eventHandler))\n        KEEP(*(.text.updateCallback))\n        KEEP(*(.text.runtime_init))\n        *(.text) *(.text.*) *(.rodata) *(.rodata.*)\n        KEEP(*(.init)) KEEP(*(.fini))\n        . = ALIGN(4);\n    }\n    ...\n    /DISCARD/ : { *(.ARM.exidx*) *(.ARM.extab*) }\n}\n```\n\n`/DISCARD/` removes unused ARM exception sections, `KEEP()` preserves only critical entry points.\n\n\u003c/details\u003e\n\n### Simulator:\n\n`pdgoc` uses Go's native build tools to compile apps for the Playdate Simulator.\n\n**Under the hood, it automatically runs:**\n```bash\ngo build -ldflags=\"-w -s\" -gcflags=\"all=-l\" \\\n  -trimpath -buildvcs=false -race=false \\\n  -buildmode=c-shared \\\n  -o \"some/output\" \"some/input\"\n```\n\nAll flags are optimized: stripping debug info (`-w -s`), disabling race detector, and producing a C-shared library with `-buildmode=c-shared` needed for Simulator instead of binary executable.\nIn Unix systems it's `.so` and in macOS  it's `.dylib`\n\n| Flag                  | Purpose                                                                                  |\n|-----------------------|------------------------------------------------------------------------------------------|\n| `-ldflags=\"-w -s\"`    | **`-w`**: Strip debug info (DWARF). **`-s`**: Strip symbol table. Shrinks binary ~30-50% |\n| `-gcflags=\"all=-l\"`   | Disable function inlining \u0026 optimizations for simulator compatibility                    |\n| `-trimpath`           | Remove local filesystem paths from binaries (security/portability)                       |\n| `-buildvcs=false`     | Skip embedding VCS data (git info) - faster builds                                       |\n| `-race=false`         | Explicitly disable race detector (already off by default)                                |\n| `-buildmode=c-shared` | **Key**: Build as C-shared library (`.dylib`/`.so`) for Playdate Simulator               |\n\n\n## Why Not Go But TinyGo\n\nNo Bare-Metal ARM Support:  \nStandard Go compiler (gc) only supports these targets:\n\n| Flag    | Purpose                     |\n|---------|-----------------------------|\n| linux   | amd64, arm64, arm, 386, ... |\n| darwin  | amd64, arm64                |\n| windows | amd64, arm64, 386           |\n\nPlaydate requires: thumbv7em-none-eabihf (ARM Cortex-M7, no OS), and this is simply impossible:  \n`GOOS=none GOARCH=thumbv7em go build  # not supported`\n\nSize:  \nStandard Go runtime includes Garbage Collector, Goroutine Scheduler, Stack Management and Reflection, binary size approx. 2-5 MB minimum.\nPlaydate constraints are 16 MB total RAM (shared with game data, graphics, sound), games typically 50 KB - 2 MB.\n\n| Feature            | Standard Go    | TinyGo                      |\n|--------------------|----------------|-----------------------------|\n| Bare-metal support | No             | Yes                         |\n| GOOS= not required | No             | Yes                         |\n| ARM Cortex-M       | No             | Yes thumbv7em target        |\n| Minimal runtime    | No approx. 2MB | Yes approx. 1-4 KB          |\n| Custom GC          | No             | Yes pluggable (gc.playdate) |\n| No OS required     | No             | Yes                         |\n| Relocatable code   | No             | Yes via LLVM                |\n| CGO on bare-metal  | No             | Yes (with custom runtime)   |\n\nIn short:\n\n```\nGo Source -\u003e TinyGo Frontend -\u003e LLVM IR -\u003e LLVM Backend -\u003e ARM Thumb-2 ELF\n                                              |\n                              Cortex-M7 optimizations\n                              Position-independent code\n                              Dead code elimination\n```\n\nSummary: Standard Go is designed for desktop/server environments, full operating systems, abundant memory (GB).\nPlaydate requires: bare-metal ARM Cortex-M7, no operating system, tiny runtime, and manual memory management (conservative mark-and-sweep GC planned).\n\nTinyGo bridges this gap by reimplementing Go compilation targeting embedded systems with LLVM backend. Our custom TinyGo build supports CGO on bare-metal Playdate hardware through a unified C wrapper layer (`pd_cgo.c`).\n\n## Flow:\n\n### Device Build\n\n```\n┌─────────────────────────────────────────────────────────────┐\n│  pdgoc -device                                              │\n├─────────────────────────────────────────────────────────────┤\n│  1. Copy pd_cgo.c from pdgo module to build/pd_runtime.c    │\n│  2. Create Source/main_tinygo.go                            │\n│  3. Run go mod tidy                                         │\n│  4. Create /tmp/device-build-*.sh                           │\n│  5. Execute build script:                                   │\n│     ├── Compile pd_runtime.c -\u003e pd_runtime.o -\u003e libpd.a     │\n│     ├── Create build/playdate.ld                            │\n│     ├── Create ~/tinygo-playdate/targets/playdate.json      │\n│     ├── TinyGo build -\u003e pdex.elf                            │\n│     ├── pdc -\u003e GameName.pdx/                                │\n│     └── Delete build/ directory                             │\n│  6. Delete Source/main_tinygo.go                            │\n│  7. Delete Source/pdxinfo                                   │\n└─────────────────────────────────────────────────────────────┘\n```\n### Simulator Build\n\n```\n┌─────────────────────────────────────────────────────────────┐\n│  pdgoc -sim                                                 │\n├─────────────────────────────────────────────────────────────┤\n│  1. Create Source/main_cgo.go                               │\n│  2. go build -buildmode=c-shared -\u003e pdex.dylib + pdex.h     │\n│  3. Delete Source/pdex.h                                    │\n│  4. Delete Source/main_cgo.go                               │\n│  5. pdc -\u003e GameName_sim.pdx/                                │\n│  6. Delete Source/pdex.dylib                                │\n│  7. Delete Source/pdxinfo                                   │\n└─────────────────────────────────────────────────────────────┘\n```\n\nTemporary files created by `pdgoc` during build:\n\n**Device Build Files**\n\n`pd_runtime.c` - Copied from `pd_cgo.c` in the pdgo module. This C file provides all Playdate SDK wrappers that Go code calls via CGO. It includes TinyGo runtime support functions (`_cgo_pd_realloc`, `_cgo_pd_logToConsole`, `_cgo_pd_getCurrentTimeMS`) and the `eventHandler` entry point. Compiled with `-DTARGET_PLAYDATE=1` to enable device-specific code.\n\n`main_tinygo.go` - Contains the `//export go_init` and `//export go_update` directives that tell TinyGo to expose these functions as C-callable symbols. The C runtime calls these functions to initialize the game and run the update loop. This file is separate from the user's main.go to avoid polluting their code with build-specific exports.\n\n`playdate.ld` - The linker script tells the ARM linker how to arrange code and data in memory. It defines the entry point (eventHandlerShim), ensures critical functions appear at the beginning of the binary, and sets up BSS/data sections.\n\n`playdate.json` - TinyGo's target configuration file. It specifies the CPU architecture (Cortex-M7), compiler flags, which garbage collector to use (gc.playdate), and links to the linker script. This file tells TinyGo exactly how to compile for Playdate hardware.\n\n`libpd.a` - A static library compiled from pd_runtime.c. TinyGo links against this library to resolve the C function references. Static linking ensures all SDK wrapper code is embedded directly in the final binary.\n\n\n| File                 | Location                     | Purpose                          | Cleanup                   |\n|----------------------|------------------------------|----------------------------------|---------------------------|\n| `pd_runtime.c`       | `build/`                     | C wrappers (copy of pd_cgo.c)    | Deleted with `build/` dir |\n| `main_tinygo.go`     | `Source/`                    | TinyGo entry points (`//export`) | Deleted after build       |\n| `device-build-*.sh`  | `/tmp/`                      | Embedded build script            | Deleted after build       |\n| `playdate.ld`        | `build/`                     | Linker script                    | Deleted with `build/` dir |\n| `playdate.json`      | `~/tinygo-playdate/targets/` | TinyGo target config             | Overwritten each build    |\n| `pdex.elf`           | `build/`                     | Compiled ELF binary              | Deleted with `build/` dir |\n| `pd_runtime.o`       | `build/`                     | Compiled C object                | Deleted with `build/` dir |\n| `libpd.a`            | `build/`                     | Static C library                 | Deleted with `build/` dir |\n| `pdxinfo`            | `Source/`                    | Game metadata                    | Deleted after build       |\n\n\n**Simulator Build Files**\n\nThe simulator build uses the same `pd_cgo.c` from the pdgo module as the device build, but compiled for the host architecture. The C wrappers are linked directly into the shared library via standard Go CGO.\n\n`main_cgo.go` - Contains `import \"C\"` and `//export eventHandler` directive that tells the standard Go compiler to generate a C-callable entry point. The simulator runs on your host machine (macOS/Linux), where CGO is fully supported.\n\n`pdex.h` - Automatically generated by go build -buildmode=c-shared. This header file contains C function declarations for all exported Go functions. We immediately delete it since Playdate doesn't need it - the SDK already knows the expected function signatures.\n\n`pdex.dylib` / `pdex.so` - The compiled shared library containing your Go game code and the C wrappers. The Playdate Simulator dynamically loads this library at runtime and calls eventHandler when your game starts. This file is moved into the .pdx bundle by pdc.\n\n| File                     | Location  | Purpose                       | Cleanup             |\n|--------------------------|-----------|-------------------------------|---------------------|\n| `main_cgo.go`            | `Source/` | CGO entry points (`//export`) | Deleted after build |\n| `pdex.h`                 | `Source/` | CGO header (auto-generated)   | Deleted after build |\n| `pdex.dylib` / `pdex.so` | `Source/` | Compiled shared library       | Deleted after build |\n| `pdxinfo`                | `Source/` | Game metadata                 | Deleted after build |\n\n\n## Known Issues: \n\nTwo confirmed crash-causing patterns in TinyGo's `fmt` package when targeting ARM Thumb (Playdate device). Both work fine in the Simulator (standard Go) but crash immediately on device.\n\n### Bug 1: `fmt.Sprintf(\"%v\", slice)` — reflection on slices\n\n```go\n// CRASHES on device:\nfmt.Sprintf(\"%v\", []int{1, 2, 3})\n\n// FIX — manual string building:\nfunc joinInts(s []int) string {\n    r := \"[\"\n    for i, v := range s {\n        if i \u003e 0 { r += \",\" }\n        r += fmt.Sprint(v)\n    }\n    return r + \"]\"\n}\n```\n\nThe `%v` format verb uses reflection to iterate slice elements, which is broken in TinyGo on ARM.\n\n### Bug 2: `fmt.Sprint(customStringerType)` — fmt.Stringer interface assertion\n\n```go\n// CRASHES on device:\ntype myString string\nfunc (m myString) String() string { return string(m) }\nfmt.Sprint(myString(\"test\"))\n\n// FIX — call String() directly:\nstring(myString(\"test\"))\n// or\nmyString(\"test\").String()\n```\n\nTinyGo's `fmt` package internally checks if a value implements `fmt.Stringer`. This interface assertion is broken on ARM Thumb.\n\n### General Rule\n\nOn TinyGo ARM/Playdate: only use `fmt.Sprintf`/`fmt.Sprint` with **basic concrete types** (`int`, `string`, `bool`, `float64` with basic format verbs like `%d`, `%s`, `%t`, `%.1f`). Never pass slices, maps, or custom types implementing interfaces to any `fmt` function.\n\n---\n\n## API Bindings\nThe latest full documentation for API bindings is hosted here:\nhttps://pkg.go.dev/github.com/playdate-go/pdgo#section-documentation\n\n## Examples\n\n\u003e [!NOTE]\n\u003e We will add more complex examples as the project progresses\n\nTo build all examples please do this:\n```bash\n# in project repo root\nchmod +x game_examples/build_all.sh \nchmod +x game_examples/*/build.sh\n./game_examples/build_all.sh\n```\n\nEach example includes a `build.sh` script that runs `pdgoc` with all necessary flags.\n\n**Particles** -- [game_examples/particles](game_examples/particles)\n\n**Exposure** -- [game_examples/exposure](game_examples/exposure)\n\n**Sprite Collisions** -- [game_examples/sprite_collisions](game_examples/sprite_collisions)\n\n**Tilemap** -- [game_examples/tilemap](game_examplestilemap)\n\n**JSON High and Low Level Encoding and Decoding** -- [game_examples/json](game_examples/json) | [examples/json_lowlevel](examples/json_lowlevel)\n\n**Bach MIDI** -- [game_examples/bach_midi](game_examples/bach_midi)\n\n**3D Library** -- [game_examples/3d_library](game_examples/3d_library)\n\n**Sprite Game** -- [game_examples/spritegame](game_examples/spritegame)\n\n**Conway's Game of Life** -- [game_examples/life](game_examples/life)\n\n**Bouncing Square** -- [game_examples/bouncing_square](game_examples/bouncing_square)\n\n**Go Logo** -- [game_examples/go_logo](game_examples/go_logo)\n\n**Hello World** -- [game_examples/hello_world](game_examples/hello_world)\n\n\n## A Tour Of Go\n\nThe official Go language tutorial — [A Tour of Go](https://go.dev/tour/) — has been adapted to run on Playdate with PdGo, out of the box, on both the Simulator and the device.\n\nIf you are coming from C or Lua gamedev and want to learn Go, this is the fastest way to try every language feature hands-on: packages, functions, control flow, pointers, structs, arrays, slices, maps, closures, methods, interfaces, type assertions, generics, errors, and io.Reader — all running directly on Playdate hardware.\n\nAll examples are located in the `tour_of_go/` directory. Each example is a self-contained PdGo project with its own `build.sh`.\n\n**Build all examples at once:**\n```bash\ncd tour_of_go\nchmod +x build_all.sh\nchmod +x */build.sh\n./build_all.sh\n```\n\n**Build a single example:**\n```bash\ncd tour_of_go/17_for\nchmod +x build.sh\n./build.sh\n```\n\nThe examples cover Go fundamentals (01-26), pointers and structs (27-32), slices (33-41), maps (44-47), functions and closures (48-49), methods (50-57), interfaces (58-62), type assertions and switches (64-65), Stringer (66), errors (67), io.Reader (68), and generics (`generics_type_parameters`, `generics_generic_types`, `generics_all`).\n\nAll examples are device-tested and avoid [known TinyGo ARM fmt issues](#known-issues-).\n\n---\n\n## Roadmap\n\n- [ ] Add more own complex code examples to cover and test all API subsystems\n- [ ] Rewrite to Go all official examples from SDK\n    - [x] Hello World\n    - [x] Life\n    - [x] Tilemap\n    - [x] Sprite Game\n    - [x] Sprite Collisions\n    - [x] Particles\n    - [x] Networking\n    - [x] JSON\n    - [x] Exposure\n    - [x] Bach.mid\n    - [ ] Array\n    - [x] 3D Library\n    - [ ] 2020\n    - [ ] Accelerometer Test\n    - [ ] Asteroids\n    - [ ] ControllerTest\n    - [ ] Drum Machine\n    - [ ] Flippy Fish\n    - [ ] Game Template\n    - [ ] Hammer Down\n    - [ ] Level 1-1\n    - [ ] MIDI Player\n    - [ ] Node7Driver\n    - [ ] Networking\n    - [ ] Pathfinder\n    - [ ] Single File Examples\n    - [ ] Sprite Collisions Masks\n- [ ] Make sure Lua interoperability works\n- [ ] Make sure C interoperability works\n- [X] Write documentation for API bindings\n- [x] Add Go-Tour like code examples to demostrate language's syntax and semantic to newcomers  \n- [ ] Add different benchmarks to compare Go with C and Lua\n- [ ] Investigate: concurrency: goroutines/scheduler support for single-threaded CPU\n- [ ] Implement conservative mark-and-sweep GC for Playdate's constraints\n- [ ] Create unit tests for `pdgoc` and API bindings\n- [ ] Add support for Windows OS\n\n## Contribution\n```bash\n# 1. Fork the repo on GitHub first (via the web UI), then:\n\ngit clone https://github.com/\u003cyour-github-username\u003e/pdgo.git\ncd pdgo\n\n# 2. Make sure you are on the main branch\ngit checkout main\ngit pull origin main\n\n# 3. Create a feature branch based on main\ngit checkout -b my_feature\n\n# 4. Make your changes, then stage only what you need\ngit add path/to/changed_file.go   # or several files\n\n# 5. Commit with a meaningful message\ngit commit -m \"Describe what this change does\"\n\n# 6. Push your branch to your fork\ngit push origin my_feature\n\n#Go to your fork on GitHub, you’ll see a banner offering to “Compare \u0026 pull request”.\n\n# Open a pull request from my_feature in your fork to playdate-go/pdgo’s main (or whichever target branch you use).\n```\n\nVerify unit tests pass \n```bash\ncd cmd/pdgoc\ngo test ./config/... ./pdxinfo/... -v\n```\n\nVerify all examples compile\n```bash\nchmod +x game_examples/build_all.sh \nchmod +x game_examples/*/build.sh\n./game_examples/build_all.sh\n```\n\n## Community\nUsing these links and places, you can discuss the PdGo project with each other:\n\nSlack\n1) https://gophers.slack.com/archives/C029RQSEE/p1769119174451979\n2) https://gophers.slack.com/archives/CDJD3SUP6/p1769119574841489\n\nReddit:\n1) https://www.reddit.com/r/golang/comments/1qk1ec9/golang_support_for_playdate_handheld_compiler_sdk/\n2) https://www.reddit.com/r/PlaydateDeveloper/comments/1qk0r60/golang_support_for_playdate_handheld_compiler_sdk/\n3) https://www.reddit.com/r/programming/comments/1qk19kb/playdate_supports_go_language_compiler_sdk/\n4) https://www.reddit.com/r/PlaydateConsole/comments/1qk0wy0/golang_support_for_playdate_handheld_compiler_sdk/\n\n**Discord**:\n1) https://discord.com/channels/118456055842734083/1464001888243548181\n2) https://discord.com/channels/675983554655551509/1464004567476867247\n\n**Playdate Development Forum (this is the main place to discuss)**:\nhttps://devforum.play.date/t/golang-support-for-playdate-compiler-sdk-bindings-tools-and-examples/24919\n\n## Attribution\n\nThe Go Gopher was designed by [Renee French](https://reneefrench.blogspot.com/)\nand is licensed under [Creative Commons 4.0 Attribution License](https://creativecommons.org/licenses/by/4.0/).\n\n## License\n\nMIT License\n\nCopyright (c) 2026 Roman Bielyi and PdGo contributors\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fplaydate-go%2Fpdgo","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fplaydate-go%2Fpdgo","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fplaydate-go%2Fpdgo/lists"}