Have you ever wanted to build a robot, but don't know where to start? Or... are you looking for a project that you can cut-your-teeth on?

This servo-controlled animatronic robot head uses two servos for movement, two speakers for eyes and an LED mouth for a friendly remote-controlled robot.

This an advanced project, for people who already know how to control servos and LEDs. This tutorial will focus on the 3D printing and assembly of the 'bot head!

Robots, animatronics, and puppets - oh my!

You can get by with fewer tools than I've listed; if you have em' great... if not, no big.

  • 220 grit sand paper
  • screw driver
  • hot-glue gun
  • hand torch
  • soldering iron
  • variable speed drill
  • hand saw (for cutting the dowel)

Some of the parts might have tight tolerances... I plan on using a M3 tap to reduce the risk of splitting the plastic.
Alternatively, you could use the tip of your soldering iron to widen holes a bit.


  • cute desktop speakers for the eyes
  • 3D printed parts
  • 2X analog feedback servo
  • electret microphone
  • 5X 3mm LEDS
  • 3X 330ohm Resistors
  • 4X 8mm M3 screws
  • 10X 10mm M3 screws
  • 2X M3 nuts
  • 5X short wood screws
  • 1X 20mm X 120mm dowel (usually sold in meter lengths)
  • super glue
  • 5-minute epoxy

Grab your speakers and let's go!
Robot ideas often come to me as I peruse the Adafruit store... this time it was pair of desktop speakers that got me thinking about WALL-E and Short Circuit. The speakers reminded me of their eyes.

I thought to myself, "I wonder what earlier versions of WALL-E and Johnny 5 were like."

A few sketches later, I had a rough-cut on the head ready to go. It's a simple design, a U-joint and two armatures connected to two servos.
The red blocky-thing represents an Adafruit feedback servo - an incredibly useful component!
U-joints provide a huge amount of gestural freedom.
Limits in articulation are primarily a function of the armature... this is where feedback from the servos can come in useful; you'll be able to sense when a servo starts to bind.
It's a simple stack... all the 3D printed parts, with the exception of the base, can fit in the palm of your hand.

When you're ready, grab the 3D models and print-them-up in a color that suits your fancy; I choose black PLA to match the color of the speakers.
Print rod ends with a slow / fine setting for an optimal ball and socket fit. Everything else can be printed coarse and fast.
Like I said before, a tap isn't necessary... but it really does help prevent cracking in printed parts.
We want the screws to fit nice and snug in the tiny hub.
And fairly loose in the arms that cradle the hub.
Turn the hub as you go to make sure that it moves free and smooth.
If it binds too much, you're going to make the servos work much harder than they need to.
We need to add mounting caps for the dowel and the eye plate... so don't assemble the other side of the u-joint just yet.
You'll need 2X 8mm M3 screws here...
Tapping these holes isn't necessary, but you can if you like.
Just make sure that the screw is flush on both sides so that it doesn't get in the way of the u-joint as it pivots or the dowel as it sits flat in the socket.
More 2X 8mm M3 screws...
Secure the other side of the u-joint to the eye plate.
Make sure the screws are flush... you'll want the eyes (speakers) to sit flat on the base.
Now, go ahead and finish assembling the u-joint.
Wiggle the joint a bit as you add the screws to make sure that the joint doesn't bind this direction either.
Cut a dowel about 120mm long and mix-up some 5-minute epoxy. Glue the dowel into the mount connected to the eye-plate... and let it stand for at least 20-30 minutes.
Time to put the servo-plate together with 2X 8mm M3 screws.
Tap the holes if you prefer... or not. Just take it slow so that you don't crack the plastic.
ABS is a little more forgiving than PLA when you use the force.
The screws should be flush on the servo side so the servos sit nice and flat.
We'll use a short wood screw to hold the dowel in place.
You'll have to make holes in the plate for the screws that hold the servo in place.
A silver Sharpie works great on black PLA.
In some cases I prefer to 'drill' using a long hot soldering iron tip; it adds a little strength around the hole.
You might have to remove some of the mushroomed plastic around the hole so that the servo fits flat against the posts.
Test fit the holes with a screw to make sure that you got the width just-right - not to tight and not too loose.
The two pieces that form the neck should rotate freely.
Rod-ends are made from four bits of plastic and a 10mm M3 screw.
The screw goes into the bulb.
Two of these ball-joints screw into the back of the eye plate and two are bolted onto servo horn extensions.
If the two halves of the ball retainer aren't flat... smack them with a bit of sandpaper on a flat surface.
Insert a ball into one half of the retainer...
And make a sammich.
Hold the two retainer halves together with an end-cap. It's a little snug... use the force.
We'll make this permanent with plastic rivets. Start by heating one end of a bit of filament until it starts to slump a bit.
Mushroom the end of the filament out on a flat surface and let it cool.
Cut the rivet to length and insert it (two of them) into the rod-end.
Heat-up the other end of the rivet.
...and your flat surface might not work here to finish the rivet. You can use a pencil eraser to shape the other end of the rivet or live dangerously and pat the plastic with the tip of your finger.
Molten plastic can stick to your skin... and when it does, it will burn. Seriously.
Repeat these steps to make a set of four... two per armature.
Time for some hot-glue.
Squeeze in a bit into the rod-end.
Push the rod in all the way...
Add a little more hot-glue around the base / rod to make it extra secure.
A couple things here.

  • rods should be roughly the same length (+/- 2mm)
  • rod-ends should be aligned
Time to attach one end of the armature to a servo; start by cutting off one end of a servo horn.
Hot-glue the servo horn to the servo horn extension.
Screw the armature on...
And add an M3 nut to hold everything together.
Time for some blink! With 5X 3mm LEDs.
Put a little dob of hot-glue in the hole.
Quickly press the LED in before the glue cools.
5X awesome.
Bend the negative leads over each other until you get to the end - clipping the excess as you go.
I stripped the insulation off hook-up wire to cover the leads. Shrink tube can also be used, you'll just have to make sure that you don't get the area too hot - it might re-melt the hot-glue.
Organize the leads so that LED one and five, and two and three are connected.
Trim three 330ohm resistors.
Solder them all in place.
At this point I did add some clear shrink tube to help with strain relief. You could also coat the area in hot-glue as well.
Just a touch of hot-glue to stick the two pieces of the mouth together.
I've used blue LEDs... the Adafruit store has red, blue, green, or white.
Add the two armatures, but don't screw them into place yet - you may need to make adjustments.
Screw the other end of the armature into the eye plate.
You'll have to guesstimate where to place the speakers. Look all around the perimeter so that you know where adhesive should go.
Mix-up some more 5-minute epoxy and put a good-sized helping on the plate.
Put the entire rig on a table and adjust the eye-plate so that it's flat and parallel to the tabletop. Then, place your speakers into the adhesive.

5-minute epoxy is pretty good... but I'd wait more like 30 minutes just to be safe.
Add the mouth with a bit of hot-glue.
If the bare wood dowel bothers you... a black Sharpie can fix that in a jiffy.

You can breathe life into your animatronic creation with a servo controller and a little bit of Arduino code... here are two awesome options.

If programmatic servo control is a little too rigid, you can always give an RC transmitter with a 4-channel receiver a try.

This approach means you will be driving the servos dynamically.
Let's control the mouth animation dynamically with a little Arduino and an electret microphone.
I prefer to adjust the gain on the microphone... although you could do it in code.
Here's how I hooked up the mouth LEDs to the Arduino.

  • yellow wire to pin 9: drives the LEDs on the far right and left
  • blue wire to pin 10: drives the LEDs between the center and outside
  • purple wire to pin 11: drives the LED in the center of the mouth
  • green wire to ground

And this is the sketch that drives the LEDs based on the sound levels captured by the microphone.

There are some settings in there like HI_CUT, LO_CUT, and DC_OFFSET that you might have to adjust depending on what kind of audio you intend to drive the microphone with and how you've hooked it up.

As-is, this sketch works pretty well with the mic attached to your shirt just below the mouth.
// Connect LED positive to pins 9, 10, 11 with common ground.

#define MIC_PIN   A0      // connected to the out pin on the mic 
#define DC_OFFSET 3.3     // mic is plugged into 3.3v 

const byte HI_CUT = 255;  // upper brightness limit
const byte LO_CUT = 40;   // lower iimit turns LEDs off

// sample window width in mS (50 mS = 20Hz)
const int sampleWindow = 50;
unsigned int sample;

int LED_CENTER = 11;  // LED at center of the mouth
int LED_MIDDLE = 10;  // LEDs between the center and outside
int LED_OUTSDE = 9;   // LEDs on the outside of the mouth

void setup() {                
  // Serial.begin(9600);  // remove comment for debugging

  pinMode( LED_CENTER, OUTPUT );
  pinMode( LED_MIDDLE, OUTPUT );  
  pinMode( LED_OUTSDE, OUTPUT );  

void loop() {
  unsigned long startMillis= millis();  // start of sample window
  unsigned int peakToPeak = 0;          // peak-to-peak level
  unsigned int signalMax = 0;
  unsigned int signalMin = 1024;
  double lvl = 0;

  // collect data for 50 mS within a reasonable range
  while (millis() - startMillis < sampleWindow) {
    sample = analogRead(MIC_PIN);

    if (sample < 1024) {
      if (sample > signalMax) {
        signalMax = sample;
      else if (sample < signalMin) {
        signalMin = sample;

  lvl = signalMax - signalMin;              // max - min = peak-peak amplitude
  lvl = ( lvl * DC_OFFSET );                // reduce operational range
  lvl = ( lvl < LO_CUT ? 0 : lvl );         // low cut filter for noise
  lvl = ( lvl > HI_CUT ? HI_CUT : lvl );    // high cut filter for clipping

  analogWrite( LED_CENTER, lvl );           // 100% brightness
  analogWrite( LED_MIDDLE, lvl * .8 );      // 80% brightness of center
  analogWrite( LED_OUTSDE, lvl * .3 );      // 30% brightness of center

  // Serial.println(lvl);  // remove comment for debugging

Have fun with your new robotic friend!

This guide was first published on Nov 11, 2013. It was last updated on Mar 08, 2024.