An improved version of the 3D printed plastic bits going into the Hard Drive Platter Mood Light:

The central pillar now has cutouts behind the Neopixel strips so you (well, I) can solder directly to the larger half-pads on the back, plus a boss on the top for better wire management:

I’m not entirely satisfied with the little slots for the strip edges; the resolution limits of 3D printing call for larger openings, but there’s not much meat around those pins up the edge.
The base becomes much larger to hold the Arduino Pro Mini and gains an optional slot to let the programming cable reach the outside:

The cap has a boss matching the one atop the pillar:

Both the cap & base have center features recessed by two thread thicknesses to let their rims apply a slight clamping force on the platters.
Our Larval Engineer says it really needs an internal battery with maybe four hours of runtime, a charging base station (ideally with inductive power transfer), buttons (or, better, a tilt switch / accelerometer) for mode selection, and perhaps a microphone to synchronize lighting effects with music. To my horror, her co-op job seems to have exposed her to Marketeers…
We do, however, agree that the Cap would look better in lathe-turned brass with a non-tarnish clearcoat.
The OpenSCAD source code:
// Hard Drive Platter Mood Light // Ed Nisley KE4ZNU November 2015 Layout = "Spacers"; // Build Show Pixel LEDString Platters Pillar Spacers TopCap Base CablePort = true; ShowDisks = 2; // number of disks in Show layout //- Extrusion parameters must match reality! ThreadThick = 0.25; ThreadWidth = 0.40; HoleWindage = 0.2; Protrusion = 0.1; // make holes end cleanly inch = 25.4; function IntegerMultiple(Size,Unit) = Unit * ceil(Size / Unit); //---------------------- // Dimensions ID = 0; OD = 1; LENGTH = 2; Platter = [25.0,95.0,1.27]; // hard drive platters - must match actual thickness! LEDStringCount = 3; // number of LEDs on each strip LEDStripCount = 4; // number of strips (verify locating pin holes & suchlike) WireSpace = 1.0; // allowance for wiring along strip ends Pixel = [13.0, 1000 / 144, 0.6]; // smallest indivisible unit of LED strip PixelMargin = [1.0, 1.0, 2.0]; // LED and circuitry atop the strip BeamAngle = 120; // LED viewing angle BeamShape = [ [0,0], [Platter[OD]*cos(BeamAngle/2),-Platter[OD]*sin(BeamAngle/2)], [Platter[OD]*cos(BeamAngle/2), Platter[OD]*sin(BeamAngle/2)] ]; PillarSides = 12*4; PillarCore = Platter[ID] - 2*(Pixel[2] + PixelMargin[2] + 2.0); // LED channel distance across pillar centerline PillarLength = LEDStringCount*Pixel[1] + Platter[LENGTH]; echo(str("Pillar core size: ",PillarCore)); echo(str(" ... length:"),PillarLength); PCB = [34.5,17.5,1.6]; // Arduino Pro Mini (or whatever) PCB size PCBClearTop = 5.0; PCBClearBot = 5.0; PCBHeight = PCB[2] + PCBClearBot + PCBClearTop; PCBRadius = sqrt(pow(Platter[ID]/2 + PCB[1],2) + pow(PCB[0]/2,2)); echo(str("PCB Corner radius: ",PCBRadius)); CoaxConn = [7.8,11.2,5.0]; // power connector Cap = [Platter[ID] + 4.0,Platter[ID] + 4.0 + 10*2*ThreadWidth,2*WireSpace + 6*ThreadThick]; // cap over top of pillar CapSides = 8*4; BaseClearHeight = max(PCBHeight,CoaxConn[OD]); Base = [2.0 + 2*PCBRadius,2.0 + 2*PCBRadius + CoaxConn[LENGTH],BaseClearHeight + 6*ThreadThick]; BaseSides = 8*4; Screw = [1.5,2.0,20.0]; // screws used to secure cap & pillar Spacer = [Platter[ID],(Platter[ID] + 2*8),(Pixel[1] - Platter[LENGTH])]; echo(str("Spacer OD: ",Spacer[OD])); echo(str(" ... thick:",Spacer[LENGTH])); LEDStripProfile = [ [0,0], [Pixel[0]/2,0], [Pixel[0]/2,Pixel[2]], [(Pixel[0]/2 - PixelMargin[0]),Pixel[2]], [(Pixel[0]/2 - PixelMargin[0]),(Pixel[2] + PixelMargin[2])], [-(Pixel[0]/2 - PixelMargin[0]),(Pixel[2] + PixelMargin[2])], [-(Pixel[0]/2 - PixelMargin[0]),Pixel[2]], [-Pixel[0]/2,Pixel[2]], [-Pixel[0]/2,0] ]; //---------------------- // Useful routines module PolyCyl(Dia,Height,ForceSides=0) { // based on nophead's polyholes Sides = (ForceSides != 0) ? ForceSides : (ceil(Dia) + 2); FixDia = Dia / cos(180/Sides); cylinder(r=(FixDia + HoleWindage)/2, h=Height, $fn=Sides); } //- Locating pin hole with glue recess // Default length is two pin diameters on each side of the split PinOD = 1.70; module LocatingPin(Dia=PinOD,Len=0.0) { PinLen = (Len != 0.0) ? Len : (4*Dia); translate([0,0,-ThreadThick]) PolyCyl((Dia + 2*ThreadWidth),2*ThreadThick,4); translate([0,0,-2*ThreadThick]) PolyCyl((Dia + 1*ThreadWidth),4*ThreadThick,4); translate([0,0,-(PinLen/2 + ThreadThick)]) PolyCyl(Dia,(PinLen + 2*ThreadThick),4); } //---------------------- // Pieces //-- LED strips module OnePixel() { render() rotate([-90,0,0]) rotate(180) // align result the way you'd expect from the dimensions difference() { linear_extrude(height=Pixel[1],convexity=3) polygon(points=LEDStripProfile); translate([-Pixel[0]/2,Pixel[2],-PixelMargin[0]]) cube([Pixel[0],2*PixelMargin[2],2*PixelMargin[0]]); translate([-Pixel[0]/2,Pixel[2],Pixel[1]-PixelMargin[0]]) cube([Pixel[0],2*PixelMargin[2],2*PixelMargin[0]]); } } module LEDString(n = LEDStringCount) { for (i=[0:n-1]) translate([0,i*Pixel[1]]) // resize([0,Pixel[1] + 2*Protrusion,0]) OnePixel(); } //-- Stack of hard drive platters module Platters(n = LEDStringCount + 1) { color("gold",0.4) for (i=[0:n-1]) { translate([0,0,i*Pixel[1]]) difference() { cylinder(d=Platter[OD],h=Platter[LENGTH],center=false,$fn=PillarSides); cylinder(d=Platter[ID],h=3*Platter[LENGTH],center=true,$fn=PillarSides); } } } //-- Pillar holding the LED strips module Pillar() { difflen = PillarLength + 2*Protrusion; // render(convexity=5) difference() { linear_extrude(height=PillarLength,convexity=4) difference() { rotate(180/(12*4)) circle(d=Platter[ID] - 1*ThreadWidth,$fn=PillarSides); for (i=[0:LEDStripCount-1]) // clearance for LED beamwidth, may not actually cut surface rotate(i*360/LEDStripCount) translate([PillarCore/2,0,0]) polygon(points=BeamShape); for (i=[0:LEDStripCount-1]) // LED front clearance rotate(i*360/LEDStripCount) translate([(PillarCore/2 + Pixel[2]),(Pixel[0] - 2*PixelMargin[0])/2]) rotate(-90) square([Pixel[0] - 2*PixelMargin[0],Platter[ID]]); } for (i=[0:LEDStripCount-1]) // LED strip slots rotate(i*360/LEDStripCount) translate([PillarCore/2,0,-Protrusion]) linear_extrude(height=difflen,convexity=2) rotate(-90) polygon(points=LEDStripProfile); difference() { // wiring recess on top surface, minus boss for (i=[0,90]) rotate(i) translate([0,0,(PillarLength - (WireSpace/2 - Protrusion))]) cube([(PillarCore + 2*Protrusion),Pixel[0] - 2*PixelMargin[0],WireSpace],center=true); cylinder(d=3*Screw[OD],h=PillarLength + Protrusion,$fn=CapSides); } for (i=[0:LEDStripCount-1]) // wiring recess on bottom surface rotate(i*90) translate([PillarCore/2 - (WireSpace - Protrusion)/2,0,WireSpace/2 - Protrusion]) cube([WireSpace + Protrusion,Pixel[0] - 2*PixelMargin[0],WireSpace],center=true); for (j=[0:LEDStringCount-1]) // platter spacer alignment pins for (i=[0:LEDStripCount-1]) rotate(i*360/LEDStripCount + 180/LEDStripCount) translate([(Platter[ID] - 1*ThreadWidth)/2,0,(j*Pixel[1] + Pixel[1]/2 + Platter[LENGTH]/2)]) rotate([0,90,0]) rotate(45) LocatingPin(); translate([0,0,-Protrusion]) // central screw hole rotate(180/4) PolyCyl(Screw[ID],difflen,4); if (false) for (i=[-1,1]) // vertical wire channels rotate(i*360/LEDStripCount + 180/LEDStripCount) translate([PillarCore/2 - 2.0,0,-Protrusion]) PolyCyl(2.0,difflen,4); for (i=[-1,1]) // locating pins rotate(i*360/LEDStripCount - 180/LEDStripCount) translate([PillarCore/2 - 2.0,0,0]) LocatingPin(); } } //-- Spacers to separate platters module Spacers() { difference() { linear_extrude(height=Spacer[LENGTH],convexity=4) difference() { rotate(180/PillarSides) circle(d=Spacer[OD],$fn=PillarSides); for (i=[0:LEDStripCount-1]) // clearance for LED beamwidth, may not actually cut surface rotate(i*360/LEDStripCount) translate([PillarCore/2,0,0]) polygon(points=BeamShape); for (i=[0:LEDStripCount-1]) // LED front clearance rotate(i*360/LEDStripCount) translate([(PillarCore/2 + Pixel[2]),(Pixel[0] - 2*PixelMargin[0])/2]) rotate(-90) square([Pixel[0] - 2*PixelMargin[0],Platter[ID]]); rotate(180/PillarSides) circle(d=Spacer[ID],$fn=PillarSides); // central pillar fits in the hole } for (i=[0:LEDStripCount-1]) rotate(i*360/LEDStripCount + 180/LEDStripCount) translate([Platter[ID]/2,0,(Pixel[1] - Platter[LENGTH])/2]) rotate([0,90,0]) rotate(45) LocatingPin(); } } //-- Cap over top of pillar module TopCap() { difference() { cylinder(d1=(Cap[OD] + Cap[ID])/2,d2=Cap[OD],h=Cap[LENGTH],$fn=CapSides); // outer lid translate([0,0,-Protrusion]) PolyCyl(Screw[ID],Cap[LENGTH] + WireSpace + Protrusion,4); // screw hole translate([0,0,Cap[LENGTH] - 2*WireSpace]) difference() { cylinder(d=Cap[ID],h=2*Cap[LENGTH],$fn=CapSides); // cutout cylinder(d=3*Screw[OD],h=Cap[LENGTH],$fn=CapSides); // boss } translate([0,0,Cap[LENGTH] - 2*ThreadThick]) cylinder(d=Cap[ID]/2,h=2*ThreadThick + Protrusion,$fn=CapSides); // recess boss } } //-- Base below pillar module Base() { SideWidth = 0.5*Base[OD]*sin(180/BaseSides); // close enough difference() { union() { difference() { cylinder(d=Base[OD],h=Base[LENGTH],$fn=BaseSides); // outer base translate([0,0,6*ThreadThick]) // main cutout cylinder(d=Base[ID],h=Base[LENGTH],$fn=BaseSides); rotate(180/BaseSides) translate([0,0,Base[LENGTH] - BaseClearHeight/2]) // power connector hole rotate([90,0,0]) rotate(180/8) PolyCyl(CoaxConn[ID],Base[OD],8); } translate([0,0,Base[LENGTH]/2]) // recess pillar support below rim cube([PillarCore,PillarCore,Base[LENGTH] - 2*ThreadThick],center=true); } for (i=[0:LEDStripCount-1]) // wiring recesses rotate(i*90) translate([PillarCore/2 - (WireSpace - Protrusion)/2,0,Base[LENGTH] - 4*WireSpace/2]) cube([WireSpace + Protrusion,PillarCore - 4*WireSpace,4*WireSpace],center=true); translate([0,0,-Protrusion]) PolyCyl(Screw[ID],2*Base[LENGTH],4); // screw hole translate([0,0,-Protrusion]) // screw head recess rotate(180/8) PolyCyl(8.5,Base[LENGTH] - 3.0 + Protrusion,8); for (i=[-1,1]) // locating pins rotate(i*360/LEDStripCount - 180/LEDStripCount) translate([PillarCore/2 - 2.0,0,Base[LENGTH] - ThreadThick]) LocatingPin(); if (CablePort) translate([0,Platter[ID]/2 + PCB[1],Base[LENGTH] - 3.0 + Protrusion]) rotate(-90) cube([PCB[1],Base[OD],3.0]); } } //---------------------- // Build it if (Layout == "Pixel") OnePixel(); if (Layout == "LEDString") LEDString(LEDStringCount); if (Layout == "Platters") Platters(LEDStringCount + 1); if (Layout == "Pillar") Pillar(LEDStringCount); if (Layout == "TopCap") TopCap(); if (Layout == "Base") Base(); if (Layout == "Spacers") Spacers(); if (Layout == "Show") { Pillar(); for (i=[0:LEDStripCount-1]) // LED strips rotate(i*360/LEDStripCount) translate([PillarCore/2,0,Platter[LENGTH]/2]) rotate([90,0,90]) color("lightblue") LEDString(); if (true) for (j=[0:max(1,ShowDisks - 2)]) // spacers translate([0,0,(j*Pixel[1] + Platter[LENGTH])]) color("cyan") Spacers(); for (j=[0:max(2,ShowDisks - 2)]) // spacer alignment pins for (i=[0:LEDStripCount-1]) rotate(i*360/LEDStripCount + 180/LEDStripCount) translate([(Platter[ID] - 1*ThreadWidth)/2,0,(j*Pixel[1] + Pixel[1]/2 + Platter[LENGTH]/2)]) rotate([0,90,0]) rotate(45) color("Yellow",0.25) LocatingPin(Len=4); translate([0,0,PillarLength + 3*Cap[LENGTH]]) rotate([180,0,0]) TopCap(); translate([0,0,-2*Base[LENGTH]]) Base(); if (ShowDisks > 0) Platters(ShowDisks); } // Ad-hoc build layout if (Layout == "Build") { if (true) Pillar(); if (true) translate([0,(Platter[ID] + Cap[OD])/2,0]) TopCap(); if (true) translate([0,-(Platter[ID] + Base[OD])/2,0]) Base(); Ybase = Spacer[OD] * (LEDStringCount%2 ? (LEDStringCount - 1) : (LEDStringCount - 2)) / 4; if (true) for (i=[0:LEDStringCount]) // build one extra set of spacers! translate([(i%2 ? 1 : -1)*(Spacer[OD] + Base[OD])/2, // alternate X sides to shrink Y space (i%2 ? i-1 : i)*Spacer[OD]/2 - Ybase, // same Y for even-odd pairs in X 0]) Spacers(); }