Processing is experimenting with a Rust core for modern rendering, and it could open new doors for artists, educators, and creative coders.
https://rustfoundation.org/media/built-with-rust-processing-foundation/
For more than two decades, Processing has been one of the most welcoming entry points into programming for people who think visually. Artists, designers, students, and educators have used it to sketch ideas in code, generate generative art, prototype interactive installations, and learn the fundamentals of computation through immediate visual feedback. Built primarily around Java (with modes and ports for JavaScript, Python, and others), it has always prioritized accessibility and rapid iteration over raw systems-level performance.
That landscape is shifting. The Processing community is exploring a new experimental foundation called libprocessing: a native implementation of the core Processing API written in Rust and powered by the Bevy game engine, with WebGPU as its rendering backend. The project aims to support desktop, mobile, and web targets over time. A talk at Libre Graphics Meeting 2026 framed this as a way to modernize Processing’s rendering while expanding what the project can become.
Why Rust?
Rust brings several practical advantages that map well to creative coding’s evolving needs:
- Performance and modern graphics. Older OpenGL-based approaches are showing their age. WebGPU and contemporary GPU APIs enable better use of current hardware, including more efficient rendering, compute capabilities, and future-proofing for higher-resolution or more complex visuals. Rust’s low-level control and zero-cost abstractions help deliver that without the usual C/C++ complexity tax.
- Cross-platform and embeddable potential. Because Rust compiles to native code and can expose a C-compatible interface (via FFI), the same core can be called from Java (the traditional Processing environment), Python, or other languages. There is also work toward WebAssembly bindings, which could eventually strengthen browser-based creative tools.
- Safety and reliability. Memory safety without a garbage collector reduces entire classes of crashes and subtle bugs—valuable when sketches grow into longer-running installations, generative systems, or tools shared with students.
- Access to a rich ecosystem. Building on Bevy gives Processing a modern application and graphics framework to draw from, while still aiming to preserve the immediate-mode, procedural style that makes Processing feel like sketching.
The project is still early and explicitly labeled experimental and unstable. The goal is not a full rewrite that discards the familiar Processing experience, but a shared native core that different language front-ends can use. In principle this could keep the friendly “setup and draw” workflow while improving the underlying engine.
Benefits for the creative community
Creative coders often hit practical limits: sketches that become sluggish with complex geometry or particle systems, difficulty targeting new platforms cleanly, or friction when moving from a classroom prototype to a more robust interactive piece. A stronger native core addresses several of these pain points.
Artists and generative practitioners could work with more ambitious real-time visuals or higher-fidelity output without abandoning the approachable API they already know. Educators might eventually offer students a path that starts with the same friendly syntax and later reveals higher-performance or systems-oriented options. Tool builders and library authors could create new language bindings or hybrid applications more easily if a stable C ABI layer emerges.
The broader creative coding world already has strong Rust options (nannou and others), but an official Processing effort carries unique weight: it keeps the large existing community, documentation, teaching materials, and cultural emphasis on accessibility connected to modern technology rather than forcing a complete migration. It also signals that Processing intends to remain relevant as graphics hardware and deployment targets continue to evolve.
There are trade-offs, of course. Rust’s learning curve is steeper than Processing’s deliberately simplified Java dialect, so most users will continue interacting through higher-level language modes rather than writing Rust directly. The experimental status means features, stability, and documentation will take time. Not every long-standing Processing library will magically transfer overnight. Still, the direction is promising: preserve the creative, exploratory spirit while upgrading the technical foundation.
In short, Processing’s exploration of a Rust-based rendering core is less about abandoning its roots and more about giving the creative community better tools for the next twenty-five years. Faster, more portable, and more capable visuals—without losing the sense that programming can feel like drawing. For a community that has always valued both art and accessibility, that combination is worth watching closely.
libprocessing integrates Bevy as its core application framework and rendering foundation while preserving Processing’s immediate-mode creative coding style.
libprocessing is an experimental Rust library that reimplements the core Processing API as a native, cross-platform layer. It sits on top of Bevy (via a project-maintained fork) and uses wgpu (WebGPU) as the rendering backend. The goal is to support desktop, mobile, and web targets eventually, while exposing a C ABI (via cbindgen and FFI) so languages like Java, Python (mewnala via PyO3), and others can call into it.
Why Bevy?
Bevy’s design aligns well with Processing’s needs:
- Modularity and ECS — Bevy is built around an Entity-Component-System architecture rather than a monolithic engine. This lets libprocessing pick only the needed pieces (2D/3D rendering, windowing, input, assets, etc.) and compose them.
- Modern graphics — Strong commitment to WebGPU/open standards, which replaces the aging OpenGL/JOGL path used by classic Processing.
- Cross-platform and embeddable — Rust + Bevy makes native performance, embedding via C ABI, and targets like desktop (with Wayland/X11/winit), Android, web (via WebAssembly bindings), and more practical.
- Broader ambitions — The Bevy community is interested in non-game uses (art, scientific visualization, CAD-like tools), which matches Processing’s creative coding focus.
The project depends on a forked Bevy (https://github.com/processing/bevy, main branch) with a selective feature set (2D/3D Bevy render, UI, picking, scene, winit, multi-threading, fonts, cameras, shader formats, etc.). Patches ensure consistency across Bevy sub-crates. There is a note to eventually return to upstream Bevy once custom needs are resolved.
Technology stack layers
From outer to inner:
- cbindgen — Generates C headers for FFI consumers (Java via Project Panama or similar, Python, etc.).
-
Rust FFI layer —
extern "C"functions that map C-compatible types to the libprocessing API. - libprocessing — The primary Processing-style API (procedural/immediate-mode).
- Bevy — App structure, ECS, graphics features, windowing, assets.
- wgpu — Bevy’s rendering hardware interface (WebGPU).
- Vulkan / Metal / etc. — Low-level GPU APIs.
The core architectural challenge: immediate mode on top of retained-mode ECS
Processing sketches are immediate-mode: you call rect(), background(), etc., and drawing happens in sequence with global mutable state. Bevy is retained-mode: a scene database of entities + components, systems that query/transform data (often in parallel), and a separate rendering phase that batches work across frames.
libprocessing deliberately presents the familiar immediate-mode API while implementing it on Bevy’s ECS. This requires inverting several Bevy defaults:
-
Recording instead of immediate execution — Draw calls do not spawn entities right away. Intent is recorded as
DrawCommands into a per-graphicsCommandBuffer. This preserves call order and allows controlled batching. -
Synchronous frame control — Bevy normally manages its own main loop and pipelined rendering. libprocessing keeps the
Appin a thread-local and callsapp.update()only when the user explicitly flushes (i.e., when a change requires rendering due to data dependencies). -
Selective rendering — Cameras are disabled by default (
activefield). AFlushmarker component is added to the relevant surface when rendering is needed. Systems that produce renderable data check forFlushso they only act on the requested surface. - Transient geometry — Shapes exist only for the frame they are drawn. Mesh entities are spawned on flush and despawned before the next frame. The ECS acts as a temporary staging area rather than a persistent scene graph.
-
Camera write control — Cameras use
CameraWriteMode::Skipso intermediate textures are not written to the finalRenderTargetuntilendDraw(or equivalent).
As long as camera state is managed correctly, calling app.update() or individual systems does not trigger unwanted renders or presents.
Practical integration details
-
Handles, not raw pointers — Long-lived data is represented by Bevy
EntityIDs (returned asu64combining index + generation). Consumers never receive pointers into Rust-owned memory. Destructor functions remove entities and free resources; higher-level language wrappers own the lifetime. -
Working with Bevy systems and borrows — Immediate-mode code frequently needs imperative world mutation. Strategies include:
- Running systems via
run_system_cached_withthat takeIn<T>(or tuple) parameters. - Collecting query results into intermediate collections to release borrows.
- Using
resource_scopeor temporarily removing/re-adding resources.
- Running systems via
-
Threading model — Currently single-threaded on the main thread (important for macOS windowing and simplicity).
app.update()runs the main + render schedules blocking; asset loads via Bevy’sAssetServerare blocking. Multi-threading may be explored later for performance. - Error handling and safety — Designed so FFI consumers cannot easily cause undefined behavior or process crashes through normal calls. Errors are treated as exceptional (often halt). Validation helpers can return user-friendly messages.
-
API philosophy — Closely follows the existing Processing API but fixes historical issues where practical and exposes useful Bevy capabilities (e.g., more texture formats) even if higher-level modes cannot fully use them yet. Naming is descriptive (
processing_graphics_..., etc.). Learnings from p5.js are considered.
Current status and related pieces
The project is explicitly R&D / highly unstable (recent releases around v0.0.8). It includes examples for 2D/3D primitives, transforms, materials, PBR, particles (CPU and GPU), text, lighting, glTF, MIDI/audio, input, filters, and more. There is also a Python binding (mewnala) and WASM work. Audio integration has used packages like bevy_seedling.
In short, Bevy supplies the modern, modular, high-performance graphics and app infrastructure. libprocessing carefully layers an immediate-mode Processing façade on top through command buffering, controlled flushing, transient entities, and careful camera management. This keeps the creative, sketch-like experience while unlocking contemporary GPU capabilities and multi-language embedding. The design is still evolving, so details may change as the experimental work matures.
Top comments (0)