diff --git a/images/dirt.gif b/images/dirt.gif
new file mode 100644
index 0000000..c71f2fe
Binary files /dev/null and b/images/dirt.gif differ
diff --git a/images/hud.bmp b/images/hud.bmp
new file mode 100644
index 0000000..396ddb9
Binary files /dev/null and b/images/hud.bmp differ
diff --git a/index.html b/index.html
new file mode 100644
index 0000000..ae2f46b
--- /dev/null
+++ b/index.html
@@ -0,0 +1,41 @@
+
+
+
+ LaserTank.js
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/lib/FileLoader.js b/lib/FileLoader.js
new file mode 100644
index 0000000..2c47119
--- /dev/null
+++ b/lib/FileLoader.js
@@ -0,0 +1,120 @@
+/**
+ * Loads a file from disk and returns a binary string
+ *
+ * Copyright (C) 2012 Mike Gerwitz
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ *
+ *
+ * The file uses a number of features introduced in the HTML5 and ECMAScript
+ * specs, as it depends on FileReader.
+ */
+
+
+/**
+ * Load file into memory as binary string from a given file element
+ *
+ * This class handles the loading of only a single file (as that is all this
+ * project requires).
+ */
+ltjs.FileLoader = Class( 'FileLoader',
+{
+ /**
+ * DOM file element to watch
+ * @type {HTMLInputElement}
+ */
+ 'private _element': null,
+
+ /**
+ * Callback to call on file load
+ * @type {function(Error,string)}
+ */
+ 'private _callback': null,
+
+
+ /**
+ * Initialize file loader, monitoring the given file element
+ *
+ * @param {HtmlInputElement} element file element to monitor
+ */
+ __construct: function( element )
+ {
+ if ( element.type !== 'file' )
+ {
+ throw TypeError( 'Expected file element, given ' + element.type );
+ }
+
+ this._element = element;
+ },
+
+
+ /**
+ * Bind callback to file load event
+ *
+ * The given callback will be called when the file is loaded. It must accept
+ * two arguments: the first will be used to indicate an error (otherwise
+ * null) and the second argument will contain the binary string of data
+ * (unless an error occurred, in which case it will be undefined).
+ *
+ * @param {function(Error,string)} callback file load callback
+ *
+ * @return {FileLoader} self
+ */
+ 'public onLoad': function( callback )
+ {
+ this._callback = callback;
+
+ // we do not want to monitor the change event until *after* a callback
+ // is registered (it is otherwise pointless)
+ this._element.addEventListener( 'change', this._loadFile.bind( this ) );
+
+ // attempt to load file immediately (in case a file has already been
+ // selected)
+ this._loadFile( { target: this._element } );
+
+ return this;
+ },
+
+
+ /**
+ * Process file and invokes callback with result
+ *
+ * Called on filename change.
+ *
+ * @param {Object} event change event
+ *
+ * @return {undefined}
+ */
+ 'private _loadFile': function( event )
+ {
+ var _self = this,
+ files = event.target.files;
+
+ if ( files.length === 0 ) return;
+
+ var reader = new FileReader();
+ reader.onload = function( revent )
+ {
+ _self._callback.call( this.__inst, null, revent.target.result );
+ };
+ reader.onerror = function( e )
+ {
+ _self._callback.call( this.__inst, e );
+ };
+
+ // load file
+ reader.readAsBinaryString( files[ 0 ] );
+ }
+} );
+
diff --git a/scripts/game.js b/scripts/game.js
new file mode 100644
index 0000000..fb6458d
--- /dev/null
+++ b/scripts/game.js
@@ -0,0 +1,69 @@
+/**
+ * Game initialization script
+ *
+ * Copyright (C) 2012 Mike Gerwitz
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+( function()
+{
+
+var load_ltg = ltjs.FileLoader( document.getElementById( 'ltg' ) ),
+ load_lvl = ltjs.FileLoader( document.getElementById( 'lvl' ) ),
+
+ ctx = document.getElementById( 'render' ).getContext( '2d' ),
+
+ ltg_loader = ltjs.LtgLoader(),
+ masker = ltjs.TileMasker( ltjs.ClassicTileDfn() ),
+ tile_set = null,
+ map_set = null,
+ render = null;
+
+load_ltg.onLoad( function( e, data )
+{
+ if ( e ) throw e;
+
+ // get tile metadata
+ var meta = ltg_loader.fromString( data );
+
+ masker.getMaskedTiles( meta.tiles, meta.mask, function( tdata )
+ {
+ tile_set = tdata;
+ gamechk();
+ } );
+} );
+
+load_lvl.onLoad( function( e, data )
+{
+ if ( e ) throw e;
+
+ map_set = ltjs.MapSet( data, ltjs.ClassicMap );
+
+ gamechk();
+} );
+
+function gamechk()
+{
+ if ( !( tile_set && map_set ) ) return;
+
+ // clear any existing locks before rendering (allowing the user to change
+ // tile sets and map sets)
+ render && render.freeCanvas();
+
+ render = ltjs.MapRender( ctx, tile_set );
+ render.render( map_set.getMapByNumber( 1 ) );
+}
+
+} )();
diff --git a/style.css b/style.css
new file mode 100644
index 0000000..c361a91
--- /dev/null
+++ b/style.css
@@ -0,0 +1,36 @@
+/** stylesheet for lasertank-js **/
+
+body {
+ background-image: url('images/dirt.gif');
+}
+
+.game {
+ position: relative;
+
+ margin: 0px auto;
+ width: 696px;
+}
+
+/* 16x16 board */
+.game canvas {
+ width: 512px;
+ height: 512px;
+ border: 2px inset gray;
+}
+
+.game .hud {
+ position: absolute;
+
+ background-image: url('images/hud.bmp');
+ background-repeat: no-repeat;
+
+ width: 180px;
+ height: 245px;
+ right: 0px;
+ top: 0px;
+}
+
+/** temporary during development **/
+.load {
+ background-color: rgba( 255, 255, 255, 0.5 );
+}