Advanced Cabinet making on the ShopBot

20240831_204246

Get it? On the ShopBot? Because…  I know, it’s a terrible pun, but I actually did most of the work with the ShopBot too!  This post is designed to show off what you can do with the tools in our shop – we offer free training for everything shown in this post, and it’s easier than I thought!

448592246_813269787214595_7974013255419937489_n

   The story starts when my friend made fun of my entry-way table, which is always covered in stuff. I realized I need an entryway cabinet, so I could hide all of that stuff in drawers! So I was bouncing ideas around in my head when a friend offered me a slab from his old tree.  Cheap, cut with an Alaskan mill (aka a chainsaw with a spacer rig), and aged a few years in his back yard.  I’ve watched fancy furniture videos on the internet for years, and here was my chance to try it!

 

 

 

20240804_191006

The first challenge is turning this chunk of tree into “Lumber”.  The professionals have giant planers… ours is only 12″, and too small for this slab.  But what we do have is a ShopBot! I drilled a few recesses and screwed the plank to the table, and ran a decking pass. Just a giant rectangle with a wide bit…  and then hand-drove the head around to do cleanup on the edges just outside the rectangle.  One side done, I flipped the slab, re-zero’d the Z axis, and in just under an hour I had a perfectly flat and parallel piece of lumber!  While it was screwed down, I used the CNC to cut it to size. Using the same large rectangle, I rounded the corners, let it overhand the slab on one edge (to get a live-edge look), and cut only the perimeter this time.  I took it down to 1/4″ of material remaining – this meant I didn’t have to worry about the slab moving because I’d cut it free of the screws. For the remaining 1/4″ I used a jigsaw for a rough first pass – staying wide of the edge – and then used a follower bit in the router to match that edge with the CNC’d profile.

The next step was sanding. So much sanding. I think I did 80-120-200. I don’t know if that’s right, I know I should have gone higher, but after so much sanding I didn’t care anymore. While sanding,  I’d take breaks to work on the live-edge. I had to pull off the bark and spongy sap-wood so I only had good material left.  I mostly worked with chisels.  If you’re at our hackerspace ask around and borrow someone’s nice chisels, the communal chisels are thoroughly abused. Despite not knowing what I was doing, I slowly got down to good wood, and the slab was ready for finishing!

Before finish, I moved onto the cabinet itself.  While working on the slab, I’d been working on the CAD for the main body. This first-round of CAD was only to figure out the proportions and the look of the piece. I can’t draw, so in order to figure out what proportions looked right, I had to model it. The first models were ugly. If you wanted to be charitable you could call them “highly functional.”   My modeling process is to make a round of updates to the CAD, and then send a screenshot to a friend asking what’s wrong with it.  Then another round of updates to fix those issues, another screenshot, and ask a different friend what’s wrong with it. It’s the same way I revise my high-effort videos (please sub!).  In addition to purely-aesthetic changes, the cad evolved as the slab shrank down to final dimensions.

allScreenshots
Cad screenshots harvested from a handful of chats

 

20240810_203936

With the cad almost-finalized I had a few structural pieces to cut on the shopbot. The legs were nice and simple, and cut out of some scrap 2x8s.  Less simple were the front corners, which I had decided should be rounded. It definitely looks cool, but unlike the previous cabinet, this curve was too tight to be able to steam-bend, while far too large for a chamfer bit.  Luckily, we have a shopbot! I exported a STL of the part, loaded it into aspire, and set up a 3d toolpath. Key tips: Use a round-nosed bit, I used 1/2″.  Take aggressive steps on the roughing passes, but dial it back to baby steps for the finishing pass. I routed this out of a 6×6 from homedepot, and while the finishing pass did take an hour, the results were worth it. A bit of sanding, and I had a long stock of quarter-round I could use for the edge rails.

With these corners and the table-top, I could trace out the base of the cabinet, which I bulked out on the table-saw and finished with a jigsaw. Having both the base and the corners gave me a final dimension for the face of the cabinet – it’s always best to measure these and not rely on the cad. I cut the doors first. A coat of stain, and then a light engraving pass to through the stain to expose a pattern! I fell down a rabbithole on the door design, which you can read all about in the previous post here.  Doors done, it was time for the fascia / drawer covers. I left tabs on these pieces, locking them all in place so that I could use the vacuum hold-down.  Then I repeated the stain-and-engrave trick to decorate the drawers.

For the drawers, I knew I wanted a similar mathematic exponential design… I tried circles again, but it looked off. Instead I just pulled up a graphing calculator and started playing with equations…  I knew I wanted curves, so that meant a sine wave. I knew I wanted it to expand over time, so that meant an x*sin(x) curve. Plotting it, I realized it needed to slow down over time as well, so x*sin( ln(x) )…    After a bunch of guess-and-check to tweak the curves, I got this:

drawerEquations

I could pretend I then used these equations in Solidworks to programmatically generate my cut-path, but I didn’t. I hit print-screen, cropped the curves in gimp, and traced them in Inkscape.  Once there, I could stretch it to fit my drawer fronts, and engrave more math into my nerdy furniture.

Spoiler: I figured it out

From here, it gets pretty boring. I bashed a frame out of 2×3 and 2x4s, sheeted it with plywood, installed home-depot hinges, and built drawer-boxes. Which, of course, I could have done on the shopbot! Instead I locked my slide and bulked them out on the table-saw. If you really need the details LMK in the comments and I’ll write a “Drawers 101″ course. I spec’ed out some nice push-to-open drawer-slides on Woodcraft, but their website crashed so I bought knockoffs on amazon. I mounted them on random scraps of wood that would give me the right offset…. which is definitely not the right way to do it, but it’s hidden so it’s not getting fixed unless it breaks.

The last step is the surface-finish.  I started with the body: Another round of sanding, and then to the tricky part: Matching stains. The different woods reacted differently to the stains, so I had to whip up a custom mix of stain using SpunSpoon. I put together a short video explaining this process:

Was that really just a commercial for SpunSpoon? Of course it was. Do you need a SpunSpoon? Of course not. But it did work a treat. Then excessive coats of poly and call it a day.

The slab is the start of the show and would see more abuse, so it deserved and needed more thorough finishing.  First were two final rounds of sanding, and I popped the grain between grits by wiping it with a damp cloth.  Then came a dark stain, and once that dried I did touch-ups with a sharpie – darkening the various worm-holes and scars that stood out against the now-dark wood. It’s super fast and simple, but this quick step makes a huge difference in appearance.  After that, I clear-coated the entire slab with poly. Poly is never a mistake. After the poly I mixed up a bunch of deep-pour slow-setting self-leveling epoxy, and smeared a layer of this over the entire table.  It looked amazing, but it also leaked through every minor crack or worm-hole on the entire table.  Because of this, my planned two coats turned into 4 coats, all 12 hours apart. Next time, I’m going to superglue, hotglue, or epoxy every single defect before the pour. Epoxy done, I tented the board in some cheap plastic sheeting to protect it from sawdust, and let the whole thing cure for 4 days. Finally, I shaved the epoxy stalactites off bottom, taking shallow passes with an inverted chisel until they were flush.

And now, almost a month later, we can move this cabinet off the shopbot!

458240134_1507252999914479_866690381658277781_n

 

 

Programming – Interlocking Exponential Circles

  • If you’re not a programmer, you can download the software here. Run the exe, load the sample SVGs, play around, and feel free to skip the details in the rest of this post.
  • If you are a programmer and want to follow along, or think you can improve this software, please do! The source is available here  .

So… I decided I would make another cabinet (post coming next week!). And then, messing about in CAD, I realized I could create cool patterns with parametrically related circles. The only issue was the sheer number of cascading parametric relationships – each circle related to the circles touching it – managed to crash my machine. Even my beefy video-editing desktop. The easiest solution would be to change the design, but I was infatuated to the point I fell asleep thinking about circles every night…

Spoiler: I figured it out
Spoiler: I figured it out

So I dove in, assuming I’d figure it out as I went.  The first challenge was to describe the actual expansion I was looking for… I knew I wanted the pattern to grow both in circle-radius and circle-spacing, but couldn’t really describe it better than that.  So forget it! We’ll come back to it.  Instead, I wrote a program that would create a list of circles on a grid.  Just X, Y, and radius. A couple nested for loops that spat out a list of numbers.

Now we need to turn those numbers into circles. I looked at a few graphic libraries for c#, but I wanted an output format I could drop into cad. I really wanted a DXF.  So I checked out some DXF libraries, and started reverse-engineering DXF files.  Reverse-engineering is really just a fancy term that means “I changed the extension to TXT and looked at it notepad”.  This still looked like a lot of work. What about SVG? I use Inkscape for cad more than I should…  some of Doomba’s mechanical parts even went straight out of Inkscape to SendCutSend.

Success!  There was a lot of complicated file definition, but when I drew a SVG of a single circle, I saw a single corresponding line of code in notepad.

<ellipse style = “fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.24600001;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1″
id = “path652″ cx = “8″ cy = “8″ rx = “5″ ry = “5″ />

Do I know what all these fields mean? Nope. But playing with cx and cy showed them to be center-x and center-y, and rx/ry were radiuses (two options for ellipses, but we’re sticking to circles so we’ll use the same number for each).

So now, I can have my program spit my list of circles into an actual SVG!  It starts by copying the beginning of the SVG, adds a line for each circle – updating cx/cy/rx/ry, and then appends the tag that closes the document.  Then save it as an SVG, and done!  On top of that, I had GPT help me look up parts of the code for me… GPT works very well as a stack-overflow research engine.

Now my program can draw circles! Which means I could start playing with the variables to figure out how to create the pattern I was looking for. I could do a lot of math… but it was faster and more fun to play guess-and-check. So much guess-and-check I had to optimize the program. I added more and more options for circle locations, spacing, and sizing. But with this many variables, I would forget the settings I used. So I had the software save the last set of variables.  But that didn’t solve the problem for previous circles, so I added a comment to each SVG containing all the variable values used in that SVG. On the flip side, I wrote a function that will extract those variables from a saved SVG, so it was easy to tweak a design that was almost right.

This is another section where GPT did most the work… I wrote sample code showing how it would work for one-variable, and then told chat GPT to duplicate that code for each of a long list of variable names, changing the data-types to match the definition. AI is excellent at what boils down to a multi-step copy-paste problem, and probably saved me a few hours over the course of this project.

After hours of guess-and-check, the equations ended up being:

double locX = deltaX * Math.Pow(expX, x – 1);
double locY = deltaY * Math.Pow(expY, y – 1);
double distance = Math.Sqrt(x * x + y * y);
double circleRadius = circleSize * Math.Pow(circleFactor, distance);

Better, I had a SVG.  I dropped this in Inkscape where I could overlay it on the outlines of the doors I’d drawn. I resized the circle-array, and mirrored it for the opposite door. Export to DXF, load it into Aspire (one trick here: use “Quick Engrave” to engrave only the lines, without the CAM software worrying about which regions over overlapping shapes are positives or negatives). Export to .SBP, and off to the shopbot!

Success!

 

 

 

Basic Cabinetry on the ShopBot

So… I still have 4000 spoons (please buy some) and needed better storage for them at the Hackerspace, so I decided to build a cabinet. This also gave me a first chance to break into furniture-making, which has always fascinated me. The goals here were cheap and fast, so we’re going to take a lot of shortcuts. However, I always try to learn one new skill on every project, so this week we’re learning wood-bending!

First off, I got a sheet of “fancy plywood” from Home-Depot. I’m sure there’s a technical term, interior-grade birch something-or-other, but… I lets be honest. It’s bougie plywood. Then I measured the space the cabinet would fit into, and laid out the lid of the cabinet. This was a simple cut on the shopbot, which does a better job on curves than I ever could by hand. And when working with sheet-goods, we can use the vacuum hold-down which is convenient and saves the material from screw-holes.

After the lid, I cut both exposed sides of the cabinet in one piece! The front portion is simple, as it matches the square edge of the top of the cabinet, with two doors inside it. Because we’re aiming for fast and easy, I cut the doors out in-situ. This means the doors will have the  1/4″ gap left by the endmill, but fast and easy.  I could cut the doors out of a separate piece, but then I’d have to go back to HD for more fancy ply…   The corner / side of the cabinet aren’t as simple as the front. With the overhang of the lid, the curved portion of the sides will be shorter than the curved portion of the lid… and I’m not sure how much shorter.  I could do a bunch of math and probably get it right, but I had an easier solution: I cut it long, and I’ll trim it down to size later.

20240115_142323

This brings us to the interesting part of the cabinet…  Bending. First, we needed to plane down the portion of the sides that will be making the bend. There are lots of ways to do this, but the plywood is already on the ShopBot… So I decked it! Really simple, just bring it down from 0.5″ to 0.1″. You’ll see in the pictures that I added a bunch of tiny slits trying to do an accordion fold… but that was a mistake that hurt more than it helped. Simply deck the entire area flat.

Now, we have to warm and moisten the wood fibers so that they’ll bend without breaking. The correct way to do this is to build an enclosed steam-box and pipe steam into it for 30m to an hour… but fast+easy, right?  I filled a storage bin with water, and put an immersion heater in it.  Honestly, this was garbage. It didn’t entirely fit, so I had to soak portions at a time, while ladling water over the exposed portion. If I were to do it again, I’d probably still do the soaking method, but I’d measure the soaking-tank beforehand to make sure it actually fit.

 

 

 

 

 

20240115_142318

However, half an hour later my board was bendy! According to the internet, you can just curve it into shape with some ratchet-straps and wait for it to dry! The internet is a liar. I mean, except for this website…  It was impossible to induce my curve without also inducing some twist in the wood.  I left the straps on, and submerged my bent and twisted wood back into it’s bath-tub while I ran to my com puter for emergency cad.  I quickly sketched inner and outer braces in Aspire, and then CNC’d two of each out of scrap wood. I swear we’re not sponsored by ShopBot, but I love that thing.  These braces worked perfectly, and I could clamp the curve into the desired shape, and held it there while it dried overnight. (Overnights, I got distracted).

 

 

 

 

 

 

20240115_192000
20240118_192616

 

The rest of the project is pretty standard. Framing out the body of the cabinet, adding shelves of scrap ply-wood, poly over everything, more poly over that, and piano-hinges from the value rack at HD. I did learn one last lesson: Edge banding. Edge banding is awesome, it’s super easy and hides the fact that you used plywood. HD only sells one type but it’s an exact match for their bougie ply. Then you iron it on like you’re making custom tshirts in the 90s, and trim the edges so they match the plywood perfectly. You can do this with a chisel or a razor, but they also make a little slidy thing that pinches razor blades to the exact right height and does all the work for you.  It even stains the same as the plywood.  I knew edge-banding was a thing, but I have an all-new appreciation for it now.

 

 

 

 

 

 

 

20240121_151903

Exploring Randomness in Mobile Gaming

I know we all expect mobile games to be rigged. Anything goes in the name of engagement and retention numbers, but it’s always hard to prove. Hidden odds, complicated algorithms and “power ups”, and incomprehensible stat sheets all obfuscate the actual pRNG (psuedo Random Number Generator). But recently, a mobile game I’d been playing held a special event that let you roll dice. This should be a clear look straight at the results of the RNG, but is it?

To find out, I rolled a pair of electronic dice 200 times, meticulously noting the values in a spreadsheet. The things we do for science. At first glance, intuitively, the data looks off:

randomSingles

That doesn’t look right at all. These should be even (aside from random noise), and instead we have a evenly sloped line – showing that higher numbers are encouraged on the dice (and in-game, higher values are rewarded). Now that we’ve got that down, lets look at doubles (also rewarded in-game):

randomDoubles

We’d expect a 16% chance (first dice comes up X. Second dice has a 1:6 chance of matching X), but we’re at 23%. More damningly, Doubles occurred at a 30% rate in the first third of the data (not pictured, see spreadsheet at end). It seems the odds change as you play, starting strong to hook you before tapering towards (but never getting to) a natural rate.

The final aspect that I chose to examine was the rate of 7s. 7s are also rewarded, but rewarded so heavily it would make the game too easy if they came up too often.

randomSum

But seven should be the most commonly rolled sum. If we put together a table we can calculate the exact chances:

naturalTwoDice

There are 6 out of 36 possible results that are 7, more than any other number. However in our gameplay 7 came up less often than 3, which only has 2 possible creations! (1+2 or 2+1).

So here we have several indicators that the RNG is not truly random, nor even psuedorandom, but instead a weighted number generator that is watching the combined totals in order to manipulate the player into playing more, striving to get that 7 that should be just around the corner. But do we have proof? Perhaps I just got particularly unlucky several hundred times in a row. So how unlikely is it for this to occur?

For the two-dice example, we only had 6^2 options, so we could build a table and calculate the odds by hand. However, in order to build a table to match all 200 runs, we’d have (6^2)^200 options! Even google gives up at that point.

googleiswrong

This is where we get to the hard math. Binomial distributions – essentially, how likely is a combined result of X, given N trials and a probability per-trial of p. This requires a lot of summations, factorials, and integrations… I don’t have time for that, I have fake dice to roll! Luckily excel will do it for us: BINOM.DIST([occurrences], [trials],[probability per trial], [cumulative or single])

So, for example, the odds of flipping 5 or more heads in 10 tosses is BINOM.DIST(5,10,.5, TRUE [meaning x or more, not exactly X]), and excel tells us this is likely 62% of the time. So lets look at our two biggest red-flags:

The chance that we got only 16 (or less) sevens, given 6/36 odds (6 of the 36 possible combinations result in 7), out of 200 rolls:
BINOM.DIST(16,200,6/36,TRUE) = .03%, about 1:3000 odds.

The chance that we got 46 (or more) pairs, in 199 trials, given 1:6 odds:
BINOM.DIST(46,199,1/6,TRUE) = .7%, about 1:120 odds.

And we can combine these and look for the chances that these both occur together, 1:(3000*120) = 1:360k. I didn’t adjust for the lack of 7s making doubles more likely, but these rough numbers are close enough to prove the point. I’m sure if we added the chances for the single dice rolls as well we’d be in 1:1M odds that these results are natural.

Potential mechanism of action: There may be a simple algorithm that would result in all of these red flags: If a 7 is rolled, flip a coin. If heads, return 7. If tails, increment one dice. This rule change would explain the slope of the singles, the lack of 7s, and the increase in doubles – but this increase would be located only on 4:4. So to test this hypothesis, lets create a graph showing the frequency of each pair:

frequencyOfEachPair

We’d expect a flat chart, so this is ridiculously unnatural. However, it’s not unnatural in the way we hypothesized above, so the proposed algorithm is clearly not the one they are using. Fewer ones were rolled than any other number, and yet they are most likely to be in a pair. Clearly something else is going on.

So remember: When you’re not paying for the product, you may be the product.

*Update!*
More data more better, right? I gathered another 280 pairs and reran the analysis.

diceRound2

The rate of doubles is identical at 23%. The individual numbers were a bit more mixed, but still trended towards the most 6s, and not nearly the correct number of sevens.

Rerunning the binomial distribution on the combined data set leads to:
23% rate of doubles in 475 throws: .017% = 1:6000
(39)7s in 475: 1:17M
Both: 1:100T

So yeah.

Postscript: All the raw numbers and calculations are available here: https://docs.google.com/spreadsheets/d/1h1KqF-IuFLbCTrlajyu_8774xoV1nsafQXCPMrw_5mU/edit?usp=sharing . If you’d like to do further analysis or correct any mistakes I may have made, please reach out, we’d love to get even deeper into these numbers.

The Game of Life Coffee Table

About a year ago, we were smashing up a TV with Mammoth (video) and I fell in love with the look of the broken LCD, and decided to make it into a table-top. Despite veering wildly offcourse throughout this build - and using almost none of the original parts – that carnage set this project underway.

First step was to CNC the base, an array of hexagons to serve as cells beneath the surface. You might think the first step would be to figure out what the end product would look like… but nah. I knew I wanted some multicolored lighting, and hexagons make a cool pattern, might as well start. Plus, it was a fun excuse to play with the CNC.

20210912_214848

Then I painted this array, and added WS2811 LED modules (link, but you can find them cheaper elsewhere – search for  “36mm ws2811″) to each cell:

Screenshot_20220606-120735_Gallery

 

After that, it was time to program. This was definitely the most fun portion of the project.  I started with Conway’s Game of Life - but Conway’s Game of Life is only played on a square grid, and I chose to use hexagons.  Additionally, CGOL uses binary states – a cell is either on or off. Purely because it looks cool, I wanted to use an analog state so I could blend between colors. So I designed a new version, aiming for a system that wouldn’t reach any steady-states, and the rules are as follows:

Initial Setup:
            A random number of cells, chosen at random,  start with 25% heat, every other cell starts with no heat.
Gameplay - for each cell, repeating indefinitely:
            1) If your neighbors are hot, get a portion of their heat and add it to your own.
            2) If you get too hot, you explode, die, and lose all your heat.
            3) If your neighbor explodes, you die from blast damage and lose all your heat, but you do not explode (no cascading explosions).

That’s it. Very simple concept, but the implementation is a little bit trickier, especially on an Arduino.  The small micro size means we need to optimize for both speed and memory. The first issue I found is there wasn’t enough room in RAM to hold the addresses of all the neighbors. The massive 6×150 byte array – essential so cells know who their neighbors are – has to be stored in flash memory. Once you figure out the keywords that the Arduino IDE wants, this is pretty simple:

const static PROGMEM byte neighborArray[][6]

And then to access those bytes:

memcpy_P(localNeighbors, neighborArray[i], 6);

There should be shortcuts to access those values directly… but there aren’t, at least not for bytes. Just copy them into a temporary array whenever you need to read them and save yourself a lot of headaches.

Memory issues solved! Onto the speed problem. There are two main portions of the program: Display and Calculation.  During the display phase, the speed was fine, I even had to add delays to get the right fade between the previous state and the current state. But during the calculation phase, the program would visibly lag and interrupt the smooth flow of the display phase.  To start with the obvious optimizations, I used simple byte math. Addition, subtraction, and bit-shifts. Very little multiplication and never any division. But optimizing the math wasn’t enough, and we had to get tricky. Instead of calculating the next state for the whole array at once, I only calculated 5 new cells at a time. By increasing the steps in the display-phase fade to 30, I was able to replace the display-phase delays with these calculations. This meant that I could continuously calculate the next array while animating the previous change, eliminating the stutter from my program. This made it a little harder to track the current state of anything, but nothing a few extra arrays couldn’t fix.

After that, it was onto aesthetics. How do we translate a byte into a 3-byte color in a way that looks good. The simple version is: 1-125 directly increased the blue value of the color, and then 125-255 decreased the blue value while increasing the red value.

The complicated version involves an analog input to choose a starting color between blue and green, and then a tweaking the brightnesses because LEDs get brighter linearly as the PWM increases, but your eye will perceive brightness logarithmically (an LED with twice the current only appears 150% brighter). Luckily, we don’t have to actually do that math, we can just fake it by getting brighter slowly for the low values and quickly for the high values. To say this in a nerdy way with lots of math:

//create a color between oldstate and newStateTemp, stepped by phase
//30 phases per change
uint32_t StateToColor(float oldstate, float newState, float phase)
{

   byte interstate=oldstate+phase*((newState-oldstate)/30);

   //if are <75, fade up blue/green slowly (at half rate)
   if (interstate < 75) {

   //colorfade(colorcomponent,brightness) is %colorcomponent * state, with speed optimizations, and overflow protection to make sure it’s a byte.
         return Color(0,colorfade(color1g,interstate>>1),colorfade(color1b,interstate>>1));
   }
   //75-125, fade up blue/green faster (at full rate, but account for the earlier slowness so there’s not a visible step)
   if (interstate < 125) {
      return Color(0,colorfade(color1g,interstate-37),colorfade(color1b,interstate-37));
   }

   //125+, fade out blue/green and fade in red.
   //fade in red doubly fast because it has 125-255 to go from 0-255
   //fade in red slightly faster than that to max out red early
   //so it doesn’t immediately disappear when it dies next round.
   int adjColor=(250-interstate-45);
   if (adjColor<0){ adjColor=0; }
   int adjColor2=2.2*(interstate-125);
   if (adjColor2>255){ adjColor2=255; }
   return Color(adjColor2, colorfade(color1g,adjColor), colorfade(color1b,adjColor));
}

After that, the electronics were done and all that was left was turning it into a table! I sketched a few wildly varied designs for legs and settled on hexagons to match the display. Then I tried a few layouts for the hexagons in CAD to settle on a final design. Despite being far more comfortable in wood, I decided to use Aluminum and practice my TIG skills. I CNC’d a hexagonal jig in some scrap MDF to hold the pieces in the right spot, and blew through an entire tank of Argon doing a few hundred welds. And it worked! A few things clicked during the process and finally feel like understand tigging now.  (Side note: My motorcycle buddy asked “So, can I just come to the hackerspace and practice welding for free?” “Yeah, totally.” “Wait really?” “Yeah, we’re an educational nonprofit. Our Mission statement is basically ‘teach anybody to build cool stuff’, so come by whenever”)
279753349_986385208571745_9136975729903789763_n

The surface is made of a sheet of plexi to hold down all the wires, a layer of diffraction grating (from the murdered TV that started all this) to spread the light in an interesting manner, and then a layer of fancy semi-transparent plexi to hide the guts but allow the light to pass through.

286362142_10101278382119334_2995923001429898190_n