Making a splash with Meteor

Apr
2013
18

Javascript, Meteor, Node

No comments

Stradjectives for Public Art League is a Meteor based web app we’ve developed at 2wav . Before starting, we knew that we wanted to create a modern HTML5+CSS3 app, prioritizing a dynamic behavior. Initially we started looking for open source projects that would allow us to create a Nodejs+Mongoose+Backbone+SocketIO solution in the short amount of time we had before the launch. Such alternatives do exist, but unfortunately none that were mature enough to pin our hopes and dreams on them. We did delve into Derbyjs initially, but sadly it proved too much for us to handle.

Then, along came Meteor. It provided everything we wanted, it has clear documentation, and where the documentation is not there, there’s a big community around it to help out. If you are looking to quickly develop a dynamic web app that “just works” and is fully data reactive, my advice is that you do give Meteor a chance.

Of course, when it came down to the implementation, some issues did come up. Wrangling the reactiveness of the app so you don’t get whole page re-renders for a minor change in the database is not trivial and it takes some time to figure out how it all works and what’s the best balance between reactiveness and performance/usability (this is a subject for another post, but let’s just say that you’ll eventually arrive at the conclusion that client side animations and data reactiveness are fundamentally at odds with each other).

Another problem we encountered with Meteor is that it’s very easy to add dependencies and before you notice it, the app gets fairly big, even with minification, gzipping, etc. This, added to the big images the app uses, required that we showed some sort of splash screen quickly upon access to prevent the user from thinking the site wasn’t responsive.

Meteor (at least as of version 0.6.2) has no functionality to do this, and no easy way to hack into its build system. What we ended up doing is a dirty hack, but one that should tide you over if you find yourself in the same spot, at least until an equivalent feature is properly implemented.

What we did is a monkey patch of sorts. Meteor fetches all the app dependencies and makes an “app.html” file that acts as the main file for your app (it loads all the CSS and Javascript). On our server code, we open that file, modify it to embed a base64 encoded image, and insert it “manually” in the document’s body (the document body doesn’t even exist at this point, so we create that too). The image is base64 encoded right into the html to make sure it’ll be shown before all the Javascript files are loaded. Otherwise, depending on the browser, the splash image can potentially be loaded after other files, thus taking longer to show up, defeating the purpose of having something show up right away.

The code is as follows:

// Paste this at the end of server.js
// This creates a base64 encoded version of splash.png
// The reason behind this is to make sure the splash image shown *before* the rest of the CSS+JS starts to load
// Otherwise the request for splash.png gets queued with the rest of the files and it takes a good while to show up

var require = __meteor_bootstrap__.require ? __meteor_bootstrap__.require : Npm.require;
var fs = require('fs');
var path = require('path');

var splash_path = path.join(path.resolve("."), "public/images/splash.png")
var splash_img = fs.readFileSync(splash_path);

var splash_base64 = new Buffer(splash_img).toString('base64');

// Hacks are not over yet, here we patch app.html to include this splash before anything else.

var app_html_path = path.join(path.resolve("."), ".meteor/local/build/app.html")
var app_html = fs.readFileSync(app_html_path, 'utf8');
app_html = app_html.replace("<body>", "");
app_html = app_html.replace("</body>", "");
app_html = app_html.replace(
    "// ##RUNTIME_CONFIG##",
    // Insert the splash image
    "var _body = document.createElement('body');\n" +
    "_body.innerHTML = '<div class=\"page loading\"><div id=\"splash-content\"><img id=\"splash-image\" src=\"data:image/png;base64,"+splash_base64+"\" /><div id=\"splash-loader\"></div></div></div>';\n" +
    "document.documentElement.appendChild(_body);\n" +
    "__meteor_runtime_config__ = " + JSON.stringify(__meteor_runtime_config__) + ";");

fs.writeFileSync(app_html_path, app_html);

There’s one minor side effect to this hack. When all is said and done, you end up with two body elements in your HTML. This didn’t prove to be a problem, we just remove the second one with:

$('body').eq(1).remove();