diff --git a/scripts/examples-html/.gitignore b/scripts/examples-html/.gitignore
new file mode 100644
index 00000000..35b5e99a
--- /dev/null
+++ b/scripts/examples-html/.gitignore
@@ -0,0 +1 @@
+/html
diff --git a/scripts/examples-html/Makefile b/scripts/examples-html/Makefile
new file mode 100644
index 00000000..96af0d66
--- /dev/null
+++ b/scripts/examples-html/Makefile
@@ -0,0 +1,48 @@
+EXAMPLES := ../../examples
+SRC=$(wildcard $(EXAMPLES)/*/*.scad)
+PNG=$(patsubst $(EXAMPLES)/%.scad,html/%.png,$(SRC))
+HTML=$(patsubst $(EXAMPLES)/%.scad,html/%.html,$(SRC))
+
+OPENSCAD := ../../OpenSCAD.app/Contents/MacOS/OpenSCAD
+#OPENSCAD := openscad
+ARGS := --imgsize=1200,900 --camera=0,0,0,55,0,25,180 --viewall --autocenter
+
+all : $(PNG) $(HTML) example-data.js assets
+
+.PHONY: clean
+clean :
+ rm -rf html
+
+.PHONY: assets
+assets :
+ cp -a assets html/
+
+.PHONY: example-data.js
+example-data.js :
+ ( \
+ echo "openscad_examples = ["; \
+ for a in Basics Functions Shapes Extrusion Advanced; \
+ do \
+ echo " {"; \
+ echo " name : \"$$a\","; \
+ echo " files : ["; \
+ for f in "$(EXAMPLES)/$$a/"*.scad ; \
+ do \
+ echo " \"`basename -s .scad $$f`\","; \
+ done; \
+ echo " ]"; \
+ echo " },"; \
+ done; \
+ echo "];" \
+ ) > html/example-data.js
+
+html/%.png : $(EXAMPLES)/%.scad
+ mkdir -p `dirname $@`
+ $(OPENSCAD) $(ARGS) -o $@ $< > out.log 2>&1
+
+html/%.html : $(EXAMPLES)/%.scad template-pre.html template-post.html
+ #echo $(notdir $(patsubst %.scad,%.png,$<))
+ mkdir -p `dirname $@`
+ sed -e 's/@@IMAGE@@/$(notdir $(patsubst %.scad,%.png,$<))/g' template-pre.html > $@
+ sed -e 's/\</; s/>/\>/;' $< >> $@
+ cat template-post.html >> $@
diff --git a/scripts/examples-html/assets/css/main.css b/scripts/examples-html/assets/css/main.css
new file mode 100644
index 00000000..6774589f
--- /dev/null
+++ b/scripts/examples-html/assets/css/main.css
@@ -0,0 +1,58 @@
+.green {
+ color:#518e04;
+}
+
+#wrapper {
+ position: fixed;
+ top: 0;
+ left: 0;
+ width:100%;
+ height: 100%;
+}
+
+#content {
+ position: absolute;
+ top: 65px;
+ left: 10px;
+ right: 10px;
+ bottom: 10px;
+ border-width: 1px;
+ border-style: groove;
+ overflow: auto;
+ background: #fdf6e3;
+}
+
+#logo {
+ position: absolute;
+ top: 5px;
+ left: 5px;
+}
+
+#logo h1 {
+ position: absolute;
+ top: 2px;
+ left: 80px;
+ width: 20em;
+ margin: 0px;
+ font-family: "Open Sans", sans-serif;
+ font-size: 22px;
+}
+
+#logo img {
+ width: 60px;
+ height: 60px;
+}
+
+#image {
+ position: absolute;
+ z-index: 50;
+ top: 80px;
+ right: 30px;
+}
+
+#image img {
+ width: 200px;
+ height: 150px;
+ border-width: 1px;
+ border-style: groove;
+}
\ No newline at end of file
diff --git a/scripts/examples-html/assets/css/menu.css b/scripts/examples-html/assets/css/menu.css
new file mode 100644
index 00000000..bc83d266
--- /dev/null
+++ b/scripts/examples-html/assets/css/menu.css
@@ -0,0 +1,129 @@
+// http://www.olivergast.de/2012/04/18/css3-dropdown-menu/
+
+#menu {
+ width: 800px;
+ margin: 50px auto;
+}
+
+#menu h3 {
+ font-size: 12px;
+ color: #fff;
+ padding: 10px;
+ margin: 0;
+ background: #000;
+ line-height: 20px;
+}
+
+#menu ul {
+ position: absolute;
+ top: 30px;
+ margin: 0 auto;
+ margin-left: 20px;
+ list-style: none;
+}
+
+#menu ul li {
+ float: left;
+ margin: 0 0 0 14px;
+ font-size: 14px;
+ font-family: 'Arial', sans-serif;
+ line-height: 30px;
+}
+
+#menu ul li a {
+ color: #000;
+ text-decoration: none;
+ -webkit-transition: all .5s ease-in-out;
+ -moz-transition: all .5s ease-in-out;
+ -o-transition: all .5s ease-in-out;
+ -ms-transition: all .5s ease-in-out;
+ transition: all .5s ease-in-out;
+}
+
+#menu ul li a:hover {
+ color: #999;
+}
+
+#menu ul li div {
+ float: left;
+ width: 4px;
+ height: 4px;
+ margin: 10px 5px;
+ padding: 4px;
+ background: #999;
+ -webkit-border-radius: 12px;
+ -moz-border-radius: 12px;
+ border-radius: 12px;
+ -webkit-transition: all .5s ease-in-out;
+ -moz-transition: all .5s ease-in-out;
+ -o-transition: all .5s ease-in-out;
+ -ms-transition: all .5s ease-in-out;
+ transition: all .5s ease-in-out;
+}
+
+#menu ul li:hover div {
+ background: #000;
+}
+
+#menu ul ul {
+ position: absolute;
+ z-index: 100;
+ top: -9999px;
+ background: #fff;
+ padding: 0;
+ margin: 0 0 0 -5px;
+ -webkit-box-shadow: 0 10px 20px #888;
+ -moz-box-shadow: 0 10px 20px #888;
+ box-shadow: 0 10px 20px #888;
+}
+
+#menu ul ul:before {
+ position: absolute;
+ content:"";
+ width: 10px;
+ height: 10px;
+ top: -5px;
+ left: 11px;
+ background: #000;
+ -webkit-transform: rotate(45deg);
+ -moz-transform: rotate(45deg);
+ -o-transform: rotate(45deg);
+ -ms-transform: rotate(45deg);
+ transform: rotate(45deg);
+}
+
+#menu ul li:hover ul {
+ top: 30px;
+}
+
+#menu ul ul li {
+ float: none;
+ font-size: 12px;
+ padding: 5px 10px;
+ text-align: left;
+ margin: 0;
+ border-bottom: 1px solid #ddd;
+ line-height: 20px;
+ -webkit-transition: all .5s ease-in-out;
+ -moz-transition: all .5s ease-in-out;
+ -o-transition: all .5s ease-in-out;
+ -ms-transition: all .5s ease-in-out;
+ transition: all .5s ease-in-out;
+}
+
+#menu ul ul a {
+ color: #333;
+ text-decoration: none;
+}
+
+#menu ul ul li.all {
+ font-size: 12px;
+ border-bottom: none;
+ text-transform: none;
+}
+
+#menu ul ul li:hover {
+ background: #333;
+ color: #fff;
+}
+
diff --git a/scripts/examples-html/assets/css/solarized-light.css b/scripts/examples-html/assets/css/solarized-light.css
new file mode 100644
index 00000000..05f45835
--- /dev/null
+++ b/scripts/examples-html/assets/css/solarized-light.css
@@ -0,0 +1,85 @@
+/**
+ * Solarized Light theme
+ *
+ * Adaptation of Solarized Light from ethanschoonover.com/solarized
+ *
+ * @author Ethan Schoonover
+ * @author David Litmark
+ * @version 1.0.0
+ */
+pre {
+ background: #fdf6e3; /* base3 */
+ word-wrap: break-word;
+ margin: 0px;
+ padding: 6px;
+ color: #657b83; /* base00 */
+ font-size: 12px;
+}
+
+pre, code {
+ font-family: 'Monaco', courier, monospace;
+}
+
+pre .comment {
+ color: #93a1a1; /* base1 */
+}
+
+pre .constant {
+ color: #657b83; /* base00 */
+}
+
+pre .constant.language {
+ color: #268bd2; /* blue */
+}
+
+pre .constant.regexp {
+ color: #2aa198; /* cyan */
+}
+
+pre .storage {
+ color: #268bd2; /* blue */
+}
+
+pre .string, pre .comment.docstring {
+ color: #2aa198; /* cyan */
+}
+
+pre .support.tag.script, pre .support.tag.style {
+ color: #2aa198; /* cyan */
+}
+
+pre .string.regexp {
+ color: #2aa198; /* cyan */
+}
+
+pre .string.regexp.open, pre .string.regexp.close {
+ color: #2aa198; /* cyan */
+}
+
+pre .keyword, pre .selector {
+ color: #859900; /* green */
+}
+
+pre .inherited-class {
+ font-style: italic;
+}
+
+pre .entity {
+ color: #b58900; /* yellow */
+}
+
+pre .support, *[data-language="c"] .function.call {
+ color: #859900; /* green */
+}
+
+pre .support.method {
+ color: #657b83; /* base00 */
+}
+
+pre .support.property {
+ color: #657b83; /* base00 */
+}
+
+pre .variable.global, pre .variable.class, pre .variable.instance {
+ color: #657b83; /* base00 */
+}
diff --git a/scripts/examples-html/assets/images/openscad.png b/scripts/examples-html/assets/images/openscad.png
new file mode 100644
index 00000000..192e5f1c
Binary files /dev/null and b/scripts/examples-html/assets/images/openscad.png differ
diff --git a/scripts/examples-html/assets/js/language/generic.js b/scripts/examples-html/assets/js/language/generic.js
new file mode 100644
index 00000000..7e1dde65
--- /dev/null
+++ b/scripts/examples-html/assets/js/language/generic.js
@@ -0,0 +1,65 @@
+/**
+ * Generic language patterns
+ *
+ * @author Craig Campbell
+ * @version 1.0.11
+ */
+Rainbow.extend([
+ {
+ 'matches': {
+ 1: [
+ {
+ 'name': 'keyword.operator',
+ 'pattern': /\=|\+/g
+ },
+ {
+ 'name': 'keyword.dot',
+ 'pattern': /\./g
+ }
+ ],
+ 2: {
+ 'name': 'string',
+ 'matches': {
+ 'name': 'constant.character.escape',
+ 'pattern': /\\('|"){1}/g
+ }
+ }
+ },
+ 'pattern': /(\(|\s|\[|\=|:|\+|\.)(('|")([^\\\1]|\\.)*?(\3))/gm
+ },
+ {
+ 'name': 'comment',
+ 'pattern': /\/\*[\s\S]*?\*\/|(\/\/|\#)[\s\S]*?$/gm
+ },
+ {
+ 'name': 'constant.numeric',
+ 'pattern': /\b(\d+(\.\d+)?(e(\+|\-)?\d+)?(f|d)?|0x[\da-f]+)\b/gi
+ },
+ {
+ 'matches': {
+ 1: 'keyword'
+ },
+ 'pattern': /\b(and|array|as|b(ool(ean)?|reak)|c(ase|atch|har|lass|on(st|tinue))|d(ef|elete|o(uble)?)|e(cho|lse(if)?|xit|xtends|xcept)|f(inally|loat|or(each)?|unction)|global|if|import|int(eger)?|long|new|object|or|pr(int|ivate|otected)|public|return|self|st(ring|ruct|atic)|switch|th(en|is|row)|try|(un)?signed|var|void|while)(?=\(|\b)/gi
+ },
+ {
+ 'name': 'constant.language',
+ 'pattern': /true|false|null/g
+ },
+ {
+ 'name': 'keyword.operator',
+ 'pattern': /\+|\!|\-|&(gt|lt|amp);|\||\*|\=/g
+ },
+ {
+ 'matches': {
+ 1: 'function.call'
+ },
+ 'pattern': /(\w+?)(?=\()/g
+ },
+ {
+ 'matches': {
+ 1: 'storage.function',
+ 2: 'entity.name.function'
+ },
+ 'pattern': /(function)\s(.*?)(?=\()/g
+ }
+]);
diff --git a/scripts/examples-html/assets/js/language/javascript.js b/scripts/examples-html/assets/js/language/javascript.js
new file mode 100644
index 00000000..fc881f82
--- /dev/null
+++ b/scripts/examples-html/assets/js/language/javascript.js
@@ -0,0 +1,93 @@
+/**
+ * Javascript patterns
+ *
+ * @author Craig Campbell
+ * @version 1.0.9
+ */
+Rainbow.extend('javascript', [
+
+ /**
+ * matches $. or $(
+ */
+ {
+ 'name': 'selector',
+ 'pattern': /(\s|^)\$(?=\.|\()/g
+ },
+ {
+ 'name': 'support',
+ 'pattern': /\b(window|document)\b/g
+ },
+ {
+ 'matches': {
+ 1: 'support.property'
+ },
+ 'pattern': /\.(length|node(Name|Value))\b/g
+ },
+ {
+ 'matches': {
+ 1: 'support.function'
+ },
+ 'pattern': /(setTimeout|setInterval)(?=\()/g
+
+ },
+ {
+ 'matches': {
+ 1: 'support.method'
+ },
+ 'pattern': /\.(getAttribute|push|getElementById|getElementsByClassName|log|setTimeout|setInterval)(?=\()/g
+ },
+
+ /**
+ * matches any escaped characters inside of a js regex pattern
+ *
+ * @see https://github.com/ccampbell/rainbow/issues/22
+ *
+ * this was causing single line comments to fail so it now makes sure
+ * the opening / is not directly followed by a *
+ *
+ * @todo check that there is valid regex in match group 1
+ */
+ {
+ 'name': 'string.regexp',
+ 'matches': {
+ 1: 'string.regexp.open',
+ 2: {
+ 'name': 'constant.regexp.escape',
+ 'pattern': /\\(.){1}/g
+ },
+ 3: 'string.regexp.close',
+ 4: 'string.regexp.modifier'
+ },
+ 'pattern': /(\/)(?!\*)(.+)(\/)([igm]{0,3})/g
+ },
+
+ /**
+ * matches runtime function declarations
+ */
+ {
+ 'matches': {
+ 1: 'storage',
+ 3: 'entity.function'
+ },
+ 'pattern': /(var)?(\s|^)(\S*)(?=\s?=\s?function\()/g
+ },
+
+ /**
+ * matches constructor call
+ */
+ {
+ 'matches': {
+ 1: 'keyword',
+ 2: 'entity.function'
+ },
+ 'pattern': /(new)\s+(.*)(?=\()/g
+ },
+
+ /**
+ * matches any function call in the style functionName: function()
+ */
+ {
+ 'name': 'entity.function',
+ 'pattern': /(\w+)(?=:\s{0,}function)/g
+ }
+]);
diff --git a/scripts/examples-html/assets/js/menu.js b/scripts/examples-html/assets/js/menu.js
new file mode 100644
index 00000000..3866e65b
--- /dev/null
+++ b/scripts/examples-html/assets/js/menu.js
@@ -0,0 +1,38 @@
+function addSubmenu(menu, name, files) {
+ var ul = document.createElement('ul');
+ ul.appendChild(document.createElement('h3'));
+ for (var a = 0; a < files.length; a++) {
+ var menulink = document.createElement('a');
+ menulink.href = "../" + name + "/" + files[a] + ".html";
+ ul.appendChild(menulink);
+ var li = document.createElement('li');
+ li.appendChild(document.createTextNode(files[a]));
+ menulink.appendChild(li);
+ }
+
+ menu.appendChild(ul);
+}
+
+function addMenu() {
+ var menudiv = document.getElementById("menu");
+ var menu = document.createElement('ul');
+ menudiv.appendChild(menu);
+
+ for (var a = 0; a < openscad_examples.length; a++) {
+ var entry = document.createElement('li');
+ var menulink = document.createElement('a');
+ menulink.href = "#";
+ entry.appendChild(menulink);
+ var entrydiv = document.createElement('div');
+ menulink.appendChild(entrydiv);
+ menulink.appendChild(document.createTextNode(openscad_examples[a].name));
+
+ addSubmenu(entry, openscad_examples[a].name, openscad_examples[a].files);
+
+ menu.appendChild(entry);
+ }
+}
+
+function load() {
+ addMenu();
+}
diff --git a/scripts/examples-html/assets/js/rainbow.min.js b/scripts/examples-html/assets/js/rainbow.min.js
new file mode 100644
index 00000000..9cd1d811
--- /dev/null
+++ b/scripts/examples-html/assets/js/rainbow.min.js
@@ -0,0 +1,8 @@
+/* Rainbow v1.1.9 rainbowco.de */
+window.Rainbow=function(){function q(a){var b,c=a.getAttribute&&a.getAttribute("data-language")||0;if(!c){a=a.attributes;for(b=0;b=e[d][c])delete e[d][c],delete j[d][c];if(a>=c&&ac&&b'+b+""}function s(a,b,c,h){var f=a.exec(c);if(f){++t;!b.name&&"string"==typeof b.matches[0]&&(b.name=b.matches[0],delete b.matches[0]);var k=f[0],i=f.index,u=f[0].length+i,g=function(){function f(){s(a,b,c,h)}t%100>0?f():setTimeout(f,0)};if(C(i,u))g();else{var m=v(b.matches),l=function(a,c,h){if(a>=c.length)h(k);else{var d=f[c[a]];if(d){var e=b.matches[c[a]],i=e.language,g=e.name&&e.matches?
+e.matches:e,j=function(b,d,e){var i;i=0;var g;for(g=1;g/g,">").replace(/&(?![\w\#]+;)/g,
+"&"),b,c)}function o(a,b,c){if(b
+
+
+
+