diff --git a/mustache-renderer-webpack-plugin/index.js b/mustache-renderer-webpack-plugin/index.js
deleted file mode 100644
index 9e9df5faf..000000000
--- a/mustache-renderer-webpack-plugin/index.js
+++ /dev/null
@@ -1,50 +0,0 @@
-var defaults = require('lodash.defaults');
-var fs = require('fs');
-var mustache = require('mustache');
-
-var render = function (template, route, config) {
-    config = config || {};
-    // Route definition
-    defaults(route, config);
-
-    // Render template
-    return mustache.render(template, route);
-};
-
-function MustacheRendererPlugin (options) {
-    if (!options.templatePath) throw new Error('MustacheRendererPlugin requires a templatePath option');
-    // Read template
-    var template = fs.readFileSync(options.templatePath, 'utf8');
-    this.template = template;
-    this.routes = options.routes || {};
-    this.config = options.config || {};
-    return this;
-}
-
-MustacheRendererPlugin.prototype.apply = function (compiler) {
-    var template = this.template;
-    var config = this.config;
-    var routes = this.routes;
-
-    compiler.plugin('emit', function (compilation, callback) {
-        var outputRoutes = {};
-        routes.forEach(function (route) {
-            var filename = route.name + '.html';
-            var content = render(template, route, config);
-            outputRoutes[route.pattern] = filename;
-            compilation.assets[filename] = {
-                source: function () {return content;},
-                size: function () {return content.length;}
-            };
-        });
-        var routeJson = JSON.stringify(outputRoutes);
-        compilation.assets['routes.json'] = {
-            source: function () {return routeJson;},
-            size: function () {return routeJson.length;}
-        };
-        callback();
-    });
-};
-
-module.exports = MustacheRendererPlugin;
-
diff --git a/package.json b/package.json
index a9f0916aa..6951df6b2 100644
--- a/package.json
+++ b/package.json
@@ -26,7 +26,6 @@
     "express": "4.13.4",
     "express-http-proxy": "0.6.0",
     "lodash.defaults": "4.0.1",
-    "mustache": "2.2.1",
     "newrelic": "1.25.4",
     "raven": "0.10.0"
   },
@@ -53,6 +52,7 @@
     "git-bundle-sha": "0.0.2",
     "glob": "5.0.15",
     "google-libphonenumber": "1.0.21",
+    "html-webpack-plugin": "2.22.0",
     "iso-3166-2": "0.4.0",
     "json-loader": "0.5.2",
     "json2po-stream": "1.0.3",
diff --git a/src/template.html b/src/template.html
index 9f905045a..440e3de6e 100644
--- a/src/template.html
+++ b/src/template.html
@@ -6,25 +6,25 @@
         <meta charset="UTF-8" />
 
         <meta http-equiv="x-ua-compatible" content="ie=edge">
-        <meta name="viewport" content="width={{viewportWidth}}, initial-scale=1">
+        <meta name="viewport" content="width=<%= htmlWebpackPlugin.options.config.viewportWidth %>, initial-scale=1">
 
-        <title>Scratch - {{title}}</title>
+        <title>Scratch - <%= htmlWebpackPlugin.options.config.title %></title>
 
         <!-- Prevent mobile Safari from making phone numbers -->
         <meta name="format-detection" content="telephone=no">
 
         <!-- Search & Open Graph-->
-        <meta name="description" content="{{description}}" />
+        <meta name="description" content="<%= htmlWebpackPlugin.options.config.description %>" />
         <meta name="google-site-verification" content="m_3TAXDreGTFyoYnEmU9mcKB4Xtw5mw6yRkuJtXRKxM" />
 
         <meta property="og:url" content="https://scratch.mit.edu/" />
         <meta property="og:type" content="website" />
-        <meta property="og:title" content="Scratch - {{title}}" />
-        <meta property="og:description" content="{{description}}" />
-        <meta property="og:image" content="{{&og_image}}" />
-        <meta property="og:image:type" content="{{&og_image_type}}" />
-        <meta property="og:image:width" content="{{&og_image_width}}" />
-        <meta property="og:image:height" content="{{&og_image_height}}" />
+        <meta property="og:title" content="Scratch - <%= htmlWebpackPlugin.options.config.title %>" />
+        <meta property="og:description" content="<%= htmlWebpackPlugin.options.config.description %>" />
+        <meta property="og:image" content="<%- htmlWebpackPlugin.options.config.og_image %>" />
+        <meta property="og:image:type" content="<%- htmlWebpackPlugin.options.config.og_image_type %>" />
+        <meta property="og:image:width" content="<%- htmlWebpackPlugin.options.config.og_image_width %>" />
+        <meta property="og:image:height" content="<%- htmlWebpackPlugin.options.config.og_image_height %>" />
 
         <!-- Favicon & CSS normalize -->
         <link rel="shortcut icon" href="/favicon.ico" />
@@ -40,7 +40,7 @@
             m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
             })(window,document,'script','//www.google-analytics.com/analytics.js','ga');
 
-            ga('create', '{{&ga_tracker}}', {
+            ga('create', '<%- htmlWebpackPlugin.options.config.ga_tracker %>', {
                 'sampleRate': 10
             });
             ga('send', 'pageview');
@@ -51,11 +51,11 @@
         <div id="app"></div>
 
         <!-- Vendor & Initialize (Session & Localization)-->
-        <script src="/js/common.bundle.js"></script>
+        <script src="/<%= htmlWebpackPlugin.files.chunks.common.entry %>"></script>
 
         <!-- Scripts -->
-        <script src="/js/{{name}}.intl.js"></script>
-        <script src="/js/{{name}}.bundle.js"></script>
+        <script src="/js/<%= htmlWebpackPlugin.options.route.name %>.intl.js"></script>
+        <script src="/<%= htmlWebpackPlugin.files.chunks[htmlWebpackPlugin.options.route.name].entry %>"></script>
 
         <!-- Translate title element -->
         <script>
@@ -65,7 +65,7 @@
                     loc = loc.split('-')[0];
                 }
                 if (typeof window._messages[loc] !== 'undefined') {
-                    var localizedTitle = window._messages[loc]['general.' + '{{title}}'.toLowerCase()] || '';
+                    var localizedTitle = window._messages[loc]['general.' + '<%= htmlWebpackPlugin.options.config.title %>'.toLowerCase()] || '';
                     if (localizedTitle.length > 0) {
                         document.title = 'Scratch - ' + localizedTitle;
                     }
diff --git a/webpack.config.js b/webpack.config.js
index 8a1ef750e..1b07dd079 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -1,7 +1,7 @@
 var autoprefixer = require('autoprefixer');
 var CopyWebpackPlugin = require('copy-webpack-plugin');
+var HtmlWebpackPlugin = require('html-webpack-plugin');
 var gitsha = require('git-bundle-sha');
-var MustacheRendererPlugin = require('./mustache-renderer-webpack-plugin');
 var path = require('path');
 var webpack = require('webpack');
 
@@ -100,12 +100,17 @@ module.exports = {
         fs: 'empty'
     },
     plugins: [
-        new VersionPlugin({length: 5}),
-        new MustacheRendererPlugin({
-            templatePath: path.resolve(__dirname, './src/template.html'),
-            routes: routes,
-            config: require('./src/template-config.js')
-        }),
+        new VersionPlugin({length: 5})
+    ].concat(routes.map(function (route) {
+        return new HtmlWebpackPlugin({
+            title: route.title,
+            filename: route.name + '.html',
+            template: './src/template.html',
+            route: route,
+            config: require('./src/template-config.js'),
+            inject: false
+        });
+    })).concat([
         new CopyWebpackPlugin([
             {from: 'static'},
             {from: 'intl', to: 'js'}
@@ -124,5 +129,5 @@ module.exports = {
         }),
         new webpack.optimize.CommonsChunkPlugin('common', 'js/common.bundle.js'),
         new webpack.optimize.OccurenceOrderPlugin()
-    ]
+    ])
 };