Initial commit

This commit is contained in:
2016-05-21 10:42:57 +01:00
commit a2e3afb01d
1689 changed files with 2262 additions and 0 deletions
@@ -0,0 +1,168 @@
---
layout: post.html
title: "Passing Properties to your GateKeeper"
date: 2013-10-22
tags: Java
summary: "Recently I've had to re-visit some old code containing a couple GateKeepers I wrote nearly two years ago. Looking at them now I realise how terribly unscalable they are. The gatekeepers are in place to protect some web service's and specify (in the gatekeeper code) which methods are open to all and which need restriction. <br/><br/>This is how not to write a gatekeeper."
---
Recently I've had to re-visit some old code containing a couple GateKeepers I wrote nearly two years ago. Looking at them now I realise how terribly unscalable they are. The gatekeepers are in place to protect some web service's and specify (in the gatekeeper code) which methods are open to all and which need restriction.
This is how not to write a gatekeeper.
While we don't have the effort or need to change it I have been thinking about how this could be better should it be done in the future.
What I have come up with (so far, this is very much a <abbr title="Work in Progress">WIP</abbr>) is a method of passing properties to the gatekeeper for each method to specify how the gatekeeper should act.
The idea boils down to using a custom annotation that the GateKeeper can access values of.
<pre><code class="java">
/**
* Provides a method annotation for use by a GateKeeper
* @author Marcus
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface GateKeeperProperty {
enum AccessLevel{
PUBLIC, OWN_CONTENT, ADMIN
}
public boolean logAccess() default true;
public AccessLevel accesLevel() default AccessLevel.PUBLIC;
}
</code></pre>
This annotation can then be used on your webservice methods to pass values regarding to logging access and access restriction.
<pre><code class="java">
/**
* Example web service demonstrating gatekeeper properties
* @author Marcus Noble
*/
@Interceptors(GateKeeper.class)
@WebService(serviceName = "ExampleWebService")
@Stateless()
public class ExampleWebService {
@WebMethod(operationName = "getListOfCountries")
@GateKeeperProperty(accesLevel = GateKeeperProperty.AccessLevel.PUBLIC, logAccess = false)
public List<String> getListOfCountries() {
// Fetch country list
return new ArrayList<String>();
}
@WebMethod(operationName = "getPersonFamilyName")
@GateKeeperProperty(accesLevel = GateKeeperProperty.AccessLevel.OWN_CONTENT, logAccess = false)
public String getPersonFamilyName(@WebParam(name="sessionId") String sessionId, @WebParam(name="userId") int userId) {
// Fetch person's family name
return "";
}
@WebMethod(operationName = "getSuperSecretData")
@GateKeeperProperty(accesLevel = GateKeeperProperty.AccessLevel.ADMIN, logAccess = true)
public String getSuperSecretData(@WebParam(name="sessionId") String sessionId) {
// Fetch super secret data
return "";
}
}
</code></pre>
Here we have three different method that perform three different types of action.
The first simply returns a list of country names, there is no reason to perform any restriction or logging on this as it is simply a helper method to populate a dropdown list.
The second requires slightly more restriction as we are potentially exposing private data. Here we have stated that the restriction is `OWN_CONTENT` which I have defined as 'The requesting user can only access data owned or related to that user' so in this case they can only request their own family name.
> Why not just pass in the sessionId and work it out from that?
This is a good question. It could very well be done this way without any difficulty. The reason for the sessionId and userId is so admin users or 'privileged systems' can call the method and the GateKeeper can be built to allow it.
Finally we have a method that exposes one of our deep dark system secret. This must only be accessible by our most trusted of users, our admins. So for this we make sure the access limit is set to `ADMIN` and logging is enabled so we can keep an eye on any unusal behaviour.
So by this point you may be wondering what magic the GateKeeper needs to perform to bring this all together. Well it's actually quite simple, the `InvocationContext` passed in to the `@AroundInvoke` interceptor method give you access to everything about the called method including its annotation.
<pre><code class="java">
@AroundInvoke
public Object intercept(InvocationContext ctx) throws Exception {
Method method = ctx.getMethod();
if(method.isAnnotationPresent(GateKeeperProperty.class)){
GateKeeperProperty gatekeeperProperty = method.getAnnotation(GateKeeperProperty.class);
// do something with the annotation
}
}
</code></pre>
So with this we have access to values associated with the calling method without the need to pre-define the method in the GateKeeper. This is a huge advance from my original GateKeeper, no more hard coded lists of public and private method that often get out of date or contain duplicates.
So in full our GateKeeper may look something like...
<pre><code class="java">
/**
* An example GateKeeper reading properties provided by the methods
* @author Marcus
*/
public class GateKeeper {
@AroundInvoke
public Object intercept(InvocationContext ctx) throws Exception {
Method method = ctx.getMethod();
boolean logAccess = true; // Default action
GateKeeperProperty.AccessLevel accessLevel = GateKeeperProperty.AccessLevel.PUBLIC; // Default access level
boolean accessGranted = true; // By default people have access (if you have more private method it is best to set this to false)
if(method.isAnnotationPresent(GateKeeperProperty.class)){
GateKeeperProperty gatekeeperProperty = method.getAnnotation(GateKeeperProperty.class);
logAccess = gatekeeperProperty.logAccess();
accessLevel = gatekeeperProperty.accesLevel();
}
switch(accessLevel){
case PUBLIC :
default:
accessGranted = true;
break;
case OWN_CONTENT :
accessGranted = ensureOwnContent(ctx);
break;
case ADMIN :
accessGranted = ensureAdmin(ctx);
break;
}
try {
if(logAccess){
Log.log("Calling "+method.getName());
}
if(accessGranted){
return ctx.proceed();
}else{
if(logAccess){
Log.log("Access prevented to "+method.getName());
}
return null;
}
} catch (Exception e) {
if(logAccess){
Log.log("Failed Calling "+method.getName());
}
return null;
}finally{
if(logAccess){
Log.log("Finished Calling "+method.getName());
}
}
}
private boolean ensureOwnContent(InvocationContext ctx){
// Perform checks that user is accessing own data
return false;
}
private boolean ensureAdmin(InvocationContext ctx){
// Perform checks that user is an admin
return false;
}
}
</code></pre>
Hopefully in the future this may become of use to me and provide a more scalable GateKeeper class. I am still tossing around the idea of how to correctly handle the `OWN_CONTENT` or similar type of restriction that requires some knowledge of the method's inputs and outputs, hopefully that will come to me soon...
@@ -0,0 +1,29 @@
---
layout: post.html
title: "Publishing My First NPM module"
date: 2013-10-29
tags: nodejs node npm javascript truncatise
summary: "For the past week or so (since setting up my new blog) I've been working on a new JavaScript module that truncates HTML."
---
For the past week or so (since setting up my new blog) I've been working on a new JavaScript module that truncates HTML.
Why have I been doing this you ask? Well, very quickly after putting together the theme for this [Ghost](https://ghost.org/) blog I notice a small annoyance. The helper used to generate excerpts of post for the main page can only truncate based on character or word count. This seemed like a bit of a shortcoming to me, I wanted to be able to truncate on paragraphs so text isn't cut off mid-sentence.
And so [Truncatise](//github.com/AverageMarcus/Truncatise) was born!
As this is my first forte into NPMs I used it as a learning experience.
My first learning opportunity came from writing tests against my new module (which turned out to be **VERY** useful in highlighting some mistakes). For this I used [mocha](https://github.com/visionmedia/mocha) and [chai](https://github.com/chaijs/chai) to construct some BDD/TDD tests.
I also took this as a chance to get to know git a bit better, with use of [http://git-scm.com/book](http://git-scm.com/book), and used the command line tools to set up the repository etc.
The [documentation](https://npmjs.org/doc/developers.html) to publish to the NPM registry was very straightforward. Surprisingly so.
* NPM Registry: [https://npmjs.org/package/truncatise](https://npmjs.org/package/truncatise)
* GitHub Repository: [https://github.com/AverageMarcus/Truncatise](https://github.com/AverageMarcus/Truncatise)
* Bug Reporting: [https://github.com/AverageMarcus/Truncatise/issues](https://github.com/AverageMarcus/Truncatise/issues)
I very much welcome bug reports and any contributions. So please use, test, break and modify to your use.
I eventually plan to replace the truncation module used by [Ghost](https://ghost.org/) with mine to get some real-world testing. If all is stable and bug-free I plan to put in a pull request to the master repository.
@@ -0,0 +1,94 @@
---
layout: post.html
title: "Updating Ghost to use Truncatise"
date: 2013-11-10
tags: nodejs node npm javascript truncatise ghost
summary: "Today I finally got around to modifying my <a href='https://ghost.org/' target='_blank'>Ghost</a> blog to make use of my new Node Module <a href='//blog.marcusnoble.co.uk/publishing-my-first-npm-module/'>Truncatise</a>.<br/><br/>This didn't go as smoothly as I hoped."
---
Today I finally got around to modifying my [Ghost](https://ghost.org/) blog to make use of my new Node Module [Truncatise](/10-29-2013-publishing-my-first-npm-module/).
This didn't go as smoothly as I hoped.
First, I discovered a [bug](//github.com/AverageMarcus/Truncatise/issues/1) in my module. It was incorrectly handling the combination of `<p>` tags with double newlines when truncating to paragraphs.
To resolve this I needed to determine whether or not to use double newline to indicate a paragraph. This was done by ignoring any newlines between paragraph tags and better regular expression matching.
I also noticed an issue with the suffix, e.g. &hellip;, when used with `<p>` tags, it was rending after the and of the tag and thus display on a new line. _Whoops!_ Not what is expected. A quick little replace when not stripping HTML solved this.
So, finally I published version 0.0.2 of [Truncatise](//npmjs.org/package/truncatise) to the NPM repository (as well as [GitHub](//github.com/AverageMarcus/Truncatise)).
As I was now happy that the issues were resolved, I made progress on modifying the Ghost helper source code to use Truncatise instead of [downsize](//npmjs.org/package/downsize).
#### Original:
<pre><code class="javascript">
coreHelpers.excerpt = function (options) {
var truncateOptions = (options || {}).hash || {},
excerpt;
truncateOptions = _.pick(truncateOptions, ['words', 'characters']);
/*jslint regexp:true */
excerpt = String(this.html).replace(/<\/?[^>]+>/gi, '');
excerpt = excerpt.replace(/(\r\n|\n|\r)+/gm, ' ');
/*jslint regexp:false */
if (!truncateOptions.words && !truncateOptions.characters) {
truncateOptions.words = 50;
}
return new hbs.handlebars.SafeString(
downsize(excerpt, truncateOptions)
);
};
</code></pre>
#### My Version:
<pre><code class="javascript">
coreHelpers.excerpt = function (options) {
var truncateOptions = (options || {}).hash || {TruncateLength: 2, TruncateBy : "paragraphs", StripHTML : false, Suffix : '...'},
excerpt;
truncateOptions = _.pick(truncateOptions, ['TruncateBy', 'TruncateLength', 'StripHTML', 'Strict', 'Suffix']);
excerpt = String(this.html);
//Set default values
if (!truncateOptions.TruncateLength) {
truncateOptions.TruncateLength = 2;
}
if (!truncateOptions.TruncateBy) {
truncateOptions.TruncateBy = "paragraphs";
}
if (!truncateOptions.StripHTML) {
truncateOptions.StripHTML = false;
}
if (!truncateOptions.Suffix) {
truncateOptions.Suffix = "&hellip;";
}
return new hbs.handlebars.SafeString(
truncatise(excerpt, truncateOptions)
);
};
</code></pre>
My changes have been pushed to my [fork of Ghost](//github.com/AverageMarcus/Ghost) if anyone wishes to make use of it.
## On Updating Ghost
I got into a bit of a mess when trying to update my copy of Ghost with the latest changes, mainly due to my carelessness. So I don't get into the same situation in the future, and to prevent others making my mistakes, here are my little tips to bare in mind:
1. Back up the `./content/data/` directory before doing anything!
2. If needed also backup your `./content/themes/` directory.
3. Copy across the new `./core`, `packages.json`, `index.js` and `Gruntfile.js`
4. `npm install -g grunt-cli`
5. `npm install --production`
6. `grunt init prod`
> If you get errors relating to sqlite3&hellip;
Run `npm install sqlite3 --build-from-source=sqlite3`
# Update:
I have since moved away from Ghost and now using Jekyll
@@ -0,0 +1,58 @@
---
layout: post.html
title: "The Power of the Bookmarklet"
date: 2013-11-21
tags:
summary: "Recently an occasion came up where I found myself repetitively typing in the same type of text into a web form. The form in question is the 'Tag this build' page in <a href='http://jenkins-ci.org/' target='_blank'>Jenkins</a>."
---
Recently an occasion came up where I found myself repetitively typing in the same type of text into a web form. The form in question is the "Tag this build" page in [Jenkins](http://jenkins-ci.org/).
![Tag this build]({{ site.url }}/images/tagthisbuild-1.PNG)
I realised that the 'Tag URL' I was entering followed a very specific pattern. The first part was taken from the 'Module URL' (here blurred out) minus the string after the final '/'. This is then followed by todays date, the name of the job being tagged (dev_Authenticate-Build-Mobile in this case) and finally the string after the final '/' we ommitted from the beginning.
The code monkey in me was crying out for this to be automated, and as a lover of JavaScript what better way to accomplish it than a nice and simple script. So I set to work! The script I currently use is very much tailored to our setup and uses hard coded URL's. As we only have two SVN repositories this was more than acceptable.
The code:
<pre><code class="javascript">
(function(){
//Find all labels that are for the tag boxes
jQuery("label[for^='tag']").each(function(){
var url = "",
id = jQuery(this).attr('for').replace("tag", ""),
now = new Date(),
today = now.getFullYear().toString() + ((now.getMonth()+1).toString().length == 1 ? "0"+(now.getMonth()+1).toString() : (now.getMonth()+1).toString()) + (now.getDate().toString().length == 1 ? "0"+now.getDate().toString() : now.getDate().toString()),
re = /([a-zA-Z-_]+)\/(\d+)\//,
buildProject = document.URL.match(re)[1] //Get the build job from the current URL
;
//We have two repositories so we simply need to check which one to use and build the URL
if(jQuery(this).text().indexOf("BIS/")>= 0){
var application = jQuery(this).text().substring(jQuery(this).text().lastIndexOf("BIS/")+4, jQuery(this).text().indexOf(" ") );
url = "https://svn.example.ac.uk/BIS/RELEASE/"+today+"/" + buildProject + "/"+application;
}else if(jQuery(this).text().indexOf("J2EE/")>= 0){
var application = jQuery(this).text().substring(jQuery(this).text().lastIndexOf("J2EE/")+5, jQuery(this).text().indexOf(" ") );
url = "https://svn.example.ac.uk/J2EE/release/"+today+"/" + buildProject + "/"+application;
}
//Populate the tag input
jQuery("input[id='name"+id+"']").val(url);
});
//Add a comment
jQuery("textarea[name='comment']").val(jQuery("textarea[name='comment']").val().replace("#", "Jenkins Build "));
return false;
})();
</code></pre>
The code requires [jQuery](http://jquery.com/) but as Jenkins already uses it I didn't need to reference it in the script.
Now to make this useful I turned this script into a bookmark that I could place in my browser's bookmark bar and click to execute the script. I used a very simple tool to do this, http://mrcoles.com/bookmarklet/, simply enter the script, click convert and drag the result into your bookmarks.
The end result looks something like this:
<pre><code class="javascript">
javascript:(function()%7B(function()%7BjQuery("label%5Bfor%5E%3D'tag'%5D").each(function()%7Bvar url %3D ""%2Cid %3D jQuery(this).attr('for').replace("tag"%2C "")%2Cnow %3D new Date()%2Ctoday %3D now.getFullYear().toString() %2B ((now.getMonth()%2B1).toString().length %3D%3D 1 %3F "0"%2B(now.getMonth()%2B1).toString() %3A (now.getMonth()%2B1).toString()) %2B (now.getDate().toString().length %3D%3D 1 %3F "0"%2Bnow.getDate().toString() %3A now.getDate().toString())%2Cre %3D %2F(%5Ba-zA-Z-_%5D%2B)%5C%2F(%5Cd%2B)%5C%2F%2F%2CbuildProject %3D document.URL.match(re)%5B1%5D%3Bif(jQuery(this).text().indexOf("BIS%2F")>%3D 0)%7Bvar application %3D jQuery(this).text().substring(jQuery(this).text().lastIndexOf("BIS%2F")%2B4%2C jQuery(this).text().indexOf(" ") )%3Burl %3D "https%3A%2F%2Fsvn.example.ac.uk%2FBIS%2FRELEASE%2F"%2Btoday%2B"%2F" %2B buildProject %2B "%2F"%2Bapplication%3B%7Delse if(jQuery(this).text().indexOf("J2EE%2F")>%3D 0)%7Bvar application %3D jQuery(this).text().substring(jQuery(this).text().lastIndexOf("J2EE%2F")%2B5%2C jQuery(this).text().indexOf(" ") )%3Burl %3D "https%3A%2F%2Fsvn.example.ac.uk%2FJ2EE%2Frelease%2F"%2Btoday%2B"%2F" %2B buildProject %2B "%2F"%2Bapplication%3B%7DjQuery("input%5Bid%3D'name"%2Bid%2B"'%5D").val(url)%3B%7D)%3BjQuery("textarea%5Bname%3D'comment'%5D").val(jQuery("textarea%5Bname%3D'comment'%5D").val().replace("%23"%2C "Jenkins Build "))%3Breturn false%3B%7D)()%7D)()
</code></pre>
Now the next time I want to tag a build I simple click my bookmarklet and I'm all done. Much better. (:
And just to show how fun bookmarklets can be, head over to [Kick Ass](https://kickassapp.com/) and have fun destroying the web!
@@ -0,0 +1,85 @@
---
layout: post.html
title: "JSOxford Code Retreat - or how to use an Apple keyboard"
date: 2014-07-12
tags: JavaScript
summary: "Last Sunday I went to a Code Retreat hosted by [JSOxford](http://jsoxford.com/). This was a full day event of coding, split into four sections all based around [Conway's Game of Life](http://en.wikipedia.org/wiki/Conway's_Game_of_Life)."
---
Last Sunday I went to a Code Retreat hosted by [JSOxford](http://jsoxford.com/). This was a full day event of coding, split into four sections all based around [Conway's Game of Life](http://en.wikipedia.org/wiki/Conway's_Game_of_Life).
{{> tweet id='485697274174275584' }}
For those not familiar with Conway's Game Of Life I will be doing another blog post on it shortly. But simply put it is a basic computer model of life.
For the whole day we focused on perfecting code rather than finishing. We didn't care if we had anything that worked or not, the idea was to practice different forms of programming. (*We were even told to delete all our code after each round*)
## Round One - Paper
The first round was aimed at getting us familiar with the task at hand. We paired up, as we did with most of the rounds, and tasked with writing out the code we thought would make up the game on pieces of paper. I had paired up with [Dan Pope](https://twitter.com/danielthepope) and we set to work on the writing out our code, *sans-highlighting*.
{{> tweet id='485722324604579840' }}
Dan focused on a method that calculates the number of neighbours a given cell has while I focused on the main game loop. The absence of an <abbr title="Integrated development environment">IDE</abbr> is a terrible thing so our code was pseudo-JavaScript/pseudo-Python. We had plenty of time to write out the whole game with a bit of time left at the end to go through and fix a couple of potential bugs!
## Round two - TDD/BDD
The next round was in more familiar territory. The rules stated we must first write a test to verify some of the game logic, then the other person must write the minimal amount of code to make that test pass. In the most haphazardly way possible.
For example:
<pre><code class="javascript">
// Test
describe('Calculator', function(){
describe('sum', function(){
it('should return 2', function(){
(calculator().sum(1,1)).should.eql(2);
})
})
})
//Code
function calculator(){
function sum(n1, n2){
return 2;
}
return { sum : sum }
}
</code></pre>
As you can see, the code doesn't actually work as expected, it just does enough to make the tests pass. I don't know how often this sort of thing actually happens in the 'real world', I know we sure don't do this. :sweat_smile:
## Lunch
{{> tweet id='485758298755698688' }}
Not the best lunch in the world but a free lunch is a free lunch.
## Round three - Randori
The word 'Randori' comes from the martial art Aikido and means many people attacking the same person. In the world of programming this lends itself towards paired programming, but with more partners.
{{> tweet id='485774627139223552' }}
Think one laptop, with one person on the keys, and a group of people gathered around telling them what to code. The person typing had no decision over what to write, their partner is the one "driving" with help from the others stood around. This is taken in turns, swapping every 5 minutes.
Having 8 people trying to decide what to code, without any prior spec or discussion went about as well as you'd expect. The code jumped around a bit and we had to backtrack at some point but the biggest challenged we faced was getting to grips with a MacBook keyboard. Who knew that they didn't have all the keys needed?
<figure class="center" markdown="1">
![MacBook symbol]({{ site.url }}/images/macbook-alt-symbol.PNG)
<figcaption>WTF is this supposed to be?</figcaption>
</figure>
## Round four - Immutables
Task four saw us trying to build the game using only immutable variables. This was **hard**.
I tried to solve this using maps, filters, cloning etc. but I ended up running out of time before I had a fully working solution. I did manage to have cells die, just couldn't generate new ones.
## Roundup
All in all it was a really good day that gave me a chance to flex a bit of JavaScript outside of the browser. If nothing else, it has made me very excited for the upcoming [NodeBots](https://www.eventbrite.co.uk/e/nodebots-summer-of-hacks-tickets-11906664153) event.
I hope to put together a fully working version of Conway's Game of Life shortly and will put together another post about it.
@@ -0,0 +1,312 @@
---
layout: post.html
title: "Conway's Game of Life"
date: 2014-07-14
tags: JavaScript Programming
summary: "As I [mentioned previously](/12-07-2014-jsoxford-code-retreat/), last week at the JSOxford Code Retreat we spent a lot of time (all the time) developing [Conway's Game of Life](http://en.wikipedia.org/wiki/Conway's_Game_of_Life) in various ways. As the code retreat was focusing on different coding methods and perfecting code rather than producing something that works I never got a fully working version. So last night I sat down and got to it!"
---
As I [mentioned previously](/12-07-2014-jsoxford-code-retreat/), last week at the JSOxford Code Retreat we spent a lot of time (all the time) developing [Conway's Game of Life](http://en.wikipedia.org/wiki/Conway's_Game_of_Life) in various ways. As the code retreat was focusing on different coding methods and perfecting code rather than producing something that works I never got a fully working version. So last night I sat down and got to it!
# About
Conway's Game of Life is a 0-player turn based simulation of life. The "game" follows some simple rules to update its cells on a board, crudely imitation evolution and life.
# Rules
The rules of the game are as follows:
<blockquote markdown="1">
* Any live cell with fewer than two live neighbours dies, as if caused by under-population.
* Any live cell with two or three live neighbours lives on to the next generation.
* Any live cell with more than three live neighbours dies, as if by overcrowding.
* Any dead cell with exactly three live neighbours becomes a live cell, as if by reproduction.
<cite>Wikipedia - http://en.wikipedia.org/wiki/Conway's_Game_of_Life</cite>
</blockquote>
# Approach
As we learnt last week, there are two ways to go about storing the game data (the state of the cells). The simple, and possibly naïve, way is to store a 2 dimensional array containing the state of every cell on the game board. The other is to only store a collection of live cells with their X and Y locations.
## 2D array
Storing the cell data in a two dimensional array is pretty simple.
<pre><code class="javascript">
var gameBoard = [
[true, false, false, true, false],
[true, false, false, true, false],
[true, false, false, true, false],
[true, false, false, true, false],
[true, false, false, true, false]
];
</code></pre>
The key thing to remember when approaching it this way is that the game board array must be cloned first before updating the cells. This is because all cells must be updated simultaneously so looping over and updating cell-by-cell would cause incorrect updating as you go through.
While this is the simplest approach, and the way we attempted when writing it out on paper, it doesn't scale very well. As the game is supposed to be infinite in size the array could very easily get too large for memory and cause the game to crash. This can be avoided by limiting the array to bounding dimensions.
## Collection of Live Cells
The more scalable approach involves only storing the positions of the live cells. As all rules are based around the presence of live cells, the next iteration of cells can be calculated from this list.
<pre><code class="javascript">
var gameBoard = [
{ x: 3, y: 5},
{ x: 8, y: 2},
{ x: 13, y: 5},
{ x: 5, y: 9},
{ x: 9, y: 10}
];
</code></pre>
This is the approach I used last night. I have struggled on effectively calculating new cells to be "born", having resulted in iterating over all cells, checking if they are dead and then counting their live neighbours. This obviously goes against the infinite game board idea so if anyone has some suggestions on how to improve it you can <a href="https://twitter.com/intent/tweet?screen_name=Marcus_Noble_" class="twitter-contact-link" data-related="Marcus_Noble_" data-dnt="true" target="_blank"><i class="icon-twitter"></i>Tweet @Marcus\_Noble_</a> or fork my [gist](https://gist.github.com/AverageMarcus/f5e34825ef89e11443be).
# Final Code
<figure class="center" markdown="1">
<a href="/ConwaysGameOfLife.html">
![Conway's Game of Life]({{ site.url }}/images/conways-game-of-life.PNG)
</a>
<figcaption>They LIVE!!!</figcaption>
</figure>
<pre><code class="javascript">
var cells = [];
var boardWidth = 20;
var boardHeight = 20;
var intervalID;
function init(){
var cell = $('<div/>').addClass('cell');
var row = $('<div/>').addClass('row');
var board = $('#gameBoard');
var currentRow, i, j, newCell;
$('#gameBoard').empty().on('click', function(){
clearInterval(intervalID);
init();
});
cells = [];
for(i=0;i<boardHeight;i++){
currentRow = row.clone();
board.append(currentRow);
for(j=0;j<boardHeight;j++){
currentRow.append(cell.clone());
}
}
for(i=0;i<50;i++){
newCell = {
x: (Math.floor(Math.random() * (boardWidth - 1) + 1)),
y: (Math.floor(Math.random() * (boardHeight - 1) + 1))
};
cells.push(newCell)
}
render();
intervalID = setInterval(function(){
tick();
}, 1000);
}
function render(){
$('.cell').css('background','white');
for(var i=0; i<cells.length;i++){
$($($('.row').get(cells[i].y-1)).find('.cell').get(cells[i].x-1)).css('background', 'black');
}
}
function tick(){
var neighbourCount = 0;
var nextIteration = [];
var deadCell, i, j;
// All cells are dead, lets restart
if(cells.length === 0){
clearInterval(intervalID);
setTimeout(function(){
init();
}, 5000);
return;
}
for(i=0; i<cells.length;i++){
neighbourCount = countNeighbours(cells[i]);
if(neighbourCount >= 2 && neighbourCount <= 3){
nextIteration.push(cells[i]);
}
}
for(i=1;i<=boardHeight;i++){
for(j=1;j<=boardWidth;j++){
deadCell = {x:j,y:i};
if(!isAlive(deadCell) && countNeighbours(deadCell) === 3){
nextIteration.push(deadCell);
}
}
}
cells = nextIteration;
render();
}
function isAlive(cell){
return (cells.filter(function(currentCell){
return (currentCell.x === cell.x && currentCell.y === cell.y);
}).length > 0);
}
function countNeighbours(cell){
var x1 = cell.x-1,
x2 = cell.x+1,
y1 = cell.y-1,
y2 = cell.y+1;
var neighbours = cells.filter(function(possibleNeighbour){
if(possibleNeighbour.x === cell.x && possibleNeighbour.y === cell.y){
return false;
}
return (possibleNeighbour.x >= x1 && possibleNeighbour.x <= x2 && possibleNeighbour.y >= y1 && possibleNeighbour.y <= y2);
});
return neighbours.length;
}
init();
</code></pre>
You can check out the [final version](/ConwaysGameOfLife.html) or take a look at the [gist](https://gist.github.com/AverageMarcus/f5e34825ef89e11443be).
As always, if you have any comments <a href="https://twitter.com/intent/tweet?screen_name=Marcus_Noble_" class="twitter-contact-link" data-related="Marcus_Noble_" data-dnt="true" target="_blank"><i class="icon-twitter"></i>Tweet @Marcus\_Noble_</a>.
# Updated 2014-07-22
With a little help from [@danielthepope](https://twitter.com/danielthepope/) I have improved the code slightly to remove the fixed universe problem. Cells are now born based on live cells instead of iterating over the game board.
<pre><code class="javascript">
var cells = [];
var boardWidth = 20;
var boardHeight = 20;
var $board = $('#gameBoard');
var intervalID;
function init(){
var cell = $('<div/>').addClass('cell');
var row = $('<div/>').addClass('row');
var currentRow, i, j, newCell;
$('#gameBoard').empty().on('click', function(){
clearInterval(intervalID);
init();
});
cells = [];
for(i=0;i<boardHeight;i++){
currentRow = row.clone();
$board.append(currentRow);
for(j=0;j<boardHeight;j++){
currentRow.append(cell.clone());
}
}
for(i=0;i<50;i++){
newCell = {
x: (Math.floor(Math.random() * (boardWidth - 1) + 1)),
y: (Math.floor(Math.random() * (boardHeight - 1) + 1))
};
cells.push(newCell)
}
render();
intervalID = setInterval(function(){
tick();
}, 1000);
}
function render(){
$board.find('.cell').css('background','white');
for(var i=0; i<cells.length;i++){
$($($('.row').get(cells[i].y-1)).find('.cell').get(cells[i].x-1)).css('background', 'black');
}
}
function tick(){
var neighbourCount = 0;
var nextIteration = [];
var candidates = [];
var neighbours, i, j,
cellX, cellY,
cellLength, neighbourLength, candidateLength;
// All cells are dead, lets restart
if(cells.length === 0){
clearInterval(intervalID);
init();
return;
}
for(i=0, cellLength=cells.length; i<cellLength; i++){
neighbourCount = countNeighbours(cells[i]);
if(neighbourCount >= 2 && neighbourCount <= 3){
nextIteration.push(cells[i]);
}
// For each cell that is alive, find its neighbours
cellX = cells[i].x;
cellY = cells[i].y;
neighbours = [];
neighbours.push({x:(cellX-1),y:(cellY-1)});
neighbours.push({x:(cellX-1),y:cellY});
neighbours.push({x:(cellX-1),y:(cellY+1)});
neighbours.push({x:cellX,y:(cellY-1)});
neighbours.push({x:cellX,y:(cellY+1)});
neighbours.push({x:(cellX+1),y:(cellY-1)});
neighbours.push({x:(cellX+1),y:cellY});
neighbours.push({x:(cellX+1),y:(cellY+1)});
for(j=0, neighbourLength=neighbours.length; j<neighbourLength; j++){
// Keep the neighbour cells if they are dead
// We don't want to keep multiple copies of the neighbour cell
if(!isAlive(neighbours[j]) && candidates.filter(function(currentCell){
return (currentCell.x === neighbours[j].x && currentCell.y == neighbours[j].y);
}).length === 0) {
candidates.push(neighbours[j]);
}
}
}
// Now we have a list of dead neighbours of at least 1 live neighbour (candidates)
// Bring the cell to life if we find that each neighbour has 3 other live neighbouring cells
for(i=0, candidateLength=candidates.length; i<candidateLength; i++){
if(countNeighbours(candidates[i]) === 3){
nextIteration.push(candidates[i]);
}
}
cells = nextIteration;
render();
}
function isAlive(cell){
return (cells.filter(function(currentCell){
return (currentCell.x === cell.x && currentCell.y === cell.y);
}).length > 0);
}
function countNeighbours(cell){
var x1 = cell.x-1,
x2 = cell.x+1,
y1 = cell.y-1,
y2 = cell.y+1;
var neighbours = cells.filter(function(possibleNeighbour){
if(possibleNeighbour.x === cell.x && possibleNeighbour.y === cell.y){
return false;
}
return (possibleNeighbour.x >= x1 && possibleNeighbour.x <= x2 && possibleNeighbour.y >= y1 && possibleNeighbour.y <= y2);
});
return neighbours.length;
}
init();
</code></pre>
@@ -0,0 +1,164 @@
---
layout: post.html
title: "Achieving 100 Google points"
date: 2014-07-17
tags: Web
summary: "Recently I have re-built my blog (hence the new life that has sprouted), not that you can tell. I spent a portion of last week moving away from [Ghost](https://ghost.org/) to using [Jekyll](http://jekyllrb.com/). With this I took the opportunity to improve the performance by utilizing [Grunt](http://gruntjs.com/) as part of the build workflow. This allowed me to achieve a 100/100 rating on [Google PageSpeed Insights](http://developers.google.com/speed/pagespeed/insights/?url=blog.marcusnoble.co.uk)."
---
Recently I have re-built my blog (hence the new life that has sprouted), not that you can tell. I spent a portion of last week moving away from [Ghost](https://ghost.org/) to using [Jekyll](http://jekyllrb.com/). With this I took the opportunity to improve the performance by utilizing [Grunt](http://gruntjs.com/) as part of the build workflow. This allowed me to achieve a 100/100 rating on [Google PageSpeed Insights](http://developers.google.com/speed/pagespeed/insights/?url=blog.marcusnoble.co.uk).
{{> picture alt="100/100" caption="All the Google points!" url="/images/GooglePageInsights.PNG" }}
> So, what's my secret?
Well its simple, do what [Google says](https://developers.google.com/speed/docs/insights/rules).
Here are the steps I took to get up to the 100 mark...
## 1. Use a static site
Using static sites (as opposed to dynamic sites) makes this much easier. As the HTML is fixed and not going to change based on what the user does or what data may be in a database you can ensure that what is rendered is in the best possible state.
I have used [Jekyll](http://jekyllrb.com/) for my Blog but I have heard good things about [Middleman](http://middlemanapp.com/), [Harp.js](http://harpjs.com/) and [Sculpin](https://sculpin.io/).
## 2. Be tappable
For any links, buttons, etc. on your pages that you want people to interact with make sure they can be pressed with a fat finger on a small touchscreen. This means to give grouped buttons enough space around them so the user can easily tap the one they want without inadvertently tapping the other and that links are roughly 2.4mm in height.
This one I have to keep fighting with. As new links appear on the page, Google sometimes decides some of them aren't big enough.
> The following tips make use of [Grunt](http://gruntjs.com/), if you haven't used it before then be sure to read the [getting started](http://gruntjs.com/getting-started) first.
## 3. Remove unused CSS
There is a great Grunt plugin called [uncss](https://github.com/addyosmani/grunt-uncss) that will remove unused CSS from your stylesheets, thus reducing the overall filesize. I found this to be mostly useful when applied to 3<sup>rd</sup> party stylesheets (such as bootstrap).
### Example config
<pre><code class="javascript">
uncss: {
dist: {
options: {
ignore: ['.center'], // Ensure these styles aren't removed
stylesheets : ['css/screen.css'] // The stylesheets to check
},
files: {
'css/screen.min.css': ['*.html', '_layouts/*.html', '_includes/*.html'] // Output stylesheet and the pages to check against
}
},
bootstrap: {
options: {
stylesheets : ['css/bootstrap.min.css']
},
files: {
'css/bootstrap.min.css': ['*.html', '_layouts/*.html', '_includes/*.html']
}
},
fontawesome: {
options: {
stylesheets : ['css/font-awesome.min.css']
},
files: {
'css/font-awesome.min.css': ['*.html', '_layouts/*.html', '_includes/*.html']
}
}
}
</code></pre>
## 4. Shrink your CSS more!
Removing unused CSS is nice but there is still a lot of bloat that isn't required - spaces, line breaks & comments. The browser doesn't use any of this, so why bother sending it? The [grunt-contrib-cssmin](https://github.com/gruntjs/grunt-contrib-cssmin) achieves this nicely. To keep files readable when developing we can have cssmin output the minified files as a *.min.css file, leaving the original intact. I don't use any JavaScript on my Blog but there is also [grunt-contrib-uglify](https://github.com/gruntjs/grunt-contrib-uglify) that can shrink down your JavaScript files in a similar way.
### Example config
<pre><code class="javascript">
cssmin: {
dist: {
expand: true,
cwd: 'css/',
src: ['*.css'],
dest: 'css/',
ext: '.min.css'
}
}
</code></pre>
## 5. Get those images into shape
Images often make up the bulk of a websites total filesize.<sup>[*citation needed*]</sup> Just like with CSS we should also be reducing bloat from our images. [grunt-contrib-imagemin](https://github.com/gruntjs/grunt-contrib-imagemin) can do this for us, with options to specify how much compression to apply. I left it at default and saw a rather sizable reduction with some images with no noticeable quality loss.
### Example config
<pre><code class="javascript">
imagemin: {
static: {
options: {
optimizationLevel: 3
},
files: [{
expand: true,
cwd: 'images',
src: '**/*.{png,PNG,jpg,JPG,gif,GIF}',
dest: 'images'
}]
}
}
</code></pre>
## 6. Reducing time over the wire
When a web browser loads a site, it must first fetch the HTML file, read over it all and then fetch any associated resources (stylesheets, JavaScript files) causing additional calls back to the web server. This is just wasteful! Why make the browser ask for what we already know it needs? So, [grunt-html-smoosher](https://github.com/motherjones/grunt-html-smoosher) to the rescue. This handy little plugin will take HTML files and replace resource tags (e.g. `<link>`) with their content inline.
### Example config
<pre><code class="javascript">
smoosher: {
all: {
files: [
{
expand: true,
cwd: '_site/',
src: ['**/*.html'],
dest: '_site/'
}
]
}
}
</code></pre>
## 7. Shrink whats left
So, we've reduced our CSS, our JavaScript (if we have any) and our images. We've event smooshed most of these into a single file. What could possibly be left? Why the HTML of course! So for our final trick we are going to use [grunt-contrib-htmlmin](https://github.com/gruntjs/grunt-contrib-htmlmin) to remove all spacing, line breaks and comments from our final HTML files.
### Example config
<pre><code class="javascript">
htmlmin: {
dist: {
options: {
removeComments: true,
collapseWhitespace: true
},
files: [
{
expand: true,
cwd: '_site/',
src: ['**/*.html'],
dest: '_site/',
},
]
}
}
</code></pre>
# Bonus - Getting the Green Padlock
As an aside I recommend giving this post about [setting up SSL in nginx](https://gauntface.com/blog/2014/07/04/installing-ssl-certs) by [Matt Gaunt](https://gauntface.com/). It walks through the process of getting a free SSL certificate and configuring nginx to make best use of it with [SSL Labs test](https://www.ssllabs.com/ssltest/analyze.html?d=blog.marcusnoble.co.uk&hideResults=on) to validate the improvements
# Conclusions
As you can see, it doesn't take a lot to improve the performance of websites. Pull all this together into an effective workflow and you don't even need to worry about it anymore. I have all my Grunt jobs, including one to [build the Jekyll sites](https://github.com/dannygarcia/grunt-jekyll), as a post-receive [Git hook](http://git-scm.com/book/en/Customizing-Git-Git-Hooks). No more ftp/scp nonsense, no more remembering how to build my site, just code and push. Simple! :smile:
If anyone uses a similar approach to their sites, or has ways to improve please <a href="https://twitter.com/intent/tweet?screen_name=Marcus_Noble_" class="twitter-contact-link" data-related="Marcus_Noble_" data-dnt="true" target="_blank"><i class="icon-twitter"></i>Tweet @Marcus\_Noble_</a>
+150
View File
@@ -0,0 +1,150 @@
---
layout: post.html
title: "NodeBots"
date: 2014-08-24
tags: JavaScript JSOxford
summary: "This weekend hosted the final event of [@JSOxford](https://twitter.com/JSOxford)'s [Summer of Hacks](http://jsoxford.com/2014/summer-of-hacks/) - **NodeBots**."
---
> **Disclaimer:** No robot uprising occurred in the making of this blog post.
This weekend hosted the final event of [@JSOxford](https://twitter.com/JSOxford)'s [Summer of Hacks](http://jsoxford.com/2014/summer-of-hacks/) - **NodeBots** kindly hosted at the [@WhiteOctober](https://twitter.com/whiteoctober) offices.
The day was aimed at learning to program various bits of hardware, predominantly in JavaScript.
This included a whole bunch of Arduinos, some wifi controlled cars, some quad-copters and various other little board such as a [Tessel](https://tessel.io/) and [Espruino](http://www.espruino.com/).
We were given free rain over what we create. Everyone was there to help each other out, in true community spirit.
{{> tweet id='503181282353119232' }}
The amount of kit made available to us was incredible. 10 Ardunio Experimentation Kits, a whole host of sensors and motors, boxes full of Lego & k'nex and plenty of varied electronics to build whatever we wanted.
{{> tweet id='502792423152836608' }}
I didn't have any goal going in. I wasn't sure what I wanted to build. A full blown robot was a bit much so I decided to focus on trying my hand at building ancillary systems.
But before all that we all, obviously, had to do the hardware equivalent of ["Hello World"](http://en.wikipedia.org/wiki/Hello_world_program) - make an LED blink.
One of the main things I did learn at this early stage was to make sure Linux is correctly set up. By default the port the Arduino is set to (likely `/dev/ttyacm0`) has permissions that prevent users from accessing it. Resolve this by either adding yourself to the correct group or by `chmod`ing it to be more permissive.
To program against ardunio using JavaScript we were advised to use a library call [johnny-five](ttps://github.com/rwaldron/johnny-five). I highly recommend it is you are planning on doing any hardware programming as it does support a range of different boards. There is also a **LOT** of examples so it is pretty easy to get up and running with whatever you want.
So with this all set up I started playing with some of the sensors available. A nice easy one to start with was the temperature sensor. It was easy to wire up, all it needed was 5v in, a ground and a data cable. Once wired up, the code is pretty simple.
<pre><code class="javascript">
var five = require("johnny-five"),
board = new five.Board(),
sensor,
temp = { c: 0, f:0 };
board.on("ready", function() {
sensor = new five.Sensor("A0");
function analogToCelsius(analogValue) {
return ((analogValue * 0.004882814) - 0.5) * 100;
}
function analogToFahrenheit(analogValue) {
return analogToCelsius(analogValue) * ( 9/5 ) + 32;
}
sensor.on("data", function() {
var celsiusValue, fahrenheitValue;
// Obtain temperature from current analog value
celsiusValue = analogToCelsius(this.value);
fahrenheitValue = analogToFahrenheit(this.value);
temp.c = celsiusValue;
temp.f = fahrenheitValue;
});
});
setInterval(function(){
console.log(Math.round(temp.c) + "c | "+Math.round(temp.f)+"f");
}, 1000);
</code></pre>
This sets up the board and the sensor and gets the appropriate value from the sensor upon detecting a change in temperature, which is pretty much constant due to how many decimal places it monitors to. I have set up a function that is called every second and outputs current values recorded from the sensor.
{{> tweet id='503494610464047104' }}
As you can see, a nice 21°c at the White October offices.
Then I came across an ultrasonic sensor. This looked fun!
I thought it could be useful as some kind of collision detection to prevent a robotic car from crashing. There is a nice johnny-five example for these so I wired it up, copied over some of the code and took a look at the results. The first thing I realised was the sensor was a little flaky sometimes and the value occasionally jumped up/down. It also couldn't correctly detect something closer than 3cm, it suddenly thinks its about 700cm away.
So I had distance values, time to do something with them. I decided that I would have it respond to things getting too close (people walking past my desk) so I started wiring up a piezo and an LED which would be triggered. When something was detected closer than 90cm the bright red LED started flashing and the piezo started beeping annoyingly to let everyone know.
This was good, but it needed something more, something cute. How about an Octocat?
So, with a little help of a stepper motor, some k'nex and a GitHub sticker, I had a cute l'il Octocat that greeted people as they passed by my station.
{{> tweet id='503501014792953856' }}
My final code can be found below, don't just it wasn't meant to be pretty. You may also notice that I also had a light sensor that turned on a super bright LED when it got too dark. I did have to make sure to move the LED away from the sensor on the breadboard though...
<pre><code class="javascript">
var five = require("johnny-five"),
board, ping, led, piezo, photoresistor, whiteLED, servo, motor, digitalWrite;
board = new five.Board();
board.on("ready", function() {
var that = this;
piezo = new five.Piezo(3);
led = new five.Led(13);
whiteLED = new five.Led(5);
servo = new five.Servo(10);
board.repl.inject({
piezo: piezo,
pot: photoresistor,
servo: servo
});
photoresistor = new five.Sensor({
pin: "A2",
freq: 250
});
ping = new five.Ping(7);
ping.on("change", function( err, value ) {
if(this.cm < 90){
piezo.tone( 150, 50 );
led.strobe(50);
wave(true);
}else {
piezo.noTone();
led.stop();
led.off();
wave(false);
}
});
photoresistor.on("data", function() {
if(this.value > 350){
console.log("Oh no! It's too dark");
whiteLED.on();
}else{
whiteLED.off();
}
});
});
function wave(wave){
if(wave){
servo.to(0);
}else if(!wave){
setTimeout(function(){
servo.to(90);
}, 500);
}
}
</code></pre>
All-in-all it was a really good day which I think everyone enjoyed. Lots was learnt. Laughs were had. I eagerly look forward to the next one.
Thanks to everyone involved in organising it, [White October](http://www.whiteoctober.co.uk/) for hosting it and the various sponsors that allowed it to happen (as well as provide us with some awesome pastries and [burritos](http://missionburrito.co.uk/)).
@@ -0,0 +1,77 @@
---
layout: post.html
title: "Emerging Tech Weekender & Digital Oxford"
date: 2014-11-17
tags: JavaScript DigitalOxford ETWOx Hackathon
summary: "I spent this past weekend in Oxford Brookes participating in the [Emerging Tech Weekender](http://emergingtechweekender.co.uk) organised by [Digital Oxford](http://www.digitaloxford.com)"
---
For those who don't know, Oxford is the place for digital, and [Digital Oxford](http://www.digitaloxford.com) is a community group dedicated to promoting this fact.
Their latest event offering was hackathon going by the name of [Emerging Tech Weekended](http://emergingtechweekender.co.uk), a weekend long event bringing together designers, developers and business brains to explorer new ideas and technologies revolving around three technology themes: Multi-device web, Internet of things and mesh networks. There was tech and business mentors floating about providing help to teams all weekend.
The weekend kicked off on Friday with something of a surprise to me...
{{> tweet id='533329134232289281' }}
[STFC](http://www.stfc.ac.uk) helped sponsor the event. Who knew? (I know I sure didn't).
After the tech mentors gave their presentation & demos we began mingling over drinks and coming up with ideas for teams to work on. When we all came back together we had a large number of good ideas suggested.
{{> tweet id='533355898086129664' }}
We each had two votes we could give to an idea and then teams were formed out of the top voted. I joined the "Dude, where's my cat?" team. The idea was, we never have any idea where our cats are, wouldn't it be great if we could track them somehow?
{{> picture alt="Dude, where's my cat?" caption="Dude, where's my cat?" url="/images/DudeWheresMyCat.jpg" }}
{{> tweet id='533380993265438720' }}
So... we discovered pretty quickly that the idea has been done, repeatedly. :disappointed:
So I joined SenseSpace! The ideas was pretty simple, create a gaming platform that could be used on mobile devices to take digital games into the real world using things like geolocation and mesh netowkring. Our proof of concept was a game of tag. Each player has a mobile device, they access our web application and their position is plotted on a map. Other players in the area are then shown as opponents. A single player is "it" and must catch an opponent by getting close enough for long enough.
Our final project didn't quite make it that far but we did get enough to convey the basic idea to people.
{{> tweet id='533979636909506560' }}
As part of our pitch we did a short example video of us playing tag with our devices. I was even able to make use of a pair of Google Glasses to get some pretty cool point-of-view footage.
I thought Google Glass would be a cool platform to be able to actually play our game on (if you ignore the cost for a moment). Unfortunately...
{{> tweet id='533995183248252929' }}
When it came to Sunday evening it was time for the pitching and judging. There was a lot of goo pitches and a lot of impressive work done in just a couple days.
SenseSpace was getting some very positive feedback.
{{> tweet id='534020893203521536' }}
There was a lot of good ideas from Queue controlling...
{{> picture alt="Q-Control" caption="Q-Control" url="/images/QControl.jpg" }}
...to audience roulette...
{{> picture alt="CrowdLighter" caption="CrowdLighter" url="/images/CrowdLighter.jpg" }}
But my personal favourite, and the one that got my vote during the audience voting, was Team Brush!
{{> tweet id='534030183129702401' }}
So... the results are.........
## Audience Favourite
**Team Brush!**
## Best Technology
**SenseSpace** (Woo!)
## Best Commercially
**Team Brush!**
All in all it was an amazing weekend with a lot of effort put in and fun had by all.
I would like to thank everyone involved in organising and running the event, truly amazing work.
+47
View File
@@ -0,0 +1,47 @@
---
layout: post.html
title: "RTHack"
date: 2015-04-14
tags: JavaScript JSOxford RTHack HackDay
summary: "Last Saturday I spent the day at [JS Oxford](http://jsoxford.com)'s RTHack day. The theme was to explore and build upon real time web technologies. "
---
Last Saturday I spent the day at [JS Oxford](http://jsoxford.com)'s RTHack day. The theme was to explore and build upon real time web technologies.
When we arrived there were many [examples](https://github.com/jsoxford/RTHackPack) provided for us (one which I had added) using a range of real time technologies such as [Pusher](https://pusher.com/), [PubNub](https://www.pubnub.com/) and [Socket.io](http://socket.io/).
After a brief intro a few pitched some ideas they had for the day and we joined into groups. I joined with [Max](https://twitter.com/omgmog) to work on some real time, web connected hardware using Arduino's etc. I had brought along my awesome [Spark Core](https://store.spark.io/?product=spark-core) which is a mini, cloud connected device. I hooked up a [DHT11](http://www.adafruit.com/products/386) temperature/humidity sensor which had a handy Spark Core library available.
{{> tweet id='586829181226524672' }}
The data from this sensor was then made available by registering events using the Spark cloud's `publish` function which I then listened for on a server and forwarded all messages to Pusher for use by others. I hooked up a simple web page that would display each of the messages as they came in and cobbled together a [D3](http://d3js.org/) graph that plotted the temperature in real time.
We quickly discovered that the DHT11 was *slow*. Very slow it seemed. I first thought it was a bug in my code but after putting the device in the fridge we started to see a change in the temperature output, but with something close to a 30 second delay. Hardly **real time**.
I switched to using a TMP36 temperature sensor, one lacking a handy library. It took me *WAY* too long to get this working as I needed to find the right equation to convert the output voltage to degrees Celsius. "*Hardware is hard!*"
But in the end I prevailed!!!
{{> tweet id='586913832121008129' }}
We gave demos at the end of the day. Mine was short and sweet but some of the others were pretty cool.
[Dan](https://twitter.com/danielthepope) made a cool game where you fight to set the background colour.
{{> tweet id='586901707285725185' }}
[Ben](https://twitter.com/benjaminbenben) and [Gil](https://twitter.com/LuRsT) made a cool way of displaying what people are tweeting about.
{{> tweet id='586926991020003328' }}
We also had a Countdown clone, modern minesweep, twitter walls with flamingos, various chat rooms and an interactive [Ben's mind palace](http://www.bensmindpalace.co.uk/rt/).
When all was done and we had wrapped up a few of us headed over to Joe Perks for some well deserved (custom) cocktails.
{{> tweet id='586955640024788992' }}
This did end up turning into a Ruby hack...
{{> tweet id='586994376812933121' }}
All-in-all it was a great day! I learned not to muck around with hardware at a JS hack day (although that may change now I've received my [Espruino Pico](http://www.espruino.com/Pico)) and there are some pretty quick and powerful things you can do with technologies like Pusher.
@@ -0,0 +1,69 @@
---
layout: post.html
title: "Nodebots 2015"
date: 2015-07-14
tags: JavaScript JSOxford Nodebots HackDay
summary: "Last Saturday was [International NodeBots Day](https://github.com/nodebots/nodebotsday) and [JS Oxford](http://JSOxford.com) was yet again hosting a hack day."
---
Last Saturday was [International NodeBots Day](https://github.com/nodebots/nodebotsday) and [JS Oxford](http://JSOxford.com) was yet again hosting a hack day. We had a really good turn out with about 30 very enthusiastic people showing up. I belive this was, in no small part, due to the fact we had [Gordon Williams](https://twitter.com/espruino), the man behind the [Espruino](http://www.espruino.com/), on hand to help us build some awesome little robot cars made possible by the Expruino Pico and a PCB made especially for the day.
{{> tweet id='624872341206642688' }}
I have been looking forward to this event for a long time now. [Last year's NodeBot event](https://blog.marcusnoble.co.uk/2014-08-24-nodebots/) was one of the main reasons I fell in love with JS Oxford. It was lots of fun and really spurred my interest in microcontrollers (Arduinos, Espruinos, etc.). I may have been a little too keen...
{{> tweet id='624607801277427712' }}
The day started out with an intro from Ryan and Gordon talking about the pre-designed project that was available (or we could make whatever we wanted) and a brief overview of the many components laid out in front of us.
{{> picture alt="So many components to play with." caption="So many components to play with." url="/images/Nodebots2015Components.jpg" }}
I started off working with [Tom](https://twitter.com/neverontarget) and [Katy](https://twitter.com/katyemoe) following the [instructions](https://github.com/espruino/EspruinoDocs/blob/master/devices/RobotPCB.md) to build a small Espruino Pico car that can be built upon. After shorting out our first board we eventually had something that worked...
{{> tweet id='624913165864857600' }}
At this point I decided I wanted to have a go with some of the kit I had brought, specifically one of [these](http://www.ebay.co.uk/itm/Hot-sale-Motor-Smart-Robot-Car-Chassis-Speed-Encoder-Battery-Box-For-Arduino-DDS/391198705451) I got off eBay. The main difference between this and the small cars we'd been building was the motors on this were considerably hungrier, requiring ~5v to work rather than 3.3v. This posed a problem as the pins on the controllable Espruino Pico all output at 3.3v with the only way to get 5v is the use the constant 5v out pin. After some (repeated) chatting with Gordon I was advised to make use of a transistor to have a controllable switch for the 5v power. I used two of these to have individually controllable wheels and threw together some code that would have the car randomly drive around.
<pre><code class="javascript">
var movement = [0, 0];
var timeout;
setWatch(function(){
if(timeout){
clearInterval(timeout);
timeout = undefined;
movement = [0,0];
digitalWrite(A6, movement[0]);
digitalWrite(A7, movement[1]);
digitalWrite(B3, 0);
}else{
go();
}
}, BTN, {repeat:1, edge:'rising'});
function getRand() {
return Math.floor(Math.random() * (1 - 0 + 1)) + 0;
}
function go(){
digitalWrite(B3, 1);
timeout = setInterval(function(){
movement = [getRand(), getRand()];
digitalWrite(A6, movement[0]);
digitalWrite(A7, movement[1]);
},1000);
}
</code></pre>
The end result is quite entertaining...
{{> tweet id='624950235509456896' }}
All in all it was a very successfull event with many [awesome](https://twitter.com/danielthepope/status/624969982934392832) [robots](https://twitter.com/roylinesuk/status/625029819550564352) being built.
{{> tweet id='624964081691324416' }}
With the day over, a robot uprising in effect and a lot of tired people it was time for some celebratory food and drinks at [Joe Perks](http://joeperksandco.co.uk/).
{{> picture url="/images/NodebotsJoePerks.jpg" }}
@@ -0,0 +1,45 @@
---
layout: post.html
title: "Elevated Intelligence Watch"
date: 2015-09-05
tags: Android SmartWatch
summary: "A few weeks ago I bought an [Asus ZenWatch](https://www.asus.com/Phone-Accessory/ASUS_ZenWatch_WI500Q/) as the price [recently dropped](https://store.google.com/product/asus_zenwatch) (due to the upcoming release of the [ASUS ZenWatch 2](https://www.asus.com/ZenWatch/ASUS_ZenWatch_2_WI501Q/)). My experiences with it so far have been..."
---
A few weeks ago I bought an [Asus ZenWatch](https://www.asus.com/Phone-Accessory/ASUS_ZenWatch_WI500Q/) as the price [recently dropped](https://store.google.com/product/asus_zenwatch) (due to the upcoming release of the [ASUS ZenWatch 2](https://www.asus.com/ZenWatch/ASUS_ZenWatch_2_WI501Q/)). My experiences with it so far have been...
...somewhat underwhelming.
I had purchased the device under the guise of assisting me with my aim to lose some weight. There are many Android Wear apps that allow you to track the number of steps you make throughout the day and encourage you to achieve the [recommended 10K steps](http://www.nhs.uk/Livewell/loseweight/Pages/10000stepschallenge.aspx). The problem is, all these apps seem to have a different way of calculating how many steps you have taken.
{{> picture alt="Google Fit" caption="Google Fit" url="/images/Fit.png" class="inline" }}
{{> picture alt="Watch Face" caption="Watch Face" url="/images/Clock.png" class="inline" }}
{{> picture alt="Asus Wellness" caption="Asus Wellness" url="/images/Wellness.png" class="inline" }}
{{> picture alt="Up by Jawbone" caption="Up by Jawbone" url="/images/Up.png" class="inline" }}
*Ugh!*
This obviously destroys any confidence I have that my steps are actually being tracked. So I decided to stick with a single app (Google Fit) and ignore the rest. My reason for picking [Google Fit](https://fit.google.com/) is it has a nice feature that can detect different types of activities, so I can track how many calories I burn playing tennis as well as track my daily number of steps. It is also quite nice to have it all backed up to my Google account, giving me a nice interface to look back over different activities I've taken part in. There is also an [API](https://developers.google.com/fit/rest/) so I can make sure of that data at a later date if I so choose.
{{> picture alt="Google Fit Activity Summary" caption="Google Fit Activity Summary" url="/images/GoogleFit.png" }}
The biggest impact I saw after I had used it a few days was on my battery. It requires bluetooth enabled on your phone constantly and this can be a massive battery drain (depending on your phone). Luckily my [OnePlus One](https://oneplus.net/one) had a pretty amazing battery life to start with so I still get a full day's use out of it (*just*) but others may not be so lucky.
## It's not all doom and gloom
One thing it does, and does *really well*, is give you the ability to be indecisive about what watch you want. When I first got it I found myself changing the watch face daily (sometimes several times throughout the day). As the watch itself looks pretty smart and "ordinary" it fits in with most situations (casual and formal). Having the ability the easily change to a suitable (or [not suitable](https://play.google.com/store/apps/details?id=pl.tajchert.swear&hl=en)) watch face to match the situation is really, really nice.
{{> picture alt="Stickers" url="/images/Stickers.png" class="inline" }}
{{> picture alt="Pixels" url="/images/Pixels.png" class="inline" }}
{{> picture alt="Mechanism" url="/images/Mechanism.png" class="inline" }}
{{> picture alt="Mr Robot" url="/images/MrRobot.png" class="inline" }}
It does also have pretty good voice commands which allow you to do pretty much anything (send a text, search, add a note, send a WhatsApp message) just by talking to your watch. I did occasionally find myself in a situation where both my watch and phone started listening when I spoke those [magic words](https://support.google.com/websearch/answer/2940021?hl=en) so I recommend turning off the "listen from any screen" option on the phone. While it is a really cool and powerful feature I have yet to overcome the social awkwardness of talking to my watch so have only used it while driving or at home. I'm sure we'll soon all be [speaking to our computers](https://vimeo.com/121710266), but for now, its a bit weird.
But so far, my primary use of it has been to check on notifications without having to pull out my phone. Simple, but really useful. Not quite sure its £160 worth of usefulness though.
## Closing thought...
One thing I hadn't anticipated before I got my new watch, and something I hadn't seen anyone mention, is that your shiny new watch... could become unresponsive and not be able to tell you what time it is...
{{> picture alt="Time has become unresponsive" caption="Time has become unresponsive" url="/images/NotResponding.png" }}
@@ -0,0 +1,51 @@
---
layout: post.html
title: "Hackathonarama"
date: 2015-10-06
tags: hackathon
summary: "This past weekend I attended my first proper 2-day hackathon. While I didn't manage to build anything awesome I did learn *a lot* about some new technologies and how to approach hackathons, especially when it's your first time."
---
Last weekend I attended my first real 2-day hackathon - [Hackference](http://2015.hackference.co.uk/) (a 1 day conference and 2 day hackathon) - not really knowing what I was doing going in. Unfortunately I didn't manage to build anything cool (more on that below) but I did learn a lot about hackathons and about my fellow developers. Here are my tips for going to your first hackathon (in no relevant order):
### 1. Drink water
This should really be a tip for life but I really made this mistake on the first day. I went equipped with cans of Relentless, bottles of Mt. Dew and other things that will do crazy things to my body. While these do give you a short-term boost in energy, the come-down can be hard and will eventually leave you dehydrated. A better tactic is to drink water for the most part and if you find yourself in need of a boost then have an energy drink sparingly. This will also help prevent the headaches kicking in on the second day.
### 2. Bring a friend (or make friends there)
I went along alone, which usually is fine (I love my own company) but, being the anti-social type that I am, I ended up working alone rather than in a team. This was by far my biggest mistake. I totally missed out on a great learning experience by working with others. If you can't get anyone to go along with you, find someone there that is working on something that interests you and ask to join them.
### 3. Don't waste too much time on a technology/library/API/etc.
On the first day you will likely have sponsors telling you about some of their resources that you can make use of. This is really cool as if you get stuck with any of them you can go right to the source for help. But the idea of the event is to build and learn. If you end up sinking many of the crucial hours trying to get a library to work you aren't going to get a project finished. This is where I went wrong. I tried to make use a Microsoft's [Face API](https://gallery.cortanaanalytics.com/MachineLearningAPI/b0b2598aa46c4f44a08af8891e415cc7) but unfortunately is was having problems.
{{> picture alt="No Face API for you :cry:" caption="No Face API for you :cry:" url="/images/AzurePortalError.png" }}
The two there from Microsoft, [Martin](https://twitter.com/MartinKearn) & [Martin](https://twitter.com/thebeebs/), did manage to generate me an API key on the second day but by then it was too late for me to do anything useful with it.
I then moved on to trying out [Twilio's](https://www.twilio.com/) WebRTC Video. I was going to build a super-simple video chat room between two people. The code required to get it set up is incredibly simple, Twilio have done a great job with the SDK. I loaded it up on my laptop, then on my Android phone. The laptop worked great... the phone didn't work. *Ugh!* After some researching I eventually came across a [blog post](http://www.broken-links.com/2010/07/08/making-html5-video-work-on-android-phones/) by [Peter Gasston](http://www.broken-links.com/2010/07/08/making-html5-video-work-on-android-phones/) that explains a problem on Android where the `.play()` method needs to be manually called to get mp4 videos to play. After adding this to my code (at the end of the second day) all worked perfectly. Throughout all this I was talking with [Rob](https://twitter.com/dN0t) from Twilio about it (who contacted the lead engineer about the issue) so when I had a solution I made sure he was aware so it could be baked back into the Twilio SDK. *How cool is that!?*
### 4. Have some ideas ready
I went in with nothing. No clue what I was going to do. Even after seeing the sponsors demo's I was still lacking a bright idea. If you can, look up the sponsors in advance, look at what they have on offer and come up with 2-3 ideas of how you could use their resources for something cool. Even if you don't act on them you wont be in the position I was of spending the first hour or two just trying to come up with an idea of what to do.
### 5. Talk to people
This was the best thing I did. I went round and talked to people, asked them what they were doing, how they were doing it, etc. The whole point of being there is to learn so don't waste the most useful resource available - other peoples experiences.
### 6. Sleep
I had planned to power through the night, which I did do, but I got to a point where I wasn't able to think of anything new or do anything creative. My brain was just too tired. I had tried to get some rest (take a sleeping bag and pillow) but it was too noisy at the time. With hindsight, some ear plugs would have been **very** useful.
### 7. Make sure you know how to use a projector
{{> picture url="/images/HackathonProjector.jpg" caption="How many developers does it take to hook up a projector?" alt="How many developers does it take to hook up a projector?" }}
### 8. HAVE FUN!
This should be your main focus. If you're not having fun, why are you even there? Don't beat yourself up if you fail to produce something but be happy with trying and learning new things.
---
I want to say thank you to everyone involved in organising and running Hackference and for everyone who attended and made me feel so welcome. I also want to thank [Pusher](http://www.pusher.com/) as I won the ticket to go from them (I :heart: Pusher). A big shout out to [Rob Spectre](https://twitter.com/dN0t) for his *AMAZING* [keynote talk](https://github.com/RobSpectre/Talks/tree/master/With%20Great%20Power) (Developers are superheroes? You bet they are!) and for continuously putting up with me finding problems with the Twilio WebRTC SDK and trying to help me overcome them, and for just being a cool guy who likes comics!
@@ -0,0 +1,90 @@
---
layout: post.html
title: "Looking after your butler"
date: 2015-10-28
tags: ContinuousIntegration Jenkins
summary: "In my team we have a handy-helpful butler that handles all our builds and deploys. I spent all last week getting to know him. Here is what I found out."
---
In my team at work we have a butler.
{{> picture alt="Picture of Geoffrey from The Fresh Prince of Bel-Air" url="/images/butler.jpg" }}
OK, so it's not [Geoffrey](http://www.imdb.com/name/nm0545186/) from [The Fresh Prince of Bel-Air](http://www.imdb.com/title/tt0098800/), it's more like this...
{{> picture alt="Jenkins Logo" url="/images/Jenkins.jpg" caption="Jenkins CI!" }}
For those unaware, [Jenkins](https://jenkins-ci.org/) is a [Continuous Integration](https://en.wikipedia.org/wiki/Continuous_integration) server that is used to automate repetitive tasks such as building your projects and running tests, among [many, many other things](https://wiki.jenkins-ci.org/display/JENKINS/Plugins). We use Jenkins to:
1. build all our code committed in and on the Master branch
2. package for various platforms (Dev, Prod, Mobile, Etc.)
3. run all tests (unit, integration and acceptance)
4. deploy to our development server.
We have been using Jenkins for roughly three years now since it was set up by a contractor we had for a while. In that time the amount of jobs we run on it has grown considerably as has the variety of tasks we now want it to perform. Since the contractor left we haven't had anyone in the team that really knows the ins-and-outs of Jenkins and it went many months (years?) without any updates being performed on it for fear of it breaking.
**Well that stops now!!!**
I spent all of last week cleaning, updating, improving, rationalising and making sense of every aspect of our Jenkins setup. Here's what I learned:
## Backups
As with any major upgrade or change you should always have appropriate backups should anything go awry. We didn't have this in place for Jenkins (short of the VM backup) so before I did anything I first made backups. The great thing about Jenkins is it's plugin ecosystem. Here you will likely find a plugin to do anything you need. Backups are no different, the very useful [thinBackup](https://wiki.jenkins-ci.org/display/JENKINS/thinBackup) allows you to backups all configurations (global system configs and individual job configs) and restore them later if needed. thingBackup also has the ability to make regular backups so we no longer need to worry about out setup getting ruined.
Before I performed the upgrade I wanted to clean up as much as I could to make things easier. We still had a *lot* of old jobs from our pre-Git days (Ugh &ndash; SVN) that were disabled but still hanging around. Being the overly-cautious type that we are we decided to back these up as well but have them removed from Jenkins. The super-handy [Shelve Project Plugin](https://wiki.jenkins-ci.org/display/JENKINS/Shelve+Project+Plugin) allows you to 'shelve' individual Jenkins jobs. This simply creates a zip of the job folder and moves it to another folder in the Jenkins directory. With that you still have the job in tact but it no longer clutters your job lists.
Another invaluable plugin I have installed it the [JobConfigHistory Plugin](https://wiki.jenkins-ci.org/display/JENKINS/JobConfigHistory+Plugin) that keeps a diff of configurations each time they are changed. You have no idea how many times in the past I had changed a job thinking I was improving it, only to have the build fail and I couldn't remember what the old config was!
## Limit tasks
Overly complex jobs are prone to failing. The more tasks you add the less other people are going to understand what is going on. The key is to only include what is needed and nothing more. What this is exactly very much depends on the job you're creating but the main thing I realised was this:
> Perform all your unit tests and static analysis on your dev builds and leave your prod builds nice and sparse with the bare minimum needed to build the project.
We had previously had our prod builds as carbon-copies of our dev build with just the build profile different. This is wasteful! By the time the you build the prod profile you should have already built the dev profile, ran all the tests and generated static analysis reports so why bother going through all that again?
## Limit checkouts
In our team we use a 'Libraries' repo to house all our dependencies that aren't included using a dependency manager. All our Jenkins jobs checked out this repo so the libraries are available during build. This has one major downside &ndash; making any chances to the Libraries repo caused *all* our jobs to start building at the same time. We had three repos that contain shared resources of one kind or another that are needed by most jobs.
To solve this I set up new jobs whose solve purpose is to checkout these resources and "Archive the Artifacts" so they can then be used by other jobs rather than needing to check them our in each job. This also has the added benefit of reducing the amount of network traffic going between Jenkins and your repositories.
## Greedy Blocking
We have some jobs that will cause problems if they run at the same time as certain other jobs. For example, it's not very good if our deploy job runs while we are in the middle of running our integration tests. The [Build Blocker plugin](https://wiki.jenkins-ci.org/display/JENKINS/Build+Blocker+Plugin) allows you to stop jobs from running when a job matching a provided regex is already running. It will queue these jobs until all conflicting running jobs have finished. I decided it is better to be greedy with your blocking regex and restrict more than less (to prevent unreliable tests due to conflicts). For most of our deploys and tests we have something similar to `Dev_(Test|Deploy|Acceptance)_.*{PROJECT_NAME}.*`
## Re-use
Writing your jobs with reusable tasks will make them much easier to understand and quicker to set up. A lot of plugins have global configurations that can then be overridden on a per-job basis. I usually try to avoid this and configure things only once. A good example of this is with the [Email-ext Plugin](https://wiki.jenkins-ci.org/display/JENKINS/Email-ext+plugin) that allows you to construct templates for emails to be sent out upon certain events (such as a build failing)&#42;.
As well as reusing plugin configurations I also tend to have a "pattern" to how the jobs are structured. As we have multiple of the same type of jobs (ASP .NET and Java) I was able to put together some very prescriptive instructions that others can follow. With the help of the [EnvInject Plugin](https://wiki.jenkins-ci.org/display/JENKINS/EnvInject+Plugin) I was able to provide some snippets that can be copied and pasted, providing the appropriate variables are defined at the start of the job. This has also help me overcome one of my most common issues when creating jobs &ndash; *typos!*
&#42; *I'm not sure if it's a bug or me misunderstanding but this plugin doesn't seem to include the default recipients for all triggers defined globally. Make sure you check the 'Advanced' section when adding this plugin to a job.*
## Nodes
Don't restrict jobs to nodes unless absolutely necessary! If you have different platforms (Windows/Linux/Etc.) on your nodes then restrict to a label (E.g. "Windows") so that adding new nodes of that type mean you don't need to update jobs. Restricting to a specific machine can often be a sign that other machines aren't correctly set up and could be missing some application or configuration. The one exception we have for this is our job that backs up our GitHub wiki to the slave in our office in the event the network is lost (it happens) so we still have a way of accessing it.
There may be a time when a job needs to be run on *all* nodes when a repo is updated. In our case this was when we updated a configuration file that needed to be copied to a location on all Jenkins nodes. Rather than having a job that copies to network paths, which would need updating any time a new slave is added, you can make use of the 'Multi-configuration project' job type that allows you to create a matrix axis of properties to run. One of these 'axis' is the 'Slaves' axis that allows you to tell Jenkins to run the job on all (or a selection) of nodes at the same time.
## Triggers
I thought a lot about when and why are jobs were being triggered. We had previously been very inconsistent in this aspect in that some polled SCM every X minutes to see if there was changes, some just built every X minutes regardless of changes, some only built when other jobs completed and other were manual only. Looking at just a job name I had no idea when (or even if) it would be built after I had made changes to the code.
We generally have 5 different types of jobs for each project: A dev build, Dev deploy, Tests (Unit, Integration or Acceptance), Prod build and Prod deploy. These jobs seem to logically follow on from each other, you don't want to deploy before your project is built right?
Jenkins has a really nice feature that allows you to create workflows of jobs by creating "downstream" and "upstream" jobs. Basically, when configuring a job, you can set it's trigger to be the completion (successful or otherwise) of another job. I decided that only our dev build jobs should be checking for changes and then the deploy is triggered only if the build was successful. Tests only trigger if the deploy was successful (or new tests have been added to SCM) and the prod build is only triggered if al tests pass. This allows us to have more confidence when deploying to production as it will have only ever built a version that has passed all our tests.
# Summary
I learned a lot digging through our Jenkins setup. A lot about Jenkins and a lot about my team (past and present). It became obvious pretty early on that we hadn't really thought about what we really wanted Jenkins to do for us. We knew automation was a good thing, and continuous testing had made us *much* better developers, but there was a lot of "try it and see" to how we approached it.
The main thing that became apparent is that we need to really think about what a job *needs* to do when we're creating it and not just 'Copy existing job'. This has made managing our various jobs much more bearable and has increased our confidence in the system due to a reduction in false-negative results caused by poorly constructed jobs.
## Bonus!
Jenkins can be used for much more than just building and testing your code. After I started writing this post we saw some issues with some of our tests randomly failing. After a little investigation it became clear that the problem lied with out MSSQL server occasionally timing out. I decided it'd be useful to put together a little project that simply tries to connect one of the databases and run a simple `select *` query. I set this up as a Jenkins job that ran every 2 minutes and published the results as a JUnit-style report. From this Jenkins could produce a graph of the past results to help identify any patterns.
{{> picture alt="SQL Graph" url="/images/SQLGraph.png" caption="Not really a pattern yet..." }}
I'd love to hear from anyone else who is using Jenkins (or similar) in more creative and inventive ways &ndash; <a href="https://twitter.com/intent/tweet?screen_name=Marcus_Noble_" class="twitter-contact-link" data-related="Marcus_Noble_" data-dnt="true" target="_blank"><i class="icon-twitter"></i>Tweet @Marcus\_Noble_</a>
@@ -0,0 +1,103 @@
---
layout: post.html
title: "My Adventures in SVG-land"
date: 2015-11-30
tags: SVG Animation
summary: "A few months back a little [GitHub repo](https://github.com/benfoxall/logo-hacks) by [@benfoxall](https://github.com/benfoxall) caught my eye. Little did I know it'd lead me through a rabbit hole of fascination in SVG animation."
---
A few months back a little [GitHub repo](https://github.com/benfoxall/logo-hacks) by [@benfoxall](https://github.com/benfoxall) caught my eye. Little did I know it'd lead me through a rabbit hole of fascination in SVG animation.
I was amazed at how you could animate pictures using JavaScript. Pictures made up of markup! Previously pricey, bloated graphic applications had been needed to create such beautiful moving imagery. No more! Here is my story of SVG-land.
Slowly, over the next few months, I began learning more about SVGs. How to build them, how to tweak them, what can and can't be done and how to bring them to life.
My first experimentation in SVG animation was building upon the [JS Oxford](http://jsoxford.com/) logo Ben had already made and incorporating more into it. This became the [welcome](http://jsoxford.com/welcome.html) page to be used at meetups.
For a while I was satisfied with this. It worked, looked pretty cool and achieved what I intended.
But the itch to explore began to grow and I wanted to refine the process. As you can see from the [source](https://github.com/jsoxford/jsoxford.github.com/blob/develop/welcome.html) it wasn't the easiest thing to manage. I wanted to find out the best way to animate SVGs and what pros and cons of the various methods were.
There are really three ways to animate SVGs:
1) JavaScript
2) CSS
3) SMIL ([Going to be deprecated](https://groups.google.com/a/chromium.org/forum/#!msg/blink-dev/5o0yiO440LM/59rZqirUQNwJ))
I had used JavaScript previously but it wasn't a controlled animation, it was just randomly showing elements. I set out to create an animation that went from the "new" JS Oxford logo to the "classic" logo, to the meetup members photos and finally the standard horse.
{{> picture alt="Logo transitions" caption="The many faces of JS Oxford" url="/images/JSOxLogos.png" }}
## Attempt #1 - JavaScript
As I had used it previously I started off using JavaScript again.
[Source](https://github.com/AverageMarcus/AdventuresInSVG/blob/gh-pages/AnimatingWithJavaScript.html)
<iframe src="https://averagemarcus.github.io/AdventuresInSVG/AnimatingWithJavaScript.html" style="width:100%;border:0;height:600px">Live: https://averagemarcus.github.io/AdventuresInSVG/AnimatingWithJavaScript.html</iframe>
**Pros:**
* Easy to change the routine of the animation. If I wanted the horse animation first, just change it's position in the array.
* Timings can be dynamically changed. As you can see above I've provided links to speed up/slow down the animation.
**Cons:**
* Still needs a bit of CSS (classes) to keep it simple
* May not perform too well on low-end devices or if the amount of work done in JS is large
## Attempt #2 - CSS
CSS3 has animations as part of the language. I mainly relied upon using [keyframes](https://developer.mozilla.org/en-US/docs/Web/CSS/%40keyframes) and [transforms](https://developer.mozilla.org/en-US/docs/Web/CSS/transform).
The major issue I experienced was with the [animation-delay](https://developer.mozilla.org/en-US/docs/Web/CSS/animation-delay) property. As the name suggests, this property lets you specify a delay for the animation. My intention was to delay the pending logo transitions so that they'd start after the previous has finished. This worked great at first, but when the animation repeated the delay was ignored and everything started happening at once. As it stands this ability is currently lacking from the CSS spec. Some Googling suggested that I need to make clever use of the keyframes so that, for example, the first logo uses 0-25%, the second uses 25-50%, and so on.
This worked, but it isn't nice. If I decide I want to add or remove a transition I will need to work out and update *all* the keyframe timings.
[Source](https://github.com/AverageMarcus/AdventuresInSVG/blob/gh-pages/AnimatingWithCSS.html)
<iframe src="https://averagemarcus.github.io/AdventuresInSVG/AnimatingWithCSS.html" style="width:100%;border:0;height:600px">Live: https://averagemarcus.github.io/AdventuresInSVG/AnimatingWithCSS.html</iframe>
**Pros:**
* Very few lines of code needed to animate something.
* Animations are smooth
**Cons:**
* Timings are difficult and not flexible (as mentioned above).
* Timings are fixed and cannot be altered on-the-fly
## Attempt #3 - Promises (JS)
Not satisfied with my approach using JavaScript I decided to re-visit it and try to tackle it using promises.
Before this I hadn't *really* used promises (other than a little in NodeJS) and I knew the browser support was lacking so I checked out some of the libraries that provide promises.
I settled on [Q](https://github.com/kriskowal/q), mainly for its built-in `delay` function that would be super useful with timing animations.
[Source](https://github.com/AverageMarcus/AdventuresInSVG/blob/gh-pages/AnimatingWithPromises.html)
<iframe src="https://averagemarcus.github.io/AdventuresInSVG/AnimatingWithPromises.html" style="width:100%;border:0;height:600px">Live: https://averagemarcus.github.io/AdventuresInSVG/AnimatingWithPromises.html</iframe>
**Pros:**
* All the same as the JS ones above
* By making use of the `delay` function timing between animations can easily be varied.
* Nice, easy to read syntax
**Cons:**
* Much more code as a rather large library was used.
# Closing Thoughts
I'm still experimenting and learning. I'm sure that there are better ways to create more complex animations (if anyone knows, please let me know).
When working with SVGs on the web I urge you to make use of [SVGOMG](https://jakearchibald.github.io/svgomg/) to remove bloat and cleanup your SVGs.
Also, SVGs manipulation by JS/CSS may or may not be allowed depending on the way you include the SVG on the page. I recommend checking out [SVG On The Web](https://svgontheweb.com) to learn more.
One thing I did discover while on this journey is that Firefox (at time of writing) doesn't handle the `transform-origin` property, causing things to incorrectly fly around the logo in comical fashion.