PixiJS-Style Rendering in Go with Willow
If you know PixiJS, Willow will feel familiar. Concept mapping, side-by-side code comparisons, and a guide to being productive in Go fast.
If you have spent time with PixiJS, you already know the feeling. You create a sprite, add it to a container, set its position, and it just appears on screen. The display tree handles draw order, transforms, and batching for you. You think in terms of objects and parent-child relationships, not raw draw calls and matrix math. It is one of the most productive mental models in 2D rendering, and it is the reason PixiJS has powered so many games, interactive experiences, and creative tools on the web.
Willow brings that same mental model to Go and Ebitengine. It was directly inspired by PixiJS and Starling; the display tree, sprite batching, filter chains, and hierarchical input are all design decisions borrowed from those two frameworks. If you are a PixiJS developer exploring Go for performance, cross-platform distribution, or just a change of pace, Willow should feel immediately familiar.
Concept Mapping: PixiJS to Willow
The core abstractions map almost one-to-one. Here is a reference table you can bookmark while getting oriented.
| PixiJS | Willow | Notes |
|---|---|---|
Application | Scene | The root entry point. Willow's Scene owns the display tree, camera, and render loop integration. |
Container | Node | A generic parent that groups children, propagates transforms and alpha, and supports z-index ordering. |
Sprite | Sprite | Textured quad with position, rotation, scale, anchor, and alpha. Willow sprites pull regions from a texture atlas. |
Graphics | Mesh rendering | Willow uses polygon and mesh nodes for procedural geometry rather than an imperative drawing API. |
Text / BitmapText | TextNode / PixelFont | TextNode uses SDF rendering with FontFamily support. PixelFont handles crisp bitmap text at integer scales. |
Ticker | Ebitengine's Update() loop | No ticker object. Ebitengine calls your Update and Draw methods at a fixed tick rate. |
filters (GLSL) | Filter chains (Kage shaders) | Composable filter slices. Built-in blur, outline, color matrix, palette swap, and drop shadow. Custom filters use Kage. |
InteractionManager | Hit testing | Hierarchical hit testing on nodes with rect, circle, and polygon hit shapes. Pointer, touch, and keyboard callbacks. |
Side-by-Side Code Examples
The best way to see the mapping is through code. Each example shows the PixiJS approach followed by the Willow equivalent.
Creating a Sprite and Adding It to the Stage
PixiJS (JavaScript):
Willow (Go):
The structure is nearly identical. Willow's NewSprite takes a region name and a texture atlas instead of a file path, which enables automatic draw call batching across sprites that share the same atlas.
Setting Up a Camera / Viewport
PixiJS does not include a built-in camera. Most projects either use the pixi-viewport plugin or manually offset the stage container.
PixiJS (manual approach):
Willow (built-in camera):
Willow's camera is a first-class object with follow targets, scroll-to animation, and frustum culling. Objects outside the viewport are skipped entirely during rendering.
Adding a Click Handler
PixiJS:
Willow:
Both frameworks route input through the display tree. Willow uses typed callback functions instead of string-based event names, so the compiler catches typos and type mismatches before your code ever runs.
Applying a Blur Filter
PixiJS:
Willow:
Willow filters are composable slices, just like PixiJS filter arrays. You can chain blur, outline, color matrix, and palette swap filters together. The key difference is the shader language: Willow uses Kage (Ebitengine's shader language) instead of GLSL.
What's Different
The mental model transfers well, but the underlying platform is fundamentally different. Here are the things that will feel unfamiliar coming from PixiJS.
Go's type system replaces the prototype chain. PixiJS uses JavaScript's prototype inheritance; you can add properties to any display object at runtime. In Willow, every node has a concrete type. You cannot attach arbitrary data to a sprite. Instead, you embed Willow nodes in your own structs or use the node's metadata map. This is more verbose, but it means the compiler validates your scene graph structure before it ever executes.
Update/Draw replaces ticker callbacks. PixiJS runs a ticker that fires callbacks every frame. Ebitengine uses a different model: your game implements Update() and Draw() methods. Update is called at a fixed tick rate (default 60 TPS) for logic, and Draw is called whenever the screen needs to repaint. There is no ticker object to subscribe to. You put your game logic in Update and call scene.Draw(screen) in Draw.
Kage shaders instead of GLSL. If you have written custom PixiJS filters, you will need to learn Kage. It is a Go-like shader language that compiles to the platform's native shader format (GLSL, Metal, HLSL, or SPIR-V depending on the target). The syntax is simpler than GLSL. There are no version directives, no precision qualifiers, and no preprocessor macros. The trade-off is that Kage is less expressive; it targets a specific subset of GPU operations.
Compiled vs interpreted. PixiJS runs in a JavaScript engine with JIT compilation. Go compiles to native machine code ahead of time. You lose the instant feedback loop of refreshing a browser tab, but you gain deterministic performance and a single binary that runs without a runtime. The edit-compile-run cycle in Go is fast; typically under two seconds on a modern machine.
What's Better in Willow
PixiJS is excellent software. It has a mature ecosystem, broad browser support, and a large community. Willow is not trying to replace it. These are different tools with different trade-offs. That said, there are areas where Willow's approach has clear advantages.
- Compiled language performance. Go compiles to native code. There is no garbage collector pause in the hot loop because Willow is designed to produce zero heap allocations per frame on the rendering path. In JavaScript, even careful code can trigger GC pauses that cause frame drops.
- Zero heap allocations on the hot path. Willow's render loop reuses all intermediate buffers and avoids allocations during traversal, batching, and draw submission. This matters for games targeting 120+ FPS or running on constrained hardware.
- Type safety catches errors at compile time. Misspelled event names, wrong callback signatures, invalid property assignments; these are runtime errors in PixiJS and compile-time errors in Willow. The feedback loop is different (you see errors in your editor or terminal, not in the browser console), but the errors surface earlier.
- No bundler, no transpiler, no npm dependency chain. A Willow project is a Go module. You run
go buildand get a binary. There is no webpack, no babel, no node_modules folder with hundreds of transitive dependencies. The build toolchain is the Go compiler and nothing else. - Single binary output. Your game ships as one executable file. No HTML wrapper, no asset loading from a web server, no CORS configuration. You can still target WebAssembly if you need the browser, but the default output is a native binary for Windows, macOS, or Linux.
- Built-in systems that PixiJS needs plugins for. Cameras, particle emitters, tile maps, and tween animation are all part of Willow's core. In PixiJS, these require third-party plugins (pixi-viewport, @pixi/particle-emitter, @pixi/tilemap, gsap) with varying levels of maintenance.
Getting Started
If you already know PixiJS, you can be productive in Willow within an afternoon. The concepts transfer directly; you just need to learn the Go syntax and Ebitengine's game loop model.
First, make sure you have Go installed (1.22 or later). Then create a new module and pull in Willow:
From there, the First Scene guide walks you through creating a scene, loading a texture atlas, adding sprites, and setting up a camera. It is the Willow equivalent of PixiJS's "Getting Started" tutorial, and it intentionally follows a similar structure.
For a deeper look at why Willow exists and how it fits into the Go game development ecosystem, read Why Willow?. It covers the rendering gap in Ebitengine that Willow fills, performance characteristics, and the types of projects it is designed for.
For a full concept mapping between PixiJS and Willow, including side-by-side code comparisons, see Willow vs PixiJS.
If you are coming from PixiJS, the biggest adjustment is not the API surface; it is the workflow. You will write code in an editor, compile, and run a native window instead of refreshing a browser tab. The display tree, the scene graph, the filter system; those all work the way you expect. The rest is just Go.