Open to Collab

Rusty Christmas tree

A LED Christmas Tree controlled by Rust. Contribute your own renderers!

Rusty Christmas Tree

This is code that draws on the LED Christmas Tree made by @aidancrowther. You can see his 3D design files and Pi Pico setup code on his project OpenPixelTree.

Adding your own renderer

If any of this doesn't make sense, write an issue and I'll try to make it more clear 👍

This should get you started with making something to display on the tree. There is a Nannou visualizer included so you can see what it will look like before making a PR. You'll be able to run this with make visualize from the root.

To write your own code to run on the tree, you need to implement a "renderer". A renderer is just a Rust module that implements a draw function, and returns a TreeCanvas. You can find the renderers in this folder. Here is an example of what you'll implement:

pub fn draw(tick: u64) -> TreeCanvas {
    let mut canvas = TreeCanvas::new();
    for y in 0..75 {
        for x in 0..20 {
            let this_pixel = Pixel {
                r: (
                    (tick as f64) // Start by converting the tick to a 64 bit float
                    .sin() // The sin will be between -1 and 1
                    .abs() // Get the absolute value so we are between 0 and 1
                    * 150.0 // Multiply by 150 to get a number between 0 and 150
                    + 100.0
                    // ^^ Add 100 to get a number between 100 and 250
                ) as u8, // Convert the float to an 8 bit integer
                g: 0,
                b: 0,
            canvas.set_pixel(x, y, this_pixel)

To add a new renderer, start by duplicating the template folder and giving it a new name. You'll then have to add code in several different places in the project:

  • First, add your module here.
  • Second, add a new enum variant here.
  • Next, we'll have to add this new variant to several match statements here, here, and here.
  • Finally, set the default vizualizer renderer to your new renderer here.

Hopefully I get some time to fix this eventually, but I don't know how right now. Now, you can start working in the file in the new renderer folder.

At this point, you should also be able to run the visualizer and see your renderer in action.

make visualize

Once you have something cool, make a pull request!

Other Make Commands

build: build tree-writer for the Raspberry Pi (requires Docker and cargo-cross)
deploy: scp the binary to the Pi
run: (build + deploy) build tree-writer for the Raspberry Pi and scp the binary to the Pi
visualize: run the Nannou visualizer
setup-web: install the prerequisites for the frontend
frontend: serve the frontend
frontend-release: serve the frontend in release mode


The plan for this project is to have many renderers implemented that show off different displays (tree-writer). There will be a web server running (tree-backend) that allows you to change what is currently being displayed on the tree. A simple web frontend (tree-frontend) will display all the options of renderers and allow you to pick one to display.

On the Raspberry Pi, there will be a process (tree-deploy) that runs the main process (tree-writer). The first process will look for new releases from Github periodically. If there is a new release, it will update the and restart the main process.

While you're developing, you can run the visualizer (tree-visualizer) to see what your renderer will look like on the tree.

Physical Tree

The physical tree is running on a Raspberry Pi Pico. There are 20 (actually 19) strips running, creating 19x75 LED grid. You send data to the LED strings using spidev, and send 4500 8-bit numbers for each frame. Up to 33 frames can be drawn per second. The light indexes on the tree are as follows:

3 4 9
2 5 8
1 6 7


This crate is where the different "renderers" are implemented. A renderer is just a module that implements a draw function, and returns a TreeCanvas. In this function, you can set any Pixel{r: u8, g: u8, b: u8} on the TreeCanvas.


This crate uses Nannou to visualize different renderers. It renders at the same speed (I think:tm:) and orientation that will be displayed on the tree.


This crate uses Warp to serve a basic web server. It allows you to change the renderer that is currently being displayed on the tree. The tree-writer will ping this server about once a second to see if there is a new renderer to switch to.


This crate uses Yew to create a basic frontend for the tree-backend. It will just have a list of buttons.


This was an attempt to use the Mun scripting language as the backend for drawing to the canvas. Currently, this isn't working, but if an MVP is made, then it could be easier to write with, and hot reloadable as well.


Licensed under either of

at your option.


Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.