A high-performance image processing server that combines Go (HTTP/concurrency) with Rust (GPU-accelerated pixel processing) via CGo FFI.
Receives an image via HTTP, runs a WGPU compute shader on the GPU to convert it to grayscale, and returns the result — all with near-zero overhead between the two languages.
| Layer | What it does | Why |
|---|---|---|
| Go | HTTP server, routing, request handling | Go routines make it trivial to handle thousands of concurrent requests with minimal memory overhead. The standard library's net/http is production-grade out of the box. |
| Rust | Image decoding/encoding, GPU compute | No garbage collector means predictable, low-latency performance for heavy pixel workloads. The GPU compute shader (via wgpu) processes every pixel in parallel — orders of magnitude faster than a CPU-bound loop. |
| FFI | Bridge between Go and Rust | Zero-copy extern "C" interface: Go passes a pointer to the raw image bytes, Rust processes it on the GPU, and returns a pointer to the result. No serialization, no JSON marshalling — just raw memory. |
Go handles the I/O, Rust handles the math — each does what it does best.
Client ──POST /gray──► Go HTTP server
│
bridge.go (CGo)
│
┌─────▼──────┐
│ Rust DLL │
│ (wgpu) │
│ │
│ GPU │
│ compute │
│ shader │
└─────┬──────┘
│
Go writes response
│
Client ◄── PNG bytes─── image/png
The Rust library uses wgpu to run a compute shader on the GPU that converts each pixel to grayscale using luminance weights (0.299R + 0.587G + 0.114B), then re-encodes the result as PNG.
image-processor/
├── rust-lib/ # Rust library (cdylib)
│ ├── src/lib.rs # Extern "C" FFI + GPU compute pipeline
│ ├── Cargo.toml
│ └── Cargo.lock
│
├── go-server/ # Go HTTP server
│ ├── handlers/
│ │ ├── image.go # POST /gray handler
│ │ └── health.go # GET /health handler
│ ├── libs/ # Compiled rust_lib.dll goes here
│ ├── bridge.go # CGo bindings to Rust
│ ├── main.go # Entry point
│ └── go.mod
│
├── scripts/
│ ├── build.ps1 # Windows build script
│ ├── run.ps1 # Windows run script
│ ├── build.sh # Linux/macOS build script
│ └── run.sh # Linux/macOS run script
│
├── bin/ # Built server binary + DLL
├── Makefile
└── README.md
# Build Rust lib + Go binary
.\scripts\build.ps1
# Start the server
.\scripts\run.ps1# Build Rust lib + Go binary
make build
# Start the server
make runThe server listens on http://localhost:8080.
Use -F (multipart) so curl transmits the original filename — the server derives the output name from it.
curl -O -J -X POST http://localhost:8080/gray -F "image=@photo.jpg"This saves the grayscale result as photo-o.jpg automatically.
With explicit output file:
curl -X POST http://localhost:8080/gray -F "image=@photo.jpg" -o result.png| Endpoint | Method | Body | Response |
|---|---|---|---|
/gray |
POST | Raw bytes or multipart/form-data (image field) |
Grayscale PNG + Content-Disposition: filename="<input>-o.<ext>" |
/health |
GET | — | {"status":"ok"} |
- Go spawns a goroutine per request — thousands of concurrent connections are cheap.
- GPU compute shader processes every pixel in parallel (workgroup size 16×16). A 4K image (8.3M pixels) is handled in a single dispatch.
- Zero-copy FFI — Go passes a pointer, Rust writes directly to GPU and reads back the result. No memory copying between languages.
- No GC pauses during image processing — Rust's allocation is deterministic and dropped as soon as the response is written.