JsExe
Revision2019 の 4K Intro 優勝作品のwebGLの圧縮スキルがわかりました。
javascriptをバイナリに変換して、それをを<img>
のソースとして読み込んで、そのソースをデコードし、eval()を使ってwebGLをやってるようです。
JsExeというToolを使うみたい。
出力ファイルは、バイナリとデコードのスクリプトが入っていて、そのまま使えます。
この圧縮するスキルは、ここに書いてある事と同等と思われます。
使い方
JsExeをdownloadしてくる。
javascriptだけで表示が出来るfileを用意する。
こんな感じ
var canvas = document.createElement("canvas");
canvas.style.position = "fixed";
canvas.style.cursor = "none";
canvas.style.left = canvas.style.top = 0;
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
document.body.appendChild(canvas);
var gl = canvas.getContext("webgl2") || canvas.getContext("experimental-webgl2");
var compileShader = function(prog, src, type){
var sh = gl.createShader(type);
gl.shaderSource(sh, src.replace(/^\n/, ""));
gl.compileShader(sh);
gl.attachShader(prog, sh);
gl.deleteShader(sh);
};
vs = `
# version 300 es
void main()
{
gl_Position = vec4(ivec2(gl_VertexID&1,gl_VertexID>>1)*2-1,0,1);
}
`
fs = `
# version 300 es
precision highp float;
out vec4 fragColor;
uniform vec2 resolution;
uniform float time;
void main() {
vec2 uv=(gl_FragCoord.xy*2.-resolution)/resolution.y;
vec3 col=.5+.5*cos(time+uv.xyx+vec3(0,2,4));
fragColor=vec4(col,1);
}
`
var p = gl.createProgram();
compileShader(p, vs, gl.VERTEX_SHADER);
compileShader(p, fs, gl.FRAGMENT_SHADER);
gl.linkProgram(p);
gl.useProgram(p);
gl.uniform2f(gl.getUniformLocation(p, "resolution"), canvas.width, canvas.height);
var zero = Date.now();
(function () {
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
gl.uniform1f(gl.getUniformLocation(p, "time"), (Date.now() - zero) * 0.001);
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
requestAnimationFrame(arguments.callee);
})();
この時のポイントは
styleを使ってcanvasを固定する事とcanvasの動的生成。
上のファイル名を test.js
として、出力ファイル名を index.html
とした場合のコマンドラインは
jsexe.exe -cn test.js index.html
これで、おしまい。
オプションは、-cn
だけで良さそう。
このindex.htmlは、localhostを立てないと見れない。
python3を使ってlocalhostを立ててみるには
python -m http.server
これでOK。
http://localhost:8000/
にアクセスしてください。
ここでは、複雑になるのでミニ化については、触れないでJsExeだけの使い方にとどめています。
JsExeを使ってのミニ化は、こちらを参考にすれば良いと思います。
Bercon/CoreCritical
base64を使ってみた
バイナリだと色々と制約があるみたい。なのでbase64を使ってみた。この場合は圧縮効果は無いと思います。利点としてソースを見せたくない時に利用が出来ると思います。
雑だけど、python3でJsExeの出力ファイルから、base64のimgソースを持つhtmlファイルを作るスクリプトを書きました。
import base64
import sys
def convert(inFile, outFile):
f = open(inFile, "rb")
data = f.read()
f.close
js= "<canvas id=V><img src=# onload=C=V.getContext(\'2d\');for($=_=\'\';C.drawImage(this,$--,0),X=C.getImageData(0,0,1,1).data[0];_+=String.fromCharCode(X));(1,eval)(_)>"
b64 = base64.encodestring(data)
js = js.replace("#","'data:image/png;base64,"+b64.decode('utf-8')+"'")
f = open(outFile, 'w')
f.write(js)
f.close()
if __name__ == '__main__':
args = sys.argv
convert(args[1],args[2])
これを b64Exe.py
として保存してもらうとしたら、
python b64Exe.py jsExe_Html out_Html
これでできます。
<canvas id=V><img src='data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAABdYAAAABCAAAAAD25KMnAAAC2UlEQVR4Xn1UXW/aMBTtMxL/wcqk
KaEhDbR9QlRiENZKXYeAdnuYxtzkJrFm7Mx2ANbuv892SgNttzyE+J5zfL85WmGBYsxWWKI+Snhc
LoGpIBaAFUQUzMl1KoLj9ZqNihtItaUQFFwSRTjTUiclG0icl4y4FJILgzPO4BVMIVUaPLApXmhT
qKnP5DVJVK6Na8ISvg4IYyC+GFvtLgeS5eoF59IaNek5sXuebANcFMCSYU5o4lZyz3oztchoHU8G
asiZgo0uwRruM9p1PPT4+BYMmwIEMS4wbe+4verKmC8LQmGW4wRMKdKSxaZqbiF45iMpYh+pbQHe
Q7OB9GM00qSb0aoRT1LXknqWZDBprTNeihhcmdubAgEFxfp88v0bO/GR43h7ioNItGQPwkrhOK+Q
XWD7eAIU1KHyj0WrR2dqBuhHs/FuBUKamTgNQwRSA5wkaIkJc71m4wHtPC4m9fCsID5ziX53XQ3c
gVCwuRq97/h7p4uLjtfqtjt+6Hes92ZDe0v/5bUQEBNryUmWFyilHJtJ4KWy7lAqcDbklAttLBlJ
uVgaoIsESE5LE1mNVHKkyBJ6Bxkh2zOrK1d9E/3Y3stFEmy2rW7Qrq/zTurvYNt7Ep6imNN+cH4c
nLdiLl3j47hcafXm2MBu6Hf9s6qLddB9WzKtrIuxm+Bif3ImgmcCL11DOux+4aOV9A3zLprOo6+L
2eVgFE3fJKYVcTwdfPwU3cz3qNpKCfu5c1M82UoJr0xVJbupq/EM1G11vuYxrpbBR05dH8fz0f7y
21O95/W+/gbBdcIjrCBgfG0TdXcbhkyDUD3+FLAw7oefrz9PFx9ux+NI/1zN0aOBR9Fkfrln3U1/
HXznf8Gbxpmw3ToW1LbxeaiFwiAMO/v7JPB6IATeSnPlfHo1uPl4HS1m+mvio9BHZzuygF8lSDVg
ZGmdjXVVwcUis/9oMogxpQB2Cjy3KszRX33+127r/QzqRT48Y2FudmFzIGlkPVY+PGltZyBzcmM9
IyBvbmxvYWQ9Qz1WLmdldENvbnRleHQoJzJkJyk7Zm9yKCQ9Xz0nJztDLmRyYXdJbWFnZSh0aGlz
LCQtLSwwKSxYPUMuZ2V0SW1hZ2VEYXRhKDAsMCwxLDEpLmRhdGFbMF07Xys9U3RyaW5nLmZyb21D
aGFyQ29kZShYKSk7KDEsZXZhbCkoXyk+
' onload=C=V.getContext('2d');for($=_='';C.drawImage(this,$--,0),X=C.getImageData(0,0,1,1).data[0];_+=String.fromCharCode(X));(1,eval)(_)>
こんなに意味不明のスクリプトが動く絵を出すなんて、楽しすぎです。
ちなみに、https://neort.io/ のhtmlに貼り付けたら動きました。