The Andes are the world longest mountain range: 7,000 km and 1,022 bytes of JavaScript, making for a nice entry for Assembly 2013
There is also a video capture and unpacked version in case your browser or graphic card does not support webGL or present some compatibility issues with the bootstrapped versions.
No rest for the wicked
It's only been one month since MINAMI DISTRICT. I was really happy with the design and result but somehow I missed the mark.
Assembly 2013 was around the corner. It was the perfect opportunity to test my growing interogation of where do JS1k prods stand against the big guys. To play anywhere near the same league, the choice of technology was obvious: webGL + audio + PNG bootstrapping
While looking in my inspiration folder, I kept coming back to this picture by Geoffroy THOORENS, concept artist and matte painter:
An epic scene, perfect to render in a relatively small raymarcher.
Two weeks to production
Time was running quick, I had some reading and testing to do to render a nice landscape in webGL, let alone in 1k. After one week of tinkering and failing, I finally got a decent landscape. Unfortunately it was clear the falling cubes would not fit.
Pluging the shader into the setup of HYPERSONIC MANDELBULB, I could start optimizing and working on the sound. The optimization was straight forward: work on the shader, lower the entropy of the code, etc... The sound part was challenging. I was hoping to get some wind and a melody or at least a sweep sound. All attempts to make a melody sounded too simple and repetitive.
Quite a mix
The PNG bootstrapping technique relies on a Canvas element and its 2D context. Unfortunately it's not possible to get another context from the same Canvas element, so another Canvas element needs to be created, and styled to take the whole screen, and get a 3D context. The most compact way to do this was to set the outerHTML
of the existing Canvas to replace it completely. Yuck!
c.outerHTML='<canvas width=768 height=432 id=d style=position:fixed;left:0;top:0;width:100%;height:100%;background:#210>';
For the animation loop I usually go with a simple setInterval(foo,9)
but when you deal with webGL, this approach makes it too easy to flood the GPU and crash the graphic driver. This time I had no choice but to do the right way and use requestAnimationFrame(foo)
. Also, using Date
objects in order to synchronize the intro and outro fades was too verbose. The shortest and most elegant way was to use the currentTime
property of the Audio element. Nice!
m=function(){dr(4,uniform1f(gf(p,'t'),o.currentTime),3);requestAnimationFrame(m)};m(o.play())
A familiar face ?
If you follow the demoscene, you may recall a brillant Windows 1k intro written in assembly and HLSL featuring a mountain landscape: HIMALAYA by TBC, released at Icons 2008:
Well, to be honest I recalled this one a little late.
I was worried about the similitudes. But upon watching both intros I fell ANDES had enough merits on its own to not scrap the project altogether. Sure it has less features. A lot less actually. However I believe the camera, design and color treatment bring something to the table.
Beside it's been 5 years since HIMALAYA, and both use a very different set of technologies. If TBC, or anybody find an offense there. I'm sorry, it was not my intention to rip off HIMALAYA. If anything the name, ANDES, is an homage to HIMALAYA.
Something missing
There are many things I wished I did better or managed to fit in 1024 bytes:
- The massive cubes falling in the background
- Better sound
- A white title with shadow at the end
All in all, I am quite happy with the result.
Source code
Here is the PNG bootstrap.
<canvas id=c><img src=# onload=for(a=c.getContext('2d'),i=e='',S=String.fromCharCode;a.drawImage(this,i--,0),t=a.getImageData(0,0,1,1).data[0];)e+=S(t);(1,eval)(e)>
And here is the actual code of ANDES. Note that the PNG bootstrap exposes the following variables: c
, t
and S
c.outerHTML='<canvas width=768 height=432 id=d style=position:fixed;left:0;top:0;width:100%;height:100%;background:#210>';for(x in g=d.getContext('experimental-webgl'))g[x[0]+x[6]]=g[x];with(g){for(p=cP();s=cS(t+35632);ce(s),aS(p,s))sS(s,t++?'attribute vec4 t;void main(){gl_Position=t;}':'precision lowp float;uniform float t;float a(vec4 r,int d){float f=0.,s=32.;for(int i=0;i<48;i++)if(i<d){vec4 n=fract(r/32.)-.5;f+=(n.x*n.x+n.z*n.z)*s;s*=.5;r.xz*=mat2(1.2,1.4,-1.4,1.2);}return f;}float f(inout vec4 r,vec4 y,int d){float f=0.,s=32.;for(int i=0;i<48;i++)if(.1<s&&f<32.){f+=s=(r.y-a(r,d))*.3;r+=s*y;}return f;}void main(){vec4 r=vec4(1.2+cos(t/8.)*8.,24.+cos(t*.3),t,0.),y=normalize(vec4(gl_FragCoord.xy/vec2(24.,13.5)-16.+cos(t),8.,0.)),m=vec4(.1,0.,min(1.,f(r,y,8)/32.),0.);gl_FragColor=mix(vec4(f(r,normalize(vec4(a(r+m.xyyy,8),a(r+m.yxyy,8),a(r+m.yyxy,8),a(r,8))-a(r,8)),2)/32.),vec4(.8,1.2,1.4,1.)*y.z,m.z)-pow(t/32.-1.,32.);}');vA(eV(bf(34962,cB())),2,5126,lo(p),ug(p),bD(34962,new Float32Array([1,1,1,-3,-3,1]),34962+82));for(r=q='data:audio/wav;base64,UklGRiQAAABXQVZFZm10IBAAAAABAAEA5h0AAOYdAAABAAgA';t<5e5;t++)q+=S(Math.random()+t/(32.+Math.cos(t/8.))%5);o=new Audio(r+btoa(q));m=function(){dr(4,uniform1f(gf(p,'t'),o.currentTime),3);requestAnimationFrame(m)};m(o.play())}
You can clearly see the different parts:
- HTML setup and hashing of the methods of the 3D context
- Setting up the the shaders and buffers
- Precalculating the audio
- The main loop
Different aspect ratios
Considering the size limit, I had to make one version of ANDES for each of the common aspect ratios:
Hope you like ANDES. Make sure to leave comments, piggies and thumbs up or down for ANDES on Pouet.net.
Other recent projects
There are many experiments and projects like ANDES to discover other here.
- FRONTFEST MOSCOW It was an honour to be invited to Fronfest Moscow 2017 with the little family to give my first workshop; implementing a Twin-stick shooter using ES6 and Canvas, and to continue my CODE🎙ART series of talks + live coding aiming to inspire new web developer artists. on November 18th, 2017
- VOLTRA VOLTRA: Grinding the Universe, a gritty JavaScript demo, winner of the 1024 bytes demo competition at the Assembly 2017. on August 6th, 2017
- BREATHING EARTH Another take on Nadieh Bremer mesmerizing Breathing Earth visualisation, running at 60fps on a 2D Canvas without libraries or frameworks. on June 26th, 2017
- CODING⯌ART AT RENDER CODING⯌ART at Render 2017 was part of my series of talks + live coding aiming to inspire new web developer artists. on March 31th, 2017
- THREAD JS Breaking the 64 bytes fronteer with the famous "10 print" maze generator. on December 16th, 2013
- MINI DISTRICT How to build a 3D City in 256 bytes with Canvas 2D on August 17th, 2013
- MINAMI DISTRICT DEMOJS 2013's winner 1k intro. With MINAMI DISTRICT I wanted to do something fresh. Something never seen in normal demos before: A city with a twister skyscraper. on June 29th, 2013
- RUBBER IN SOLID # A crazy twister effect in 256bytes of DHTML on January 6th, 2008
Let's talk
Don't be shy; get in touch by mail, twitter, github, linkedin or pouet if you have any questions, feedback, speaking, workshop or performance opportunity.