Check out Lutz Roeder’s Monotone — very, very cool.
Pay close attention to how tightly synched the animation is with the visuals. I’ve been digging into the source code and there are several techniques he uses that are worth calling out. First, it is interesting to note how the entire logic of the experience is controled in code. There is nothing in Page.xaml. It is in the constructor of his Page class that he wires up his Loaded event and it is in the Loaded event where he uses the Downloader object to insure that the mp3 he wants to play is in fact downloaded. He then starts the .mp3 playing on the Downloader_DownloadProgressChanged. If you watching carefully, you’ll notice that he throws up a progress bar while the Downloader is running and he includes the code for the ProgressBar class, worth grabbing that source. So why does he go to all this work instead of just assigning the mp3 to a MediaElement directly? Well, by handling the downloading of the mp3 himself, it ensures that he knows exactly when the music starts, so that his animations are exactly in synch, which if you notice, the timing is perfect as the squares animate in.
He adds the first visual by dynamically loading his XAML file into the canvas with this code:
using (StreamReader reader = new StreamReader(this.GetType().Assembly.GetManifestResourceStream(“Monotone.Grid.xaml”)))
I include this because it isn’t exactly intuitive code to write! Sure, XmlReader.Load is not rocket science, but getting at the xaml file requires some arm twisting, as you can see. Once the Grid.xaml file is loaded, its animations kick off based on an EventTrigger. I’m sure he used Blend to create these animations. In fact, if you open up the project in Blend, you’ll see that you can run all the animations from within Blend. However, here’s one tricky part. You can see there are two Storyboards nested within the Canvas.Loaded EventTrigger. But the second storyboard’s BeginTime happens to fall exactly at 6.85 seconds, which is timed for when the first storyboard ends. Unfortunately, there isn’t a way to set this value in Blend. He probably just manually added it to the XAML.
The next part of the piece is a spiral math art animation. But before how does he know to unload the grid.xaml and fire off the spiral animation? He actually uses a timer! You’ll notice that he starts off an HtmlTimer right after he calls play on on the mp3. The timer’s interval is set here:
this.timer.Interval = (int)((60f / 140f) * 4 * 4 * 2 * 1000f);
Not sure why he couldn’t just use the TimeSpan object and cast to (int), but Lutz will have to address that! Anyway, this is what he uses to then unload his Grid and load his custom Spiral class. The Spiral class, which subclasses Canvas, does not use the Silverlight animation engine but rather wires up a timer for behavior similar to WPF’s CompositionTarget.Render or Flash’s OnEnterFrame. Now, I must admit this code threw me for a loop:
double interval = ((60f / 140f) * 50f);
if (HtmlPage.BrowserInformation.Name != “Microsoft Internet Explorer”)
interval = interval * 1.461f;
Why did he have to special case IE when creating his timer?
For the spiral effect, he’s using some trigonometry to create a new ellipse on each tick in such a way that a spiral is formed. Similarly, on each tick, he rotates the spiral. At a certain point, he zooms and fades it. And, when his other timer hits what is around 6.85 seconds, he yanks it from the VisualTree and inserts Stroposcope, which also is a timer based animation and also subclasses Canvas. Within Strotoscope, he adds three instances of Ring, which is another canvas that is a series of Ellipse objects that he then manipulates, again using trigonometry. There is someother interesting code in there for getting the background to white out at certain points in the animation as far as watching where he is in the timer.
All in all, a great piece. The music is so tightly synched with the visuals. Watch and listen closely and you’ll see what I mean. Which raises the question: which came first? The animation or the music?