Publishing your C.V. on the web and exporting PDF with added Gulp and Bootstrap

When I wrote my first C.V. coming out of university I used LaTeX since I had just written my thesis using it. You can create beautiful and professional documents with it but today I think things have moved on and your web presence is likely to be discovered and read first. So I decided to write it web first this time as a responsive web page. You can see an example of it here.

Initially I looked at both markdown-resume and strapdownjs and choose the latter to get the benefits of writing in markdown and omitting the build step.  To get up and running quickly I started with bootstrap-sass and the minimal and typographic paper theme. However the more I customised the layout and formatting of my content the more markdown was replaced with HTML and CSS. There came a point where I realised it was easier and more powerful to complete it all without the markdown component.

You will need:

bower install bootstrap-sass-official
bower install gulp gulp-clean gulp-load-plugins gulp-rename gulp-sass gulp-shell

npm install -g http-server

A working installation of wkhtmltopdf and invoker. A github account and a custom domain.

Write your C.V. in HTML / CSS

I'll explain the details of build and sass here and leave the markup and styling to you. Since we are using the bootstrap-sass plugin we will need to setup gulp to build our changes.

var gulp = require('gulp'),
	gulpLoadPlugins = require('gulp-load-plugins'),
	plugins = gulpLoadPlugins();

var files = {
    mainStylesheet : 'stylesheets/main.scss',
    pdfStylesheet : 'stylesheets/pdf.scss',
    stylesheets : 'stylesheets/*.scss'
};

gulp.task('clean',  function(){
	return gulp.src('css')
		.pipe(plugins.clean())
})

gulp.task('sass', ['clean'], function(){
	return gulp.src(files.mainStylesheet)
		.pipe(plugins.sass())                
		.pipe(gulp.dest('css'));
});

I really like how fast gulp executes and the model for composing the build pipeline. Calling gulp sass will generate our CSS file which we can use on the website. You will also need to import the bootstrap-sass and any theme scss files into your main.scss file.

@import 'paper-variables.scss';
@import '../bower_components/bootstrap-sass-official/assets/stylesheets/_bootstrap.scss';
@import 'paper-bootswatch.scss';

You can use my page as a template for writing your own markup / bootstrap layout: index.html.

If you want the CSS to automatically get regenerated on changes you can use gulps built in watcher.

gulp.task('watch', function(){
	gulp.watch(files.stylesheets, ['sass'])
})

Hopefully you should have a c.v. page that scales nicely across different devices and ready to exporting to PDF.

     

Export a PDF with a custom style

To export the page we will use wkhtmltopdf which I find works extremely well. Please make sure you have the latest version, my machine had v0.9 which was plagued with layout and text kerning issues. The current version at the time of this post is 0.12.2.

We can now setup a gulp task to call the wkhtmltopdf command.
gulp.task('pdf', ['pdfsass'], plugins.shell.task([
    'wkhtmltopdf --margin-left 15 --margin-right 15 --zoom 1.0 --viewport-size 1280x1024 http://naeemkhedarun.dev/index.html naeemkhedarun.pdf'
]));
You can adjust the margin and zoom for your own layout. The viewport-size is to generate the desktop responsive layout, so you can change this to suit your responsive media queries for the layout you want. We have added a dependency on a pdfsass task too so lets create that.
gulp.task('pdfsass', ['clean'], function(){
	return gulp.src(files.pdfStylesheet)
		.pipe(plugins.sass())
        .pipe(plugins.rename('main.css'))
		.pipe(gulp.dest('css'))
})
This will use my pdf.scss stylesheet to generate the CSS file for the page and is called before the generation happens. This will allow use to remove elements like the navigation bar and adjust the layout to better suit the PDF.
@import 'main.scss';

body {
    padding-top: 20px;
}

.navbar {
    display: none;   
}

Mine is pretty straightforward and I only adjust the padding and navbar display, one of the benefits of keeping the page design simple and typographic.

Hosting on Github

Github gives you a User page which feels like an appropriate place to put your c.v. and its quite easy to get up and running. Github uses a convention for user pages so create a repositoty called username.github.io

If you want a custom domain put into the root of your sites directory a file called CNAME and put in the domain name without the protocol. In mine I have:

naeem.khedarun.co.uk

You can now push your repository up to github and within 30 minutes your page will be available at http://username.github.io. If you are using a custom domain add a CNAME record pointing your domain to username.github.io.

All done!

I personally find that this approach works well for me and I hope you might find it useful too. All the source is at naeemkhedarun.github.io and feel free to clone and use it as you like.

Skeletal animations in javascript with Spine and MelonJS

When we started our html5 game Peter Acorn we were optimistic but cautious about the level of performance we could get from the platform on mobile. We started off with simple spritesheets which looped through our animations frame by frame and were extremely fast to execute. I will assume you either already have a MelonJS game setup or bootstrap yourself with one of the MelonJS tutorials to get up and running.



This was fast to get setup initially with good performance but there are limitations:

  • Getting smooth transitions between animations is difficult.
  • Using an image editor and scripts to structure and export the spritesheets takes time.
One major limitation which eventually broke the camels back with related to resolution and frame rate. If you want to increase the resolution of the game, then your sprite sheet gets much larger. To compound the issue if you want a smoother or more complex animation then adding extra frames to the sprite sheet exasperates the issue the higher the resolution is. You can offset this by having a square sheet but by now we had already seen Spine and wanted to try it.

We were skeptical about the performance of the javascript runtime especially on mobile but these concerns were not warranted in the end. First we had to find a way to get it integrated into MelonJS. You can find an example of this on Github  and a big thanks to Aaron for adding this support to MelonJS! 

You will want to load integration after both the melon and spine runtimes:

<script type="text/javascript" src="lib/melonjs/melonJS-1.1.0.js"></script>
<script type="text/javascript" src="lib/melonjs-spine/spine.js"></script>
<script type="text/javascript" src="lib/melonjs-spine/melonjs-spine.js"></script>

I have already added my chopped assets to Spine and created a few animations. I noticed that I needed to key all the bones otherwise I would get strange behaviour in-game and the animations would be totally out of whack as a result of bad relative co-ordinate data. So on the first frame of each of your animations select all the bones and make sure their rotation, translation and scale is keyed in. Take care especially on mobile to keep the number of bones, images and frames to a minimum to improve performance.



When exporting lets create an atlas which efficiently puts all the components parts of your character onto a single image. 



Turn off rotation (which packs more efficiently) as the integration does not seem to support it, not a huge loss anyway. You can also adjust the scaling factor but if you choose to use this you will also need to adjust the animationScale parameter in the Spine.Entity settings. It does not map directly to the scale factor you plug into here so some experimentation is needed.



You will get three files as a result of the export. The png contains the images for your character. The atlas is the mapping between the images and the skeleton and the json contains the animation data.

{name: 'peter', type:'json', src:'assets/atlas/peter/skeleton.json'},
{name: 'peter_atlas', type:'binary', src:'assets/atlas/peter/skeleton.atlas'},
{name: 'peter_atlas', type:'image', src:'assets/atlas/peter/skeleton.png'},

The integration allows you to create spine entities which will use skeletal animation instead of a sprite sheet. So you can use this base class for your animated entities.

var player = me.Spine.Entity.extend({
    
    init: function(x, y, settings) {
                   
        var settings = {
            atlas: 'peter_atlas',
            imagePath: 'peter_atlas',
            spineData: 'peter',
            name: 'PlayerEntity',
            imageScale: 1,
            animationScale: 1
        };
       
        this._super(me.Spine.Entity, "init", [x, y, settings]);

Still within the init method you can adjust how the integration maps your character into the game.

this.anchorPoint = new me.Vector2d(0, 0);
this.renderOffset = new me.Vector2d(0, 0);

The anchorPoint adjusts where the collision box is mapped related to your characters position and their width and height.

this.body.addShape(new me.Rect(-(this.width * this.anchorPoint.x), -(this.height * this.anchorPoint.y), this.width, this.height));

The renderOffset adjusts where the sprite is rendered in relation to the characters position.

context.translate(x + this.renderOffset.x, y + this.renderOffset.y);

Once these are set you can call this.updateColRectToAnchorPoint() to set the collision box on your character. We can now configure the animations on our character. My character Peter will start off with the running animation with 0 delay and we’ll set loop indefinitely to true. We can also set animation mixing using setMixByName which will tween between the animations when they are changed and makes the transitions appear really smooth. The time the runtime takes to smooth between the animations is configurable and I’ve set them to 100ms. The longer this value is the smoother it can look but you will need to experiment to find the best value for your animations.

this.state.setAnimationByName(0, "running", true);

this.stateData.setMixByName("running", "jumping", 0.1);
this.stateData.setMixByName("jumping", "running", 0.1);

You can switch animations by calling:

this.state.setAnimationByName(0, "jumping", true);

You can also set up a playlist of animations to run through using addAnimationByName. Once each animation is complete it will move onto the next one assuming you have queued one up. You will likely have one animation which will then loop indefinitely until a change in gameplay state forces a change. Using these animation queues you can create even smoother transitions than tweening alone.

this.state.setAnimationByName(0, "start-gliding", false);
this.state.addAnimationByName(0, "gliding", true, 0);

If you are taking advantage of spine slots which allow you to change the images associated with the bones then you will need to call setSlotsToSetupPost when changing animation.

this.skeleton.setSlotsToSetupPose();

Lastly if you want to get the name of the currently executing animation you can use:

var animation = this.state.tracks[0].animation.name

Despite using the runtime we were still able to reach 60 FPS on mobile devices as old as 3 years ago like my Galaxy Nexus which is hugely impressive. We managed this using the CocoonJS container and we are looking forward to talking about our experiences with this in the future.

The Spine software is a pleasure to work with and putting together animations is great fun. The large amount of integrations for many game engines and platforms together with the great performance makes it a fantastic package. Highly recommended!

Here are the animations in action although I do not doubt that an experienced animator would be able to do better:



Getting a faster sass-watch using grunt-sass

My sass --watch was getting increasingly slow especially with compiling bootstrap on every change. It was also using a substantial amount of battery life as a result.

After looking around it seems Sindre Sorhus has already solved the problem. The repository provides a native sass compiler which you can easily install using:

npm install --save-dev grunt-sass

I want to run grunt sasswatch to invoke the watcher separately to the other tasks to I created a new one. When it is called it will run the sass compile once and then invoke the watcher.

grunt.registerTask('sasswatch', [
  'sass',
  'watch:scss'
]);

The sass configuration calls my main scss which includes the many and heavy bootstrap files.

sass: {
    options: {
        sourceMap: true
    },
    dist: {
        files: {
            'app/css/main.css': 'app/main.scss'
        }
    }
}

Finally we can write the watcher which only listens for scss files in my application folder and the bootstrap one.

watch: {
  {
    files: ['stylesheets/*.scss', 'bootstrap-sass-official/assets/stylesheets/**.scss'],
    tasks: ['sass']
  }
}

Including the watch response time this solution is now taking around 1.6s compared with around 6.9s.

How we used the chrome dev tools to optimise our html5 game

We wanted to experiment and see whether we could build a simple cross platform game using the latest web tech. The idea was that it would run across the mobile platforms and be easy to develop using our existing skills and tools. So for some background we are using cocoonjs to hardware accelerate canvas drawing and melonjs which is an OSS game engine which is easy to run on cocoon.

Our initial attempt was running very smoothly at 60fps on our powerful desktop browsers, however I was getting half that on my Galaxy Nexus. Given how simple the game was we were concerned and looked to find out why. We are developing using Chrome Canary which has the latest developer tools within.

CPU Profile

This was the first place we looked to see what was happening.

image

The trace is telling us we spend a majority of our time rendering rather than executing game logic. The sprites we are drawing are basic and we’ve made them as small as possible so this was the first surprise.

image

The flame chart puts into perspective how much idle time we have on a desktop machine and that each frame is speedily rendered in approximately 5ms. Initially that sounded good but given the lack of complexity in our graphics this performance is extremely disappointing and is a good indicator as to why the a mobile device might be struggling.

It wasn’t enough to know what to fix yet and for that we used the Canvas Debugger.

Canvas Debugger

This is an experimental chrome feature which means you will need to manually enable it. I used the learningthreejs blog which has a good video explanation but if you prefer something more textual you can follow the guide at html5rocks.

With the ability to inspect each canvas call both at the API level and visually we could track down where we were losing the performance. Below is a gif animation of cycling through each draw call shown in the debugger:

before

And with that visual representation it has become quite obvious where the extra draw calls are coming from, the background gradient is a 1px wide repeating image! Ironically we chose to do it this way for performance thinking loading a smaller image would be lighter on resources.

We were able to easily fix this in the map editor and it resulted in a big reduction in draw calls.

after

The gif animation also highlights the draw call for each tile which is imported from the map editor and this could be a further avenue to investigate if we want to target even lower performance devices.

image

Each frame now takes between 2 and 3ms to complete and more importantly the draw portion of that has been greatly reduced. It only takes 1ms to render on the desktop and the game code is now visible in the profile.

image

These changes were not only enough to run the game at 60fps on our mobiles, but has allowed us to increase the animation and visual fidelity while keeping the frame rate smooth. If you are working with canvasses either for game development or visualisations like d3 I recommend you grab the latest chrome tools and give them a go.

@naeemkhedarun

Older Posts