Danish Christmas Tree

Late November I made this Wooden Dowel Christmas Tree with advent-style felt ornaments (with some talented friends helping with all the ornaments).

The tree is collapsible (you just pull the branches out) which is useful for the rest of the year when its not Christmas!

Lessons learned

  • Dowel is cheap
  • 25 hand-stiched ornaments can take a while
  • Once you’re half way through you have to finish!

Resources and References

Even Steven - available for download

Even Steven is now available as a free download on the App Store

Feedback welcomed via Twitter

Thanks to people that helped test it!

Even Steven - a group expenses app for iOS

Update Now available on the App Store!

Even Steven is a handy app that records group spending for events such as holidays and suggests who needs to pay what to whom in order to get even!

Even Steven is designed for iPhone and:

  • requires no signing up
  • does not require an internet connection (ideal when holidaying abroad and not having data)

Behind the scenes, Even Steven is a pure Swift3 iOS app and uses no third party libraries.

...
Walnut Tealight Holder

Walnut Tealight Holder

Still horrendous at making things with my hands but will keep trying! This is a tealight holder that is basically three bits of wood, two drill holes and glue.

  • Band Saw
  • Pillar Drilling Machine
  • 1 1/2" Forstner Bit
  • Sand Paper
  • Wood Glue
  • Circular Saw

Lessons learnt

Buying Wood

  • Walnut is a beautiful dark hardwood
  • Walnut is expensive
  • A DIY Store will sell wood at fixed lengths, but will cut it into pieces for you.
  • My car can fit about 10feet of wood just!

Prepping

  • Make a detailed plan beforehand.
  • If it doesn’t work on paper it won’t work in the shop (this applies to coding too btw). Draw out everything to scale.
  • Use SketchUp or some other CAD software if you want to have reusable plans.
  • Itemize the equipment you’ll need. Nothing more frustrating than being halfway through and realising you don’t have any sanding paper!
  • Use Painters Tape or Super77 glue to stick your paper plans onto real wood.

In the Shop

  • Sanding concave curves is hard without a spindle sander
  • Always sand with the grain
  • Wood Glue needs clamps and needs to sit clamped for at least 30mins
  • Have a Pencil and a backup!
Tensorflow 101

Tensorflow seems like a pretty exciting alternative to scikit-learn that works just as effectively for beginners as it does for pros.

For my team’s monthly drink’n’sync I wrote an interactive worksheet on using TensorFlow to build a handwritten number detector.

Check it out on github!

Pong in < 250 lines of JS!

This is a crude example of a playable game that can be expressed in less than 250 lines of code!

Demo

Code

  1<!DOCTYPE html>
  2<html>
  3<body>
  4  <canvas id="game"> </canvas>
  5  <script type="text/javascript">
  6    var game = (function() {
  7      var Width = 800, Height = 450;
  8      var canvas = document.getElementById("game");
  9      var FPS = 1000 / 60;
 10      canvas.width = Width;
 11      canvas.height = Height;
 12      canvas.setAttribute('tabindex', 1);
 13      var ctx = canvas.getContext("2d");
 14
 15      var BG = {
 16        Color: '#333',
 17        Paint: function() {
 18          ctx.fillStyle = this.Color;
 19          ctx.fillRect(0, 0, Width, Height);
 20        }
 21      };
 22
 23      var Ball = { Radius: 5, Color: '#999', X: 0, Y: 0, VelX: 0, VelY: 0 };
 24      Ball.Paint = function() {
 25        ctx.beginPath();
 26        ctx.fillStyle = this.Color;
 27        ctx.arc(this.X, this.Y, this.Radius, 0, Math.PI * 2, false);
 28        ctx.fill();
 29        this.Update();
 30      };
 31      Ball.Update = function() {
 32        this.X += this.VelX;
 33        this.Y += this.VelY;
 34      };
 35      Ball.Reset = function() {
 36        this.X = Width / 2;
 37        this.Y = Height / 2;
 38        this.VelX = (!!Math.round(Math.random() * 1) ? 1.5 : -1.5);
 39        this.VelY = (!!Math.round(Math.random() * 1) ? 1.5 : -1.5);
 40      };
 41
 42      function Paddle(position) {
 43        this.Color = '#999';
 44        this.Width = 5;
 45        this.Height = 100;
 46        this.X = 0;
 47        this.Y = Height / 2 - this.Height / 2;
 48        this.Score = 0;
 49        if (position == 'left')
 50          this.X = 0;
 51        else this.X = Width - this.Width;
 52        this.Paint = function() {
 53          ctx.fillStyle = this.Color;
 54          ctx.fillRect(this.X, this.Y, this.Width, this.Height);
 55          ctx.fillStyle = this.Color;
 56          ctx.font = "normal 10pt Calibri";
 57          if (position == 'left') {
 58            ctx.textAlign = "left";
 59            ctx.fillText("score: " + Player.Score, 10, 10);
 60          } else {
 61            ctx.textAlign = "right";
 62            ctx.fillText("score: " + Computer.Score, Width - 10, 10);
 63          }
 64        };
 65        this.IsCollision = function() {
 66          if (Ball.X - Ball.Radius > this.Width + this.X || this.X > Ball.Radius * 2 + Ball.X - Ball.Radius)
 67            return false;
 68          if (Ball.Y - Ball.Radius > this.Height + this.Y || this.Y > Ball.Radius * 2 + Ball.Y - Ball.Radius)
 69            return false;
 70          return true;
 71        };
 72      };
 73
 74      window.requestAnimFrame = (function() {
 75        return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || function(callback) {
 76          return window.setTimeout(callback, FPS);
 77        };
 78      })();
 79      window.cancelRequestAnimFrame = (function() {
 80        return window.cancelAnimationFrame || window.webkitCancelRequestAnimationFrame || window.mozCancelRequestAnimationFrame || window.oCancelRequestAnimationFrame || window.msCancelRequestAnimationFrame || clearTimeout
 81      })();
 82
 83      var Computer = new Paddle();
 84      var Player = new Paddle('left');
 85
 86      function Paint() {
 87        ctx.beginPath();
 88        BG.Paint();
 89        Computer.Paint();
 90        Player.Paint();
 91        Ball.Paint();
 92      }
 93
 94      function MouseMove(e) {
 95        Player.Y = e.pageY - Player.Height / 2;
 96      }
 97      canvas.addEventListener("mousemove", MouseMove, true);
 98
 99      function Loop() {
100        init = requestAnimFrame(Loop);
101        Paint();
102        if (Player.IsCollision() || Computer.IsCollision()) {
103          Ball.VelX = Ball.VelX * -1;
104          Ball.VelX += (Ball.VelX > 0 ? 0.5 : -0.5);
105          if (Math.abs(Ball.VelX) > Ball.Radius * 1.5)
106            Ball.VelX = (Ball.VelX > 0 ? Ball.Radius * 1.5 : Ball.Radius * -1.5);
107        }
108        if (Ball.Y - Ball.Radius < 0 || Ball.Y + Ball.Radius > Height)
109          Ball.VelY = Ball.VelY * -1;
110        if (Ball.X - Ball.Radius <= 0) {
111          Computer.Score++;
112          Ball.Reset();
113        } else if (Ball.X + Ball.Radius > Width) {
114          Player.Score++;
115          Ball.Reset();
116        }
117        if (Computer.Score === 10)
118          GameOver(false);
119        else if (Player.Score === 10)
120          GameOver(true);
121        Computer.Y = (Computer.Y + Computer.Height / 2 < Ball.Y ? Computer.Y + Computer.Vel : Computer.Y - Computer.Vel);
122      };
123
124      function GameOver(win) {
125        cancelRequestAnimFrame(init);
126        BG.Paint();
127        ctx.fillStyle = "#999";
128        ctx.font = "bold 40px Calibri";
129        ctx.textAlign = "center";
130        ctx.fillText((win ? "YOU WON!" : "GAME OVER"), Width / 2, Height / 2);
131        ctx.font = "normal 16px Calibri";
132        ctx.fillText("refresh to replay", Width / 2, Height / 2 + 20);
133      }
134      return {
135        Start: function() {
136          Ball.Reset();
137          Player.Score = 0;
138          Computer.Score = 0;
139          Computer.Vel = 1.25;
140          Loop();
141        }
142      };
143    })();
144    game.Start();
145  </script>
146</body>
147</html>
Reroku (Heroku on a Raspberry Pi)

My team recently switched our applications from Heroku to barebones AWS. The experience made me truely appreciate just how effective Heroku is and how it has made deploying apps as painfree as could be. After all, what good is making an app if nobody can see it!

I’ve also been a huge fan of my Raspberry Pi. Every time I look at it, I feel challenged to think of a cool use for it.

...