Update - I'm happy to annouce the demo got 3rd place in the JS1k 2018 competition!
1. Changing the process
2. Julia fractal auroras
3. 80s wireframes
4. Implementation
Since I had done a lot of background work I was really anxious and motivated to start programming the demo. I had a clear vision of the end product and had achieved a personal connection to it on many levels. I was so excited I basically did the whole thing over one weekend. I code-golfed the PoC implementations as small as I could so I would have room to add a storyline and add some bonus effects. As usual the biggest improvements came from simplifying the visuals rather than working on the code itself:
- I dropped the vertical lines from the wireframe engine and made the height function really simple
- I limited the number of total colors
- I rendered the fractal with really large pixels and only two colors. This not only saved space but made the demo run nicely on mobile phones
- I reused my old implementation of a starfield for a cheap night sky
Now I had a solid demo and a lot of bytes still left. It was time to go back to Pinterest to look for those little things that make a huge difference...
5. Adding the storyline
I think the reason that my Highway at Night demo became somewhat popular was because it not only created a small world but also had a story. Events followed each other and got crazier towards the end just when you thought you'd seen it all. I wanted to do the same thing this time and because of the auroras theme it was obvious how it all should go...
- Start with a nice landscape view with the camera following the path of a river
- Sun sets changing the water color and revealing the stars in the sky
- Scene opens up for the upcoming light show when we arrive at a lake
- Auroras fade in and approach the viewer
- Water starts reflecting the sky adding more interest to the scene
At this point I had pretty much ran out of space but I needed a grand finale. I got the idea for a meteor shower from a GIF on the internet showing objects hitting water. It was time to do some more code-golfing because it was clear I wouldn't be happy without getting it all in. Luckily my scanline rendering engine allowed to do the splashes with small cost and the meteors could be drawn as a simple chain of growing rectangles.
After some more heavy size optimizations I could actually get lazy and just do the background mountains with regular canvas lineTo-function and add shadowBlur to them. I know I could have done it differently to save space for even more things but I felt the demo was ready. I released it two weeks before the deadline. I was and still am very happy with it. I was left with many more ideas in my head but luckily found my way to Dwitter. I spent the next two weeks releasing several 140 bytes demos there and you can check them all out HERE. I'll be writing about them later.
6. Conclusions and source code
Inspiration and personal connection to the subjects will drive you to better performance and leave you a much happier programmer! Thanks for reading and see you on Twitter & Dwitter. :)
X=0;
S=50;
w=S*6;
setInterval(()=>{
X||(Z=T=0); // intialize counters
X=X>999?Z=0:X+.2; // loop back to start
a.height=a.width=W=w*2;
c.fillRect(0,0,W,W);
c.fillStyle="#A4A";
c.globalAlpha=(Z++-W)/W;
for(x=W;x&&Z>W;x-=6)// Julia
for(y=W;y>w;y-=6){
i=W*S/(y-w);
k=(X+i*Math.cos((x-w)/W)-S)/W;
l=i*Math.sin((x-w)/W)/W;
for(i=0;k*k+l*l<4&&i<40;i++)z=k*k-l*l+Math.cos(Z/w)/20-.8,l=2*k*l+.2,k=z;
if(i>25){
c.fillStyle=i>38?"#A4A":"#0C4";
c.fillRect(x,W-y,3,5);
X>420&&c.fillRect(x,y,3,1)// reflection
}
}
c.globalAlpha=1;
s=Z<w?0:s+.3; // sunset
for(x=7;x--&&s<S;)c.fillRect(250+x*x,337+s-x*9,99-x*x*2,6);
c.fillStyle="#0AC"; // stars
for(x=w;x&&s>S;x-=6)c.fillRect(x*x%W,x,1,1);
c.fillStyle="#112"; // mountains
c.strokeStyle=c.shadowColor="#0FF";c.shadowBlur=S;
c.lineTo(-99,350);c.lineTo(90,220);c.lineTo(140,270);c.lineTo(170,250);c.lineTo(w,360);c.lineTo(500,220);c.lineTo(700,350);c.fill();
c.fillStyle="#000"; // mask
c.fillRect(c.shadowBlur=0,340,W,S);
p=Z/S|0;
for(y=p*S;y<W*4+p*S;y+=S){ // scanlines
c.beginPath();
for(x=W*4;x-=S;){
z=w/(y-Z+w); // scaling
k=Math.abs(W*2-x)/(W+y)*9-1|0; // terrain height
i=w+(x-W*2)*z;j=w-(k*90-w)*z; // project to screen coordinates
y<w*7&&y%80<S&&x!=W*2&&c.lineTo(i,j);
k=y>1900?y-1900:0; // river flows into a lake
if(x==W*2){
c.fillStyle="#000";
y<W*4&&c.fillRect(i+Math.cos(y)*S*z-(S+k)*z,w+w*z,(150+k*2)*z,S*z);
c.fillStyle=s>S?"#0AC":"#A4A";
c.fillRect(i+Math.cos(y)*S*z-(S+k)*z,w+w*z+70*z,(150+k*2)*z,.5)
}
T>S&&( // meteor hit
k=(Q-120)*-4+x,
l=y-p*S-W,
j=Math.sqrt(k*k+l*l),
T>S&&j<T*4&&j>T*4-70&&c.fillRect(i,w+w*z,4,2)
)
}
c.stroke()
}
// meteor shower
if(T){
for(x=20;x--&&T<S;)c.fillRect(Q-T*2-x,T*7+x*3,x/4,x/2);
T=T>120?0:T+.9
}
!T&&Z>W*6&&(Q=w+Z%w,T=1)
},9)
Ei kommentteja:
Lähetä kommentti