Start a new tradition and design your own Tree Topper. Celebrate this holiday with DIY electronics from Adafruit. Our tutorial will show you how to use Autodesk's 123D Design software and a 3D Printer to create a custom tree topper.

This is a great "Learn to 3D model & print" project as the shape is fairly simple, snaps together and can be customized.
This guide was written for the 'original' Gemma board, but can be done with either the original or M0 Gemma. We recommend the Gemma M0 as it is easier to use and is more compatible with modern computers!

Prerequisite guides

Tools & Supplies

  • Soldering iron
  • Solder
  • Scissors
  • Wire strippers
  • Wire spool set
  • Double sided foam tape
  • 3D Printer


This diagram uses the original Gemma but you can also use the Gemma V2 or M0 with the exact same wiring!

Prototype Circuit

Use small alligator clips to connect the components together for testing; we’ll solder it up later. The pins on the NeoPixel strip are small, so be sure to double check your connections if your having trouble getting them to light up. It might be easier to solder wires to the NeoPixel strip and then alligator clip to those. The NeoPixel strips digital input connects to pin D1 of the Gemma. The negative connection of the NeoPixel strip goes to the ground pin on the Gemma. The positive power wire of the NeoPixel LED strip connects to the Vout pin of the Gemma (not 3Vo!).

If you have all your connections securely clipped, attach the rechargeable battery to the JST port on the Gemma to power it on. The NeoPixel LEDs won't light up because there's no code loaded on the Gemma yet! Use a USB cable to connect the Gemma to your desktop.

Even though the current “v2” and “M0” versions of Gemma now include an onboard power switch, we’ll be leaving it in the “on” position and adding a separate switch of our own, so the circuit can be easily turned on or off after it’s installed in the 3D-printed case.

The Arduino code presented below works equally well on all versions of GEMMA: v1, v2 and M0. But if you have an M0 board, consider using the CircuitPython code on the next page of this guide!

The Code

The Gemma is programmed via USB with the Arduino IDE. You can modify and customize the code to fit your setup. For starters, we can easy change the pin outs and number of NeoPixels. In our setup, our tree topper used 36 NeoPixels.

If your new to using the Gemma and the Arduino IDE, check out this guide for the original Gemma  or this guide for the Gemma M0 for setting up. Have some extra time on your hands? Learn how to customize the code to change the animation sequence!

We slightly modified another project’s code to use a single purple color, but you can change it to whatever you like! Look for this line where a hexadecimal color value is listed (around line 10):

uint32_t color = 0xA000A0;

Just replace "#A000A0" with whatever hex color value you want, it's that easy!

// SPDX-FileCopyrightText: 2017 Phillip Burgess for Adafruit Industries
// SPDX-License-Identifier: MIT

#include <Adafruit_NeoPixel.h>

#define PIN       1
#define NUM_LEDS 36

Adafruit_NeoPixel pixels = Adafruit_NeoPixel(NUM_LEDS, PIN);

uint8_t  mode   = 0,        // Current animation effect
         offset = 0;        // Position of spinner animation
uint32_t color  = 0xA000A0; // Purple
uint32_t prevTime;          // Time of last animation mode switch

void setup() {
  pixels.setBrightness(255); // Full brightness
  prevTime = millis();       // Starting time

void loop() {
  uint8_t  i;
  uint32_t t;

  switch(mode) {

   case 0: // Random sparkles - just one LED on at a time!
    i = random(NUM_LEDS);           // Choose a random pixel
    pixels.setPixelColor(i, color); // Set it to current color;                  // Refresh LED states
    // Set same pixel to "off" color now but DON'T refresh...
    // it stays on for now...both this and the next random
    // pixel will be refreshed on the next pass.
    pixels.setPixelColor(i, 0);
    delay(10);                      // 10 millisecond delay
   case 1: // Spinny wheel
    for(i=0; i<NUM_LEDS; i++) {    // For each LED...
      uint32_t c = 0;              // Assume pixel will be "off" color
      if(((offset + i) & 7) < 2) { // For each 8 pixels, 2 will be...
        c = color;                 // ...assigned the current color
      pixels.setPixelColor(i, c);  // Set color of pixel 'i'
    };                 // Refresh LED states
    delay(90);                     // 90 millisecond delay
    offset++;                      // Shift animation by 1 pixel on next frame

    // More animation modes could be added here!

  t = millis();                    // Current time in milliseconds
  if((t - prevTime) > 8000) {      // Every 8 seconds...
    mode++;                        // Advance to next animation mode
    if(mode > 1) {                 // End of modes?
      mode = 0;                    // Start over from beginning
    pixels.clear();                // Set all pixels to 'off' state
    prevTime = t;                  // Record the time of the last mode change

Test Code

Once you have the code loaded to the Gemma from the Arduino IDE, it should automatically run and make the Gemma to light up the NeoPixel strip. If your having problems or issues getting it to work, check your connections and be sure to skim the Gemma and NeoPixel guides.

GEMMA M0 boards can run CircuitPython — a different approach to programming compared to Arduino sketches. In fact, CircuitPython comes factory pre-loaded on GEMMA M0. If you’ve overwritten it with an Arduino sketch, or just want to learn the basics of setting up and using CircuitPython, this is explained in the Adafruit GEMMA M0 guide.

These directions are specific to the “M0” GEMMA board. The original GEMMA with an 8-bit AVR microcontroller doesn’t run CircuitPython…for those boards, use the Arduino sketch on the prior page of this guide.

Below is CircuitPython code that works similarly to the Arduino sketch shown on theprior page. To use this, plug the GEMMA M0 into USB…it should show up on your computer as a small flash drive…then edit the file “” with your text editor of choice. Select and copy the code below and paste it into that file, entirely replacing its contents (don’t mix it in with lingering bits of old code). When you save the file, the code should start running almost immediately (if not, see notes at the bottom of this page).

If GEMMA M0 doesn’t show up as a drive, follow the GEMMA M0 guide link above to prepare the board for CircuitPython.

# SPDX-FileCopyrightText: 2017 Phillip Burgess for Adafruit Industries
# SPDX-License-Identifier: MIT

import time

import board
import neopixel

    import urandom as random  # for v1.0 API support
except ImportError:
    import random

numpix = 36  # Number of NeoPixels
pixpin = board.D1  # Pin where NeoPixels are connected
strip = neopixel.NeoPixel(pixpin, numpix, brightness=1.0)

mode = 0  # Current animation effect
offset = 0  # Position of spinner animation
color = [160, 0, 160]  # RGB color - purple
prevtime = time.monotonic()  # Time of last animation mode switch

while True:  # Loop forever...

    if mode == 0:  # Random sparkles - lights just one LED at a time
        i = random.randint(0, numpix - 1)  # Choose random pixel
        strip[i] = color  # Set it to current color
        strip.write()  # Refresh LED states
        # Set same pixel to "off" color now but DON'T refresh...
        # it stays on for this and the next random
        # pixel will be refreshed on the next pass.
        strip[i] = [0, 0, 0]
        time.sleep(0.008)  # 8 millisecond delay
    elif mode == 1:  # Spinny wheel (4 LEDs on at a time)
        for i in range(numpix):  # For each LED...
            if ((offset + i) & 7) < 2:  # 2 pixels out of 8...
                strip[i] = color  # are set to current color
                strip[i] = [0, 0, 0]  # other pixels are off
        strip.write()  # Refresh LED states
        time.sleep(0.08)  # 80 millisecond delay
        offset += 1  # Shift animation by 1 pixel on next frame
        if offset >= 8:
            offset = 0
    # Additional animation modes could be added here!

    t = time.monotonic()  # Current time in seconds
    if (t - prevtime) >= 8:  # Every 8 seconds...
        mode += 1  # Advance to next mode
        if mode > 1:  # End of modes?
            mode = 0  # Start over from beginning
        strip.fill([0, 0, 0])  # Turn off all pixels
        prevtime = t  # Record time of last mode change

This code requires the library. A factory-fresh board will have this already installed. If you’ve just reloaded the board with CircuitPython, create the “lib” directory and then download from Github.


Most common tree toppers designs are in the shape of a star, but yours can be what ever shape you desire! We used our Adafruit logo for the base shape. Do a bit of googling to find images of tree toppers for inspiration, there are so many! For more homework, why not explore the thingiverse site for ideas that have been printed.

Create Artwork

We used illustrator to create the Adafruit logo, but you can any use any freeware open sourced vector art programs. It's best to use a solid shape and avoid complex patterns that might be too small to make on a 3D printer. Our Adafruit logo has five holes in the center that is simple enough to turn into an extruded shape.

Import SVG

After you have your base shape, merge the paths so they are manifold and save it out as an SVG. The dimensions of your vectors doesn't have to be exact, you will modify the size in the modeling app.

In Autodesk 123D Design web app, import your SVG by selecting "Import" under the file menu (or command+shift+i). Your shape will load into the scene with the option to resize the dimensions. You must click on the grid layout to accept and load the shape.

Measure & Size

Once you click, you should see arrow handles appear on the shape. These allow you to move the object around in the grid. Use a measuring tool to determine the overall size of your tree topper. If you have a reference topper handy, measure the dimensions and compare it to the max build size of your 3D printer (make it as big as you can!). You can resize the shape at any time by selecting the object and changing the input values at the bottom of the UI. Use the "lock" icon to scale proportionately.

Save & Export

Once you have resized your shape to your desired measurements, save your project. With your design named and saved, goto the top right and click on your username. Select "My Projects" and then click on the "Models" link in the left sidebar. Locate your design and click on the thumbnail. Click the big blue Edit/Download button and select "Download 3D Models" from the drop down. Check the .smb file and select "Download Models" (you won't need the STL or recipe files).

123D Desktop App

Withe the app downloaded, click open and select the "browse my computer" tab to navigate to your downloaded design (the .smb file). Select your design to open it. The desktop app is similar to the web app but different in terms of boolean operations (combining and subtracting shapes versus object types like solids and voids). We found the current web app to be a bit slower and unstable at times but feel free to test both of them out and see which one works for you.

Extrude Object

Once you have the shape loaded and set to your desired measurements, you will need to make a duplicate of the shape and move it down and out of the way for now (don't delete this, you will need it for creating the cover) Extrude the shape so its thick enough to create a shell and hollow it out. To extrude, click on the shape to select it and then click once more on the top face. It should highlight blue. A gear icon will appear close to the object. Click on the gear icon and select the Press/Pull option. Enter the your desired distance for the height of the object. Test and see how thick you want your tree topper to be. Since you will be using NeoPixel strips inside the shape, it should be at least 12mm thick (Our tree topper is 20mm thick).

Shell Object

With your shape extruded to the desired thickness, click on the object to select it and then click on the top face to highlight it blue again. Click on the gear icon and choose the shell option. A new options menu will appear at the bottom of the screen. Put the value "1" in the input box of the Thickness Inside option. Click on an empty space in the grid to accept or hit enter to complete the shell function.

Remove Access

In our shape, we have undesired edges that were created during the shelling process. We can easily remove these sharp edges by subtracting with a few shapes. Simple cylinders can be added and subtracted to the shape by using the combine function. We used a total of 5 new cylinders to cut of our unwanted edges. This step is optional if your shape is simple and doesn't have any unwanted edges. Once you have your desired shell, now is a good time to save it!

Creating Cover

Move the duplicated base shape back to your work area. You will need to create 2 more duplicates (a total of 3 base shapes) to create a snap on cover. We need to make an inner edge on the cover so it can snap to the inside of the shell we created. To do this, we will create a shell out of one of the duplicates. Use a thickness of 1.3mm.

In our shape, we had to fill in the five holes that are in the center of our shape because we were having issues removing the access edges. We moved simple cubes to the holes and merged them to the duplicates to create a solid shape. This step is optional if you have a simple shape with no cut outs.
Once your cover is shelled, you should now have a frame. Align this new frame to the next duplicate shape and position it flush to the object. Click on the "Combine" icon in the top menu. Select the base shape that is inside of the shelled frame first, and then selected the the shelled frame. Now choose "Subtract" from the floating combine options menu. Click on an empty space in the grid to accept.
With your newly cut base shape selected, click on the top and bottom faces to highlight them blue. Use the shell function found in the floating gear menu. Enter the value "1" into the thickness inside option and hit enter to accept the function. Now you have your inner frame that should snap tightly onto your shelled case. Move the frame onto the bottom of the last remaining duplicate base shape and combine them together to make one object.

Creating Cone

Under the primitives shapes menu at the top, select a cone object. Move your mouse around in the grid and click to load a new cone shaped object. Our cone shape was 100mm in height with a radius of 15. We need to cut off 20mm from the top of the cone using a primitive cube shape.
With your newly trimmed cone, create a duplicate. Reposition and rotate the duplicate cone shape to the bottom of your shell shape or desired location. We are going to use this duplicate to make a cutout so the shell can fit on top of the cone. Use the combine and subtract option to cut the cone from the shell. Now you should have a hole in your shell.
Next, you will need to select the original cone. Select the top and bottom face of the cone to highlight it blue. Click on the gear icon and select shell. Use 1mm thickness.

Export STLs

Now you have your three pieces. The case, cover and cone. You will need to export each piece out individually as an STL. Use the top left menu (under the 123d logo) and select "Export STL". With your three stl's export, now its time to slice and 3d print them!
Depending on your 3D Printer, you will need to use a slicing program that supports your hardware. We're using a Makerbot Replicator 2 and slicing with MakerWare. We recommend using transparent PLA material for the cover, shell and cone. Transparent PLA difuses and softens the lights from the NeoPixel LEDs.
Our three piece enclosure design will house the Gemma, a recharger battery and the slide switch. The cover is designed to mount a slide switch on top for easy powering access. The cover snaps onto the top of the case. The hanger can be attached to the back of the base. The hanger has a hole that fits on to a small branch or twig. The case design is slim and non-intrusive.
Gemma Drum Enclosure
About 30 minutes
PLA @230
No Raft
No Support
.20 Layer Hieght

Printing Techniques

Build Plate Preparations
There's a great video tutorial by Dr. Henry Thomas who demonstrations a great technique for preparing acrylic build plates for awesome prints. Wipe down the plate with a paper towel lightly dabbed in acetone. Use another paper towel and apply a tiny dab of olive oil. Wipe down the plate so a small film of oil is applied, this will allow the parts to come off the plate easier.

Live Level
We recommend going raft-less for each piece because it will have the best quality result. Each piece will require a well leveled platform. We tend to "live level" our prints, meaning we adjust the build plates thumb screws while the print is laying down filament. This way we can make adjustments directly and improve the leveling by seeing how the extruders are laying down the first layer onto the build plate. We recommend watching the first layer so that you get a more successful print. If you see the layers aren't sticking or getting knocked off, you can always cancel print, peel it off and try again.
Once your pieces are printed, test out to see if the cover tightly snaps onto the case. Check to see if the slide switch fit through the cutout on the cover. If its too tight, you can use an X-Acto knife to shave off the edges. If its too lose, you can secure it in place with adhesive. The hanger will be attached to the back of the case with doubled-sided foam tape. Set these pieces aside for now. It's time to prototype some circuits!

Solder Connections

Once your have a working prototype, you will need to solder wires to the Gemma and NeoPixel strip for a solid connection. Start by measuring the length of wires you will need for the NeoPixel strip. We used the height of our cone as reference since it needs to reach to the end of the cone with some extra slack. You can use alligator clips to extend the topper and NeoPixel strip away from the Gemma and battery.

Start off by applying small blobs of solder to the pins on the Gemma. This will make it easier to solder three wires to the gemma. We color coded them so we could easily tell them apart. Red wire is the VBat positive connection. The blue wire is the digital input D1. The black wire is the GND ground pin.
Add small blobs of solder to the pins of the strip where the arrows are pointing to the right. These will make it easier to solder wires to these pins. Solder three wires to these pins on the NeoPixel strip. Black wire goes to the GND ground pin. Blue wire goes to the DI digital in pin. Red wire goes to the +5V positive pin.
Cut the red wire from the rechargeable battery in half. Expose some wire with wire strippers. We will need to solder a slide switch to these two ends. One in the middle, and the other to either the left or right. This will allow us to easily turn on and off the circuit.
Place the Gemma first inside the enclosure. Use a piece of double-sided foam tape to secure it to the bottom of the case. Slide the wires of the Gemma through the slit. Lay the battery on top of the Gemma and push the slide switch through the cut of of the cover. Snap on the cover.
Start by positioning the wires of the NeoPixel strip to the bottom hole cut out of the cone. NeoPixel Strips are friendly and can be cut to whatever size you want, just be sure to follow the cut indicator on the strip. Removing the protective gel allows you to bend it to the shape of your tree topper. Carefuly bend the strip to the inner lining of the shape.
Snap the cover on and secure the cone by using some hot glue or your preferred adhesive. Now you can test out your circuit to see if it still works. If it's all good, time go place it on the tree!

The battery will need occasional recharging. To do this, open the round case, disconnect the battery from the Gemma board and plug it into a LiPoly charger such as our Micro Lipo. The switch we added needs to be in the “on” position or the battery won’t charge! A full charge from a depleted state should take about three hours.

This guide was first published on Dec 06, 2013. It was last updated on Feb 08, 2024.