Anders Tornblad

All about the code

Label archive for demo

Finally got around to fixing Sparky

Back in 2010, the iPad was all the rage. The first really usable tablet device, sporting lightning-fast multitouch capabilities, that were even exposed as (then non-standard) HTML5 Web APIs for JavaScript developers to play with.

It wasn't always easy to keep up with the latest trends, and trying out multitouch experiments then required an iPad. So I wrote a touch event simulation tool, called addTouch. The principle was to use the mouse to add, manipulate and remove multiple touch points, dispatching touch events to the browser.

This made it possible to develop multitouch experiences and test them using an average desktop browser. I supported both Apple's touch events and the (now extinct) Mozilla touch events.

Sparky : just a demo app

Sparky in actionTo demonstrate what addTouch could do, I put together a simple demonstration app called Sparky. Its principle is very simple — put your fingers on the screen to create sparking lines that you can move around. This was in the really early days of HTML5 support, and I hadn't learned Canvas drawing yet, so my implementation uses a few <div> elements and some CSS transform magic.

This has worked fine for almost six years now. I just had to fix a small issue when Apple released the iPhone 4, which had a high-dpi ("Retina") display. There were a few versions of the iOS Safari browser that had bugs when it came to handling device pixels vs logical pixels. I had to do a lot of experimenting with this, and wrote a StackOverflow answer that is currently my third most up-voted one.

What started out as just a demo app for addTouch, now has a life of its own. After my latest complete blog remake, I have been keeping a close look at the IIS logs that I get from Azure. It turns out that Sparky still gets a few hundred visits per week from different incoming, which is nice, but it prompted me to do some much-needed spring cleaning.

Update

The code was actually not too shabby, but some of it was really outdated. It still supported the old Mozilla touch events, and it used screen coordinates instead of local coordinates, which made it look pretty horrible in a non-fullscreen view, especially on the Android Chrome browser. That needed to change.

It was written in a certain style, specifically catered to my own old JavaScript minifier which I no longer use. Also, it polluted the global window object, which I never do anymore, unless I'm writing public APIs. That needed to change, too.

Other than that, it was just the small issue of estimating screen DPI, where I had used hard-coded values directly adapted for various iOS devices. That needed to be replaced with some more general-purpose code, and after a little head-scratching, I just decided to go with the same hard-coded value for all devices. At least for now.

The result is now live and kicking on spark.attrakt.se, and the complete code is available in the lbrtw/sparky GitHub repository.

Next step

I will try to fix some of the issues with addTouch as well, but I'll save that exercise for later. It is not very useful anymore, because the major browsers and development environments have really good touch emulation built in.

Try it out: spark.attrakt.se

Revisiting CssFlip

When I first started experimenting with modern Web APIs, 3D transforms was still an experimental feature. Browser support was limited and shaky, and required vendor prefixes. These days support is much better, but there are still a lot of quirks when it comes to browser implementation.

Back in 2011, I wrote a demo called CSS Page Flip, using a combination of CSS and some JavaScript to let a user flip through pages in a book. The transitions are declared in CSS and are triggered by JavaScript. Pages on the left have their transform-origin set to their right edge, which is aligned to the horizontal center of the screen, while pages on the right have their transform-origin set to their left edge. By applying backface-visibility: hidden, I can then rotate pages along the Y axis for a pretty simple effect.

#book { perspective: 2000px; transform-style: preserve-3d; } .page { width: 50%; height: 100%; position: absolute; top: 0px; left: 0px; margin-left: 50%; overflow: hidden; transform-style: flat; backface-visibility: hidden; transition: none; transform: none; } .page:first-child { margin-left: 0px; transform-origin: right center; } .page:last-child { transform-origin: left center; } .currentFold.forward > .page:last-child { transform: rotateY(0deg); } .nextFold.forward > .page:first-child { transform: rotateY(179.9deg); } .currentFold.backward > .page:first-child { transform: rotateY(0deg); } .nextFold.backward > .page:last-child { transform: rotateY(-179.9deg); } .folding .page { transition: all 1s ease-in-out; } .folding > .currentFold.forward > .page:last-child { transform: rotateY(-179.9deg); } .folding > .nextFold.forward > .page:first-child { transform: translateZ(1px) rotateY(0deg); } .folding > .currentFold.backward > .page:first-child { transform: rotateY(179.9deg); } .folding > .nextFold.backward > .page:last-child { transform: translateZ(1px) rotateY(0deg); }

It took a lot of fiddling to find good values for rotateY(). Almost every new version of Webkit broke my experiment, but I eventually settled on a combination of 0deg, -180deg and 180deg.

CSS Flip in EdgeEdge behaving badly A couple of years later, the major browsers started supporting 3D transforms, even without vendor prefixes. Unfortunately all of them have different ideas about how to transition from 180deg or -180deg to 0deg. Finally I thought of forcing the browsers to do what I want by having -179.9deg and 179.9deg. This works fine now, unless for (of course) IE and Edge. IE doesn't even support 3D transforms correctly without prefix, and for some reason Edge treats the transformation matrix differently than the other browsers, completely breaking part of the animation.

Apart from the page flipping, the demo also has an automatic page layout mechanism that reflows the chapters and the text blocks when needed. In the original demo, the contents of the book was actually a detailed description of how the demo was made, but unfortunately the original content was lost at some point. Now it is my personal story instead. Enjoy!

During the next week or two, I will make an effort to bring the solution forward into the 2016 state of mainstream browsers, including the following:

  • Fix scroll wheel flipping in Firefox
  • Handle chapter navigation using history.pushState()
  • Add some interactive stuff on some of the pages
  • Some effort of fixing the weird behavior in Microsoft Edge
  • Minimal effort of making it work in Microsoft IE

When that is done, I will open-source the whole thing on GitHub.

About to solve an old THREE.js bug and move on with Artsy

I really need to pay more attention. Almomst a year ago, THREE.js released the r67 version, which removed the concept of centroids. This made part 3 of Artsy break. I used centroids and the Mesh.calculateCentroid function, not because I needed to, but because some tutorial told me I should.

When the concept of centroids was removed, in April 2014, my JavaScript demos was very low on my list of priorities, but soon I will make time for fixing and advancing. Who knows, I might even be able to finish Artsy once and for all. I started working on it in October of 2013, so it's really overdue!

For now, I have removed the calls to calculateCentroid and done some small changes to at least get Part 3 to start. Stay posted!

Artsy part 3, SoundBox

Wow, it's been a really long time since I wrote anything about my demo projects here. I apologize for that, but the good news is that I have finally found some extra time to spend on "pleasure programming" again.

First, there's the unfinished business of Artsy (and slightly insane). About five months ago, I was finished with the first two parts, and I haven't had time to start on part three until now. So far, it looks pretty good. Unfortunately it doesn't yet work on iOS devices, but feel free to check it out:

Artsy part 3

SoundBox

Someone in the CODEF Facebook group linked to the SoundBox tool, and I find it quite amazing. It's a JavaScript-based chip tune tracker, with a really nice and small playback routine. I have been experimenting a bit with it, and here are some results:

Lots of unfinished business in this update, and I'm sorry about that.

Artsy (and slightly insane), first two parts now in beta

A couple of minutes ago, I uploaded a new version of Artsy (and slightly insane). It includes JavaScript remakes of the first two parts of the iconic Amiga Demo Arte by Sanity.

The code is pure JavaScript and Canvas. There is no Flash, no Silverlight, no WebGL stuff, and there are no frameworks involved. When I'm done remaking the third and final part of Arte, my plans are to release the full source code for the demo. Also, I'll take the "plumbing" parts of the code and release as a JavaScript demo framework in itself, and I'll open-source it.

But for now, enjoy the first two parts of "Artsy (and slightly insane)". The adress is demo.atornblad.se/artsy.

Phenomenal & Enigmatic, part 4

TV Cube remakeI remember seeing the "TV Cube" part of Enigma for the first time – and not really being able to figure out how it was made. Heck, I couldn't even do the math for a proper backface culling, so back in the 1990s my occational 3D objects were never any good. So the thought of making 2D and 3D objects appear on the surfaces of another 3D object was way beyond my understanding of math.

Once again, I am aware that the prettier way of doing this is by manipulating a transformation matrix to rotate, translate and project coordinates from different branches of a hierarchical coordinate system. But I ignored that and rolled it all by hand.

Star field

The stars on the front of the cube might look as if there is some depth, but that's just an illusion. Each star has an (X,Y) coordinate, and a third constant (which I called Z) that governs speed along the X axis and also the alpha component of its color. The lower the speed, the dimmer the light. When observed face on, it gives the impression of a 3D space, but it's really just a form of parallax scroller.

Pseudo code

for (var star, i = 0; star = stars[i++];) { // Move the star a bit to the "right" star.x += (star.z * star.z * speedConstant);     // Limit x to (-1 .. 1) if (star.x > 1) star.x -= 2;     // Left out: Project the star's coordinates to screen coordinates var screenCoords = ( /* left out */ );     // Draw the star, using Z to determine alpha and size context.fillStyle = "rgba(255,255,255," + (star.z * star.z).toFixed(3) + ")"; context.fillRect(screenCoords.x, screenCoords.2, star.z * 2, star.z * 2); }

Hidden line vector

Back in the days, I could never do a proper hidden line vector, because I didn't know how to properly cull back-facing polygons. For the Phenomenal & Enigmatic "TV Cube" part, I arranged all polygons in the hidden line pyramid so that when facing the camera, each polygon is to be drawn clockwise. That way I could use a very simple algorithm to determine each polygon's winding order.

I found one really efficient algorithm on StackOverflow, and I learned that since all five polygons are convex (triangles cannot be concave, and the only quadrangle is a true square), it's really enough to only check the first three coordinates, even for the quadrangle.

Rotating the pyramid in 3D space was exactly the same as with the intro part of the demo, and after all coordinates are rotated, I simple use the polygon winding order algorithm to perform backface culling, then drawing all polygons' outlines. Voilá, a hidden line vector.

Pseudo code

// Points var points = [ { x : -40, y : -40, z : 70 }, // Four corners at the bottom { x : 40, y : -40, z : 70 }, { x : 40, y : 40, z : 70 }, { x : -40, y : 40, z : 70 }, { x : 0, y : 0, z : -70 } // And finally the top ];   // Each polygon is just an array of point indices var polygons = [ [0, 4, 3], // Four triangle sides [1, 4, 0], [2, 4, 1], [3, 4, 2], [3, 2, 1, 0] // And a quadrangle bottom ];   // First rotate the points in space and project to screen coordinates var screenCoords = [];   for (var point, i = 0; point = points[i++];) { screenCoords.push(rotateAndProject(point)); // rotateAndProject is left out }   // Then go through each polygon and draw those facing forward for (var polygon, i = 0; polygon = polygons[i++];) { var edgeSum = 0; for (var j = 0; j < 3; ++j) { var pointIndex = polygon[j]; var pointIndex2 = polygon[(j + 1) % 3];   var point = screenCoords[pointIndex]; var point2 = screenCoords[pointIndex2];   edgeSum += (point2.x - point.x) * (point2.y + point.y); }   if (edgeSum < 0) { // This polygon is facing the camera // Left out: Draw the polygon using screenCoords, context.moveTo and context.lineTo } }

Plane vector

The plane vector is super-simple. Just rotating a plane around its center and then using the code already in place to project it to screen coordinates.

Projection

The function responsible for translating coordinates in the 3D space to screen coordinates is not particularly complex, since it's basically the exact same thing as for the intro part of the demo. Also, to determine which faces of the cube that are facing the camera, I just used the same backface culling algorithm as for the hidden line vector. I was really pleased with the end result.

Phenomenal & Enigmatic, part 1
Phenomenal & Enigmatic, part 2
Phenomenal & Enigmatic, part 3
Phenomenal & Enigmatic, part 4 (this part)

The entire demo, including non-minified JavaScript, is available on GitHub: /lbrtw/enigmatic

Artsy (and slightly insane), first part now in beta

In between writing about the Phenomenal & Enigmatic JavaScript demo, I'm also doing a JavaScript remake of the Arte demo by Sanity from 1993. The first effect I made was the "disc tunnel" effect, seen 2min 9sec into the YouTube clip, and the entire first part is now live, but still in beta.

The address is demo.atornblad.se/artsy, but I haven't tested it that many browsers and devices yet. I do know that it crashes on Windows Phone 7.8 after just a few scenes, but it works really nicely on my iPad 3, especially in fullscreen mode. Add a shortcut to your iPad's start screen for fullscreen mode.

I will make some changes to the bitmap tunnel effect, and make sure that the demo runs correctly on most browsers and devices. Also, stay tuned for parts 2 and 3 of Sanity Arte, and of course there will be a blow-by-blow description here on atornblad.se when the whole thing is complete.

Phenomenal & Enigmatic, part 2

Stars and text intro

Stars and text Enigma screendumpThe opening scene of Enigma by Phenomena starts out looking like an average side-scrolling star parallax, which was very normal in 1991. Nice way to lower people's expectations. :) But after just a couple of seconds, the stars begin twisting and turning in space around all three axes.

Back in 1991 I knew how to rotate a plane around the origo, by simply applying the angle sum identities of Sine and Cosine. I also realized that rotating any 3D coordinate in space could by done by simply the case of rotating around more than one axis, one axis a time.

Stars and text screendumpIn the Phenomenal & Enigmatic demo, I only rotate the stars around two axes. First I rotate the (x,z) components around the Y axis to get (x',z'), and then the (y,z') components around the X axis to get (y',z''). I also translate each star along the X axis before the rotation takes place. To finally get the 2D screen coordinates of the 3D space coordinate, I take the (x',y') coordinate and multiply by (2/(2+z'')) for a pseudo-distance feel. The z'' value controls both the color alpha component, and the size of the rectangle being drawn.

The even better way of doing this is through vector addition and multiplication, but I'm sticking to the math that I know. :) After all this math is in place, the trick is to change the offset and rotation variables in a nice way.

Rendering text is just a couple of calls to the context.fillText method and animating the value of context.globalAlpha.

Pseudo code

// Prefetch sin and cosine of angles var cosY = Math.cos(yAngle); var sinY = Math.sin(yAngle); var cosX = Math.cos(xAngle); var sinX = Math.sin(xAngle);   for (var star, i = 0; star = stars[i++]; ) {     // Fetch x, y, z and translate x     var x = star.x + xOffset;     var y = star.y;     var z = star.z;         // Limit x to [-1 .. 1]     while (x > 1) x -= 2;     while (x < -1) x += 2;         // Rotate (x, z) around Y axis     var x2 = x * cosY + z * sinY; // x'     var z2 = z * cosY - x * sinY; // z'         // Rotate (y, z') around X axis     var y2 = y * cosX + z2 * sinX; // y'     var z3 = z2 * cosX - y * sinX; // z''         // Transform to screen coordinates     var screenX = x2 * 2 / (2 + z3) * halfScreenWidth + halfScreenWidth;     var screenY = y2 * 2 / (2 + z3) * halfScreenWidth + halfScreenHeight;         // Draw the star     context.fillRect(screenX, screenY, 2 - z3, 2 - z3); }

Phenomenal & Enigmatic, part 1
Phenomenal & Enigmatic, part 2 (this part)
Phenomenal & Enigmatic, part 3
Phenomenal & Enigmatic, part 4

The entire demo, including non-minified JavaScript, is available on GitHub: /lbrtw/enigmatic

Almost pure CSS page flip

Google Chrome still has a long way to go when rendering stuff that is a just little bit more advanced. Experimenting with CSS3 3D Transforms.

I placed a video element on a div element that rotates in 3D using CSS3 3D Transforms and CSS3 Transitions. In Safari, everything renders smoothly, even when the video is playing! In Chrome (I’m using Google Chrome 9 Beta), the entire div containing the video disappears while the transition is running.

That experiment led to more experimenting, and suddenly I had a fully functioning page flipping effect – almost purely done in CSS. Using a simple combination of -webkit-tranform-origin and -webkit-transform went a very long way.

Actually, the contents of the project explains itself. If you are using Google Chrome or Desktop Safari on Windows or Mac, use the scroll wheel to flip through pages, or click on a page. If you are on an iOS device, flick a page to go forward or backward.

Please note! This works best on Safari and on iOS devices. Google Chrome does support CSS 3D Transforms, but only if you actively enable it. Type about:flags into the address bar of your Google Chrome browser, and click Enable for GPU Accelerated Compositing to enable 3D CSS in Chrome, then restart the browser.

The experiment is called CSS Flip. Please try it out now on demo.atornblad.se/cssflip and tell me what you think.