Instantly share code, notes, and snippets.

@jtpio /README.md forked from gre/README.md
Last active Mar 2, 2016

Embed
What would you like to do?
JS1K WebGL Template

JS1K Boilerplate for WebGL entries

Inspired by @greweb's boilerplate

Flow

First time? Install the tools:

npm install

Then download shader_minifier.exe:

wget http://www.ctrl-alt-test.fr/dl/shader_minifier.exe

Then:

  1. Copy your shader from shadertoy to shader.fs.
  2. npm run all: to minify and compress the shader and the javascript code.
  3. (optional) npm run compo: to minify and compress the javascript code only.

It will give you some stats:

    615 compo.js
    515 crushed.js
    495 minified.js

DWTFYW license

  • 2014 - @greweb
  • 2015 - extended by @jtpio
for(x in g)
g[x.match(/^..|[A-V]/g).join("")]=g[x];
with(g){
for(x=crP(y=v=2);y;coS(n),atS(x,n))
shS(n=crS(35634-y),
--y?"precision lowp float;"+
"vec2 R=vec2("+({RESOLUTION_WIDTH})+","+({RESOLUTION_HEIGHT})+");"+
"{FRAGMENT}"
:"attribute vec4 a;void main(){gl_Position=a;}");
veAP(enVAA(biB(34962,crB())), 2, 5126, liP(x), usP(x),
buD(34962,new Float32Array([1,1,1,-3,-3,1]),35044)
);
setInterval(
'g.drA(4,g.uniform1f(g.geUL(x,"T"),v+=.01),3)',
33)
};
var fs = require('fs');
var path = require('path');
var spawn = require('child_process').spawn;
var shader = fs.readFileSync(path.resolve(__dirname, 'shader.fs')).toString();
var compoShim = fs.readFileSync(path.resolve(__dirname, 'compo_shim.js')).toString();
var RESOLUTION = 4; // the resolution will be divided by 4, but still displayed fullscreen
if (!fs.existsSync(path.resolve(__dirname, 'shader_minifier.exe'))) {
console.error('Cannot find shader_minifier.exe.\nPlease install it from the following, or rename it to shader_minifier.exe:\n' +
'http://www.ctrl-alt-test.fr/dl/shader_minifier.exe');
process.exit(1);
}
shader = shader.replace(/mainImage\(.*\)/, 'main()');
var mapping = {
'iGlobalTime': 'T',
'iResolution': 'R',
'fragCoord': 'gl_FragCoord',
'fragColor': 'gl_FragColor'
};
for (var m in mapping) {
shader = shader.replace(new RegExp(m, 'g'), mapping[m]);
}
shader = 'uniform float T;' + shader;
fs.writeFileSync(path.resolve(__dirname, 'shader_renamed.fs'), shader);
var shaderMinifier = spawn('mono', [ path.resolve(__dirname, 'shader_minifier.exe'), '--format', 'none', 'shader_renamed.fs', '-o', 'shader_minified', '--preserve-externals' ]);
shaderMinifier.on('close', function (code) {
injectShaderInCompo();
});
function injectShaderInCompo() {
var shaderMinified = fs.readFileSync(path.resolve(__dirname, 'shader_minified')).toString();
var compo = compoShim.replace('{FRAGMENT}', shaderMinified);
compo = compo.replace(/\{RESOLUTION_WIDTH\}/g, 'a.width/=' + RESOLUTION);
compo = compo.replace(/\{RESOLUTION_HEIGHT\}/g, 'a.height/=' + RESOLUTION);
fs.writeFileSync(path.resolve(__dirname, 'compo.js'), compo);
process.stdout.write(compo);
}
{
"name": "js1k_template",
"version": "0.0.0",
"devDependencies": {
"uglify-js": "2.4.x",
"jscrush": "0.0.0"
},
"scripts": {
"all": "node pack.js | uglifyjs -c unused=false | tee minified.js | jscrush > crushed.js && sed -e '/SUB/r crushed.js' < shim.html > index.html && wc -c compo.js crushed.js minified.js",
"compo":"cat compo.js | uglifyjs -c unused=false | tee minified.js | jscrush > crushed.js && sed -e '/SUB/r crushed.js' < shim.html > index.html && wc -c compo.js crushed.js minified.js"
}
}
void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
vec2 uv = fragCoord.xy / iResolution.xy;
fragColor = vec4(uv,0.5+0.5*sin(iGlobalTime),1.0);
}
<!doctype html>
<html>
<!-- note: your demo in this shim runs in an iframe with this content: https://gist.github.com/qfox/3cccc4f36c8319e09bb7 -->
<!--
(c) js1k.com 2015
Note: submissions belong to their respectful owner, do not copy without their consent
-->
<head>
<meta charset="utf-8">
<title>JS1k 2015 - NNNN - TITLE</title>
<meta name="author" content="YOU">
<link rel="icon" type="image/png" href="http://js1k.com/favicon.png">
<link rel="canonical" href="http://js1k.com/2015-hypetrain/demo/NNNN">
<link rel="shortlink" href="http://js1k.com/NNNN">
<script>
// GA
</script>
<style>
/* http://qfox.nl/notes/333 */
body,html,iframe{margin:0;padding:0;border:0;width:100%;height:100%}
iframe{position:absolute;top:0;left:0;padding-top:50px;box-sizing:border-box}
header{position:relative;z-index:1;height:47px;padding-top:2px;border-bottom:1px solid #000;box-shadow:0 -10px 25px #ccc inset;background-color:#eee}
aside,div,h1,p{overflow:hidden;white-space:nowrap;text-overflow:ellipsis;text-align:center;font-size:16px;font-weight:inherit;line-height:22px;padding:0;margin:0;cursor:default}
aside,h1{display:inline}
a{color:#000;text-decoration:none;border-bottom:1px dashed #000}
a:hover{border-bottom:1px solid red}
a[href="0"]{text-decoration:line-through;pointer-events:none;border-bottom:0;color:#ccc}
.button{float:left;width:40px;height:40px;line-height:40px;text-align:center;padding:0;margin:2px 0 0 10px;border:1px solid #888;border-color:#ddd #888 #888 #ddd;font-family:sans-serif;font-size:30px;font-weight:700;cursor:pointer}
.button:hover{color:red;border-bottom-color:#888}
.r{margin-right:10px}
time{display:none}
</style>
</head>
<body>
<header>
<div>
<h1>
<a href="http://js1k.com/">JS1k</a>
<a href="http://js1k.com/2015-hypetrain">2015</a>
<strong></strong> demo
&mdash;
"TITLE" by YOU
</h1>
<p>
<em>
FIRST PART OF YOUR DESC GOES HERE
</em>
</p>
<aside>
&mdash;
1024 bytes
&mdash;
<a href="http://js1k.com/2015-hypetrain/details/NNNN">demo details</a>
&mdash;
<a href="http://js1k.com/2015-hypetrain/demos">list of demos</a>
&mdash;
<a href="http://js1k.com/1955" title="short link for your mobile devices" rel="nofollow">js1k.com/NNNN</a>
<time datetime="NOW" pubdate>NOW</time>
</aside>
</div>
<a href=".html" class="button p">&Larr;</a>
<a href=".html" class="button n">&Rarr;</a>
</header>
<script type="shim">
// SHIM (will be ran in the context of the iframe...)
// unprefix some popular vendor prefixed things (but stick to their original name)
iwin.AudioContext = iwin.AudioContext || iwin.webkitAudioContext; // ios8 unmutes audio only during the first user triggered event with sound
iwin.requestAnimationFrame = iwin.requestAnimationFrame || iwin.mozRequestAnimationFrame || iwin.webkitRequestAnimationFrame || iwin.msRequestAnimationFrame || function(f){ iwin.setTimeout(f, 1000/30); };
canvas.requestPointerLock = canvas.requestPointerLock || canvas.mozRequestPointerLock || canvas.webkitRequestPointerLock;
navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia;
a = canvas;
b = idoc.body;
if (webgl) iwin.c = canvas.getContext('2d');
else iwin.g = (function () {
iwin.onorientationchange = iwin.onresize = null;
try {
var o = { antialias: true, stencil: true };
var gl = canvas.getContext('webgl', o) || canvas.getContext('experimental-webgl', o);
// keep in scope, must not be garbage collected
iwin.__glExts =
[ 'OES_texture_float', 'OES_texture_float_linear', 'OES_standard_derivatives',
'EXT_texture_filter_anisotropic', 'MOZ_EXT_texture_filter_anisotropic', 'WEBKIT_EXT_texture_filter_anisotropic',
'WEBGL_compressed_texture_s3tc', 'MOZ_WEBGL_compressed_texture_s3tc', 'WEBKIT_WEBGL_compressed_texture_s3tc'
].map(function(ext) {
return gl.getExtension(ext);
});
} catch (e) {
idoc.body.innerHTML = 'WebGL not supported.';
iwin.a=iwin.b=iwin.c=iwin.d=null;
throw e;
}
return gl;
})();
</script>
<script>
// submission form configurables:
// enable canvas shim at all? (2d/3d). other settings are ignored if this is false.
var TOKEN_CANVAS_SHIM = true;
// true enables webgl shim (exposes `g`), false enables canvas shim (exposes `c`)
var TOKEN_WEBGL = true;
// px, 0 means always 100%
var TOKEN_MAX_WIDTH = 0;//640;
// px, 0 means always 100%
var TOKEN_MAX_HEIGHT = 0;//360;
// only if width<100%
var TOKEN_CENTER_CANVAS = true;
// "press" reload button on orientation change?
var TOKEN_RELOAD_ONORIENTATIONCHANGE = true;
</script>
<script type="demo">
//SUB
</script>
<script>
(function(){var doc=document;var header=doc.getElementsByTagName("header")[0];var firstChild=header.firstChild;var p=doc.getElementsByClassName("p")[0];var n=doc.getElementsByClassName("n")[0];header.insertBefore(p,firstChild);header.insertBefore(n,firstChild);header.appendChild(doc.getElementsByTagName("p")[0])})();
(function reload(fullscreen){var doc=document;var header=doc.getElementsByTagName("header")[0];var iframe=doc.createElement("iframe");doc.body.appendChild(iframe);var iwin=iframe.contentWindow;var idoc=iframe.contentDocument;idoc.open();idoc.close();idoc.write("<!doctype html>"+'<html style="margin: 0; padding: 0; border: 0; width: 100%; height: 100%;">'+"<head>"+'<meta charset="utf-8">'+'<body style="margin: 0; padding: 0; border: 0; width: 100%; height: 100%;">'+(TOKEN_CANVAS_SHIM?'<canvas style="display: block;'+
(TOKEN_CENTER_CANVAS?" margin: auto;":"")+'"></canvas>':"")+"");if(TOKEN_CANVAS_SHIM){var canvas=idoc.getElementsByTagName("canvas")[0];var cs=canvas.style;idoc.body.clientWidth;cs.width=(canvas.width=Math.max(Math.min(TOKEN_MAX_WIDTH||innerWidth,innerWidth),0)||0)+"px";cs.height=(canvas.height=Math.max(Math.min(TOKEN_MAX_HEIGHT||innerHeight-50,innerHeight-50),0)||0)+"px"}if(TOKEN_RELOAD_ONORIENTATIONCHANGE)onorientationchange=reloadClick;iwin.AudioContext=iwin.AudioContext||
iwin.webkitAudioContext;iwin.requestAnimationFrame=iwin.requestAnimationFrame||iwin.mozRequestAnimationFrame||iwin.webkitRequestAnimationFrame||iwin.msRequestAnimationFrame||function(f){iwin.setTimeout(f,1E3/30)};if(TOKEN_CANVAS_SHIM)canvas.requestPointerLock=canvas.requestPointerLock||canvas.mozRequestPointerLock||canvas.webkitRequestPointerLock;idoc.body.requestPointerLock=idoc.body.requestPointerLock||idoc.body.mozRequestPointerLock||idoc.body.webkitRequestPointerLock;navigator.getUserMedia=navigator.getUserMedia||
navigator.webkitGetUserMedia||navigator.mozGetUserMedia||navigator.msGetUserMedia;if(TOKEN_CANVAS_SHIM)iwin.a=canvas;iwin.b=idoc.body;if(TOKEN_CANVAS_SHIM){if(!TOKEN_WEBGL)iwin.c=canvas.getContext("2d");if(TOKEN_WEBGL)iwin.g=function(){iwin.onorientationchange=iwin.onresize=null;try{var o={antialias:true,stencil:true};var gl=canvas.getContext("webgl",o)||canvas.getContext("experimental-webgl",o);iwin.__glExts=["OES_texture_float","OES_texture_float_linear","OES_standard_derivatives","EXT_texture_filter_anisotropic",
"MOZ_EXT_texture_filter_anisotropic","WEBKIT_EXT_texture_filter_anisotropic","WEBGL_compressed_texture_s3tc","MOZ_WEBGL_compressed_texture_s3tc","WEBKIT_WEBGL_compressed_texture_s3tc"].map(function(ext){return gl.getExtension(ext)})}catch(e){idoc.body.innerHTML="WebGL not supported.";iwin.a=iwin.b=iwin.c=iwin.d=null;throw e;}return gl}()}var demo=idoc.createElement("script");demo.textContent=doc.querySelector('script[type="demo"]').textContent;idoc.body.appendChild(demo);idoc.close();iframe.contentWindow.focus();
var firstLine=doc.getElementsByTagName("div")[0];function reloadClick(b){doc.body.removeChild(iframe);r.parentElement.removeChild(r);iframe=null;r=null;idoc=null;header=null;reload(b)}window.reload=reloadClick;var r=doc.createElement("div");r.innerHTML="&#8635;";r.className="button r";r.title="restart just the demo (local, without remote fetch)";r.onclick=reloadClick;header.insertBefore(r,firstLine)})();
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment