c.T=c.translate,c.Z=c.rotate,c.m=c.moveTo,c.l=c.lineTo,r=new AudioContext,T=s=0;setInterval("a.width=J=innerWidth;a.height=K=innerHeight;D=.5*(S=Math.sin)((N=Date.now())/500)-.5;A=(P=Math.PI)/4*(C=Math.cos)(N/1500)+P/4;B=N/10%(3*R);c.clearRect(0,0,J,K);c.T(-B,0);for(e=0;e<K/R*3/2;e++)for(f=0;n=0,f<J/R;f++){c.T(U=f*3*R+(e%2>0?R*3/2:0),V=e*R*S(P/3)),h=[P*2/3,-P,-P*2/3,-P/3,0,P/3];for(i=0;i<P*5/3;n++)c.lineCap='butt',c.lineWidth=9,c.strokeStyle='hsl('+(((5-n)*60+N/10)%360)+',99%,50%)',L=S(j=i+P/3)*R,M=C(i)*R,Q=(C(j)*R+M)/2,O=(S(i)*R+L)/2,c.beginPath(),c.T(u=Q+(Q-M)*D,v=O-(O-L)*D),c.Z(y=h[n]+A),c.m(0,0),c.l(H=R/2*(1-D)*S(P/3)/S(P*2/3-A),0),c.Z(-y),c.T(-u,-v),c.T(w=Q-(Q-M)*D,x=O+(O-L)*D),c.Z(z=h[n]+P-A),c.m(0,0),c.l(H,0),c.Z(-z),c.T(-w,-x),c.stroke(),i=j;c.T(-U,-V)}c.T(B,0)",1);setInterval("if(s.type)s.disconnect();(s=r.createOscillator()).connect(r.destination);s.frequency.value=Math.pow(2,((W=\"EAAAYEEEFFFFnAAAE@A@YEEEF@F@nAAA\".charCodeAt(T/2)>>(T++%2)*3&7)+16)/12)*(W>0?440:0);T%=64;s.start()",R=150)
// JS1K ENTRY
// Title: Harmony
// by Muhammad Rifqi Priyo Susanto
// This source code mostly telling tricks used in this demo.
/*
Below are list of global one-character variable used in this demo.
a: (JS1k) canvas
b: (JS1k) body
c: (JS1k) CanvasRenderingContext2D
d: (JS1k) document
e: loop
f: loop
g: (JS1k) CanvasRenderingContext3D
h: at[]
i: loop
j: loop (next)
n: counter
r: AudioContext
s: Oscillator
u: ux
v: uy
w: vx
x: vy
y: ur
z: vr
A: ray's angle
B: translation animation
C: Math.cos
D: distance between rays
H: Hanken's length
J: window.innerWidth
K: window.innerHeight
L: jy
M: ix
N: Date.now()
O: my
P: Math.PI
Q: mx
R: Hexagon's size
S: Math.sin
T: audio's time
U: Center of hexagon in x axis
V: Center of hexagon in y axis
W: audio's data
Z: draw loop
*/
// Initialise some values.
// Some shortcuts for canvas.
c.T = c.translate, c.Z = c.rotate, c.m = c.moveTo, c.l = c.lineTo,
// No need to add round-bracket if it has no argument.
r = new AudioContext,
T = s = 0;
// DRAWING //
// The drawing loop.
// This demo is an example of islamic geometric pattern with hexagonal tiling.
// Mostly inspired by this video: https://youtu.be/sJ6pMLp_IaI
setInterval( function () {
// Set the canvas size into correct width and height while also assign them
// into variables.
a.width = J = innerWidth;
a.height = K = innerHeight;
// Assign a function or value into a variable while also using it.
// Distance and angle is calculated using Date.now().
// I was experimenting with the value, so these numbers are "magic numbers".
D = .5 * ( S = Math.sin )( ( N = Date.now() ) / 500 ) - .5;
A = ( P = Math.PI ) / 4 * ( C = Math.cos )( N / 1500 ) + P / 4;
// Translation for the tiles. We use modulo 3 * R, so we only repeat the
// tile movement and don't need to draw many hexagons.
B = N / 10 % ( 3 * R );
c.clearRect( 0, 0, J, K );
c.T( -B, 0 );
for ( e = 0; e < K / R * 3 / 2; e++ )
for ( f = 0; n = 0, f < J / R; f++ ) {
c.T( U = f * 3 * R + ( e % 2 > 0 ? R * 3 / 2 : 0 ), V = e * R * S( P / 3 ) ),
// This array contains rotation offset for each side of hexagons.
h = [ P * 2 / 3, -P, -P * 2 / 3, -P / 3, 0, P / 3 ];
// Old trick: use comma to separate command to save two curly-brackets.
for ( i = 0; i < P * 5 / 3; n++ )
c.lineCap = 'round',
// We use biggest number which can fit in one byte.
c.lineWidth = 9,
// Instead of writing 100%, we can slightly reduce it and use 99%.
// Because one byte is expensive.
c.strokeStyle = 'hsl(' + ( ( ( 5 - n ) * 60 + N / 10 ) % 360 ) + ',99%,50%)',
// These messy things are for drawing two lines at each side
// of hexagon.
L = S( j = i + P / 3 ) * R,
M = C( i ) * R,
Q = ( C( j ) * R + M ) / 2,
O = ( S( i ) * R + L ) / 2,
// We can use just one beginPath() to draw two lines.
c.beginPath(),
// We use Manhattan distance instead of Euclidean distance to
// save bytes also increasing performance.
c.T( u = Q + ( Q - M ) * D, v = O - ( O - L ) * D ),
c.Z( y = h[ n ] + A ),
c.m( 0, 0 ),
c.l( H = R / 2 * ( 1 - D ) * S( P / 3 ) / S( P * 2 / 3 - A ), 0 ),
c.Z( -y ),
c.T( -u, -v ),
c.T( w = Q - ( Q - M ) * D, x = O + ( O - L ) * D ),
c.Z( z = h[ n ] + P - A ),
c.m( 0, 0 ),
c.l( H, 0 ),
c.Z( -z ),
c.T( -w, -x ),
c.stroke(),
i = j;
c.T( -U, -V )
}
c.T( B, 0 )
// Actually, some browsers support to not include second argument, but seems
// like it is not supported in Firefox.
}, 1 );
// AUDIO //
// The song you hear is from Vidya Vidya - Safari Fruits. Only some parts included.
// (https://youtu.be/PbIjuqd4ENY)
// It is not copyrighted sound, so I can use it. Also, I can't make good song.
setInterval( function () {
// Rather than checking if it's undefined or not, we check value
// inside OscillatorNode.
if ( s.type )
s.disconnect();
( s = r.createOscillator() ).connect( r.destination );
// We use note to frequency equation:
// freq = base * 2^((note - 49) / 12)
s.frequency.value = Math.pow(
2,
// This string is actually song data, packed.
// This song used in this demo only ranging 6 notes, so we can pack
// it like this:
// |---------8 bits---------|
// 0 1 1 1 0 0 0 0
// mark note (6) no-notes
// Since do (C6) is note number 64, we can simplify the equation:
// freq = base * 2^((note + 16) / 12)
// Played note = 63 + value
// To simplify things, we place first note at rightmost bits, so we
// can shift right 0 bit for first note and then 3 bits for
// second note.
( ( W = "EAAAYEEEFFFFnAAAE@A@YEEEF@F@nAAA".charCodeAt( T / 2 ) >>
( T++ % 2 ) * 3 & 7 ) + 16 ) / 12
// If the value is zero, it means it doesn't play anything.
) * (W > 0 ? 440 : 0);
T %= 64;
s.start()
// This R variable is actually also used in drawing loop.
}, R = 150 );
// More explanation will be in my blog post.