Scott Hanselman

Fallback HTML5 audio tags for a simple MP3 podcast are harder than you'd think

March 26, '13 Comments [33] Posted in HTML5 | Javascript
Sponsored By

I've been spending the evenings and weekends lately redesigning the blog and the Hanselminutes podcast site. I hadn't realized how cheesy looking the podcast site was all these years. I'd like to get the show expanded to a wider audience as I feel that listenership has kind of flattened lately. I am in the process of adding faces for ALL 360+ shows going back 6 years.

A big thanks to Lynsey Smith from Portland Girl Geek Dinners, by the way, for her hard work in finding pics for me!

I also wanted a nicer in-browser audio experience so I assumed I'd just drop in the audio tag and be done, right?

The HTML5 Audio tag is wonderful, right? Just works. This is the dream:

<audio id="audioplayer" preload="metadata" type="audio/mp3" >
<source src="" type="audio/mp3"/>
Your browser doesn't support the HTML audio tag. Be sad.

You can try that live at if you like.

Except it's not nearly that easy.

Here's what you'll see on IE9+:


Here's Chrome:


Here's Firefox, version 19:

Ya, Firefox currently doesn't support MP3 audio so it just flashes once then disappears. Firefox will support MP3s in audio soon though by using the underlying operating system to play the stream rather than its own embedded code.

In Firefox 20 (the beta channel) on Windows 7 and above, you can test MP3 Audio support by turning on the preference in about:config.

The part I was disappointed in was more of an HTML5 specification issue. Notice that while I have fallback text present, I don't see it in Firefox. That's because fallback elements are only used if your browser doesn't support the audio tag at all.

It doesn't do what I would expect at all. What I want is "Can you support any of these audio sources? No? Fallback." This seems intuitive to me.

I talked to Chris Double via Christian Heilmann at Mozilla and he said "You'd need to raise the issue with WHATWG/W3C. It's been debated before in the past. " Indeed it has. From Oct 2009, more people saying that it's not intuitive to fall back in this way:

I expected (incorrectly, in this case) that if I only produced one source element (an MP4), Firefox would drop down to use the fallback content, as it does if I include an object element for a format not supported (for example, if I include a QuickTime object and QT is not installed, the user sees fallback content). As far as I can see, the only option in this situation is to rely on Javascript and the video element's canPlayType() function. - Kit Grose

This lack of an intuitive fallback means that I can't make an audio player that works everywhere using just HTML. I have to use JavaScript, which is a bummer for such a fundamental scenario.

Getting HTML5 audio to fall back correctly in all browsers

Instead you have to make an audio tag dynamically, then interrogate the tag. This applies to both audio and video tags. I ended up using some code from my friend Matt Coneybeare.

<audio id="audioplayer" preload controls loop>
<source src="audio.mp3">
<script type="text/javascript">
var audioTag = document.createElement('audio');
if (!(!!(audioTag.canPlayType) && ("no" != audioTag.canPlayType("audio/mpeg")) && ("" != audioTag.canPlayType("audio/mpeg")))) {
AudioPlayer.embed("audioplayer", {soundFile: "audio.mp3"});

The AudioPlayer.embed at the end there is the WordPress AudioPlayer in standalone form. This way on Firefox I get the flash player since it answered false to canPlayType.

Flash audio player in Firefox

A Responsive and Touch-Friendly Audio Player in HTML5

However, the default audio player made by the <audio> tag is kind of lame, and I'd like it to better support touch, look great on tablets, etc. For this, I'll turn to Osvaldas Valutis's AudioPlayer. It's a nice little jQuery plugin that replaces the <audio> element with a lovely chunk of HTML. Since you can't actually style the HTML5 <audio> element, people just hide it, recreate it, then broker calls over to the hidden-but-still-working audio element.

This plugin, along with a little CSS styling of its default colors gives me a nice audio player that looks the same and works everywhere. Except Firefox 19/20 until the next version Firefox answers true to "canPlayType" and then it should just start working! Until then, it's the Flash fallback player, which works nicely as well.


The other problem is the QuickTime plugin that most Firefox users have installed. When styling with the Osvaldas' AudioPlayer, the JavaScript interrogation would cause Firefox will prompt folks to install it in some cases if it's not there, and it still doesn't work if it is installed.

I ended up modifying Matt's detection a little to work with this Osvaldas' styling. I realize the code could be more dynamic with less elements, but this was easier for me to read.

  • First, try the audio tag. Works? Great, style it with audioPlayer();
  • Can't do MP3 audio? Dynamically make a Flash player with that P. Hide the audio player (likely not needed.)

Unfortunately for readability, there's the ".audioPlayer" jQuery plugin that styles the HTML and there's the "AudioPlayer" flash embed. They are different but named the same. I didn't change them. ;)

<audio id="audioplayer" preload="auto" controls style="width:100%;" >
<source src="your.mp3" type="audio/mp3">
Your browser doesn't support the HTML audio tag. You can still download the show, though!
<p id="audioplayer_1"></p>
<script type="text/javascript">
var audioTag = document.createElement('audio');
/* Do we not support MP3 audio? If not, dynamically made a Flash SWF player. */
if (!(!!(audioTag.canPlayType) && ("no" != audioTag.canPlayType("audio/mpeg")) && ("" != audioTag.canPlayType("audio/mpeg")))) {
AudioPlayer.embed("audioplayer_1", {soundFile: "your.mp3", transparentpagebg: "yes"});
$( '#audioplayer').hide();
else /* Ok, we do support MP3 audio, style the audio tag into a touch-friendly player */
/* If we didn't do the "if mp3 supported" check above, this call would prompt Firefox install quicktime! */
$( '#audioplayer' ).audioPlayer();

All in all, it works pretty well so far.

ODD BUG: Chrome does seem to have some kind of hang where this audio player is getting blocked while the comments load on my site. Any JavaScript experts want to weight in? If you load a page - like this one - and hit play before the page is loaded, the audio doesn't play. This only happens in Chrome. Thoughts?

While you're here, check out the new and consider subscribing! It's "Fresh Air for Developers."

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by SherWeb

Changing ASP.NET web.config inheritance when mixing versions of child applications

March 26, '13 Comments [19] Posted in ASP.NET | Bugs | IIS
Sponsored By

Mixed Application Pools

My blog and all the sites in and around it are a mix of .NET 2.0, 3.5 and 4. This blog engine is currently .NET 3.5 and runs at, but the application at (the root) is .NET 4.

You can happily mix and match applications across .NET versions on a single IIS instance. You can see how mixed my system is in the screenshot at right there.

However, things got messy when I changed the parent / application to .NET 4, but kept the child /blog as .NET 3.5 (the 2.0 CLR). I got lots of errors like

  • Unrecognized attribute ‘targetFramework’. Note that attribute names are case-sensitive. The targetFramework attribute was inherited from the root .NET 4 Web.config file in the Default Web Site root using ASP.NET configuration inheritance and confused the /blog .NET 2 application.

I didn't want to change the /blog applications' web.config. I just wanted to stop it from inheriting the settings from the parent application. Turns out you can wrap whole sections in a location tag, and then tell that scoped tag to prevent child applications from inheriting.

What you do is change the parent .NET 4 app's web.config to indicate its settings shouldn't flow down to the children, like the .NET 2/3.5 /blog app.

<location path="." inheritInChildApplications="false">
...your system.web stuff goes here

You can actually read about this in detail in the ASP.NET 4 "breaking changes" documentation. Of course YOU read those closely, don't you? ;)

I chose to change this settings for all of System.Web, but you could do it on a per-section basis if you preferred.

Hope this helps you!

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by SherWeb

NuGet Package of the Week #13 - Portable HttpClient makes portable libraries more useful

March 21, '13 Comments [17] Posted in NuGet | NuGetPOW | Win8 | Windows Client | WinPhone
Sponsored By

Reference Assemblies include .NET Portable AssembliesWhen you've got an idea for an app, it's likely that you've got the idea for that app in more than one place. By this I mean, you'll start with a phone app, then make a desktop app, then a web app. Or you'll make a game on one platform and then want it to work anywhere. In fact, with the rise of Xamarin, C# lets you put an app in every AppStore in the world with one language.

You likely already knew that you can target different versions of the .NET framework. You likely also know that there are small .NET Frameworks like Silverlight and the tiny .NET Micro Framework.

You can also target XBox and Windows Phone, OR better yet, target a profile called Portable Libraries that I've briefly mentioned before. Portable Libraries are a great idea that have some issues when you try to really use them. There's actually a great (if a little older) video with the inventors over at Channel 9. Note that Portable Libraries ship with Visual Studio 2012 and are a supported thing.

The idea is that you write a library that contains as much shared functionality as possible and then every application uses your now "portable" library. However, the subset of classes that are available are a subset. That means you can only use things that are available in the intersection of the targets you choose. Check this dialog:

Choose your target framework

And check out the Supported Features table at this MSDN article on Portable Libraries to find out what you can use where. Here's a graphical table I stole from Daniel.


However, most folks that use Portable Libraries have ended up using them mostly for ViewModels - just simple classes without any real functionality. Almost as if we had a DLL full of structs. There are some great posts on how to make Portable Class Libraries work for you using architectural techniques like indirection and appropriate interfaces.

The number one complaint around code resuse and the number one voted item over at the Visual Studio UserVoice was "Add HttpClient support in Portable Class Libraries (including Windows Phone 8)." Why? Because the GETting of a remote resource via HTTP is such a fundamental thing that it'd be awesome to be able to bake that data access into a portable library and use it everywhere.

Now there is a Portable Http Client and you can get it via NuGet!

install-package -pre

Here's an example of what the code looks like for a GET. Note that I'm using async and await also.

public static async Task<HttpResponseMessage> GetTheGoodStuff() 
var httpClient = new HttpClient();
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, "");
var response = await httpClient.SendAsync(request);
return response;

.NET Portable Subset

If you were going to make a Twitter client (lame example, but bear with me) you could now put the JSON HTTP data access code in one library and share it across Windows Phone, Windows Store, WinForms, Console, whatever.

I'm hoping that the folks at MS and the folks at Mono will continue to work to make Portable Libraries a good option for Mono as well. I've been advocating (and pushing) to make something happen as well, as have the Portable Libraries folks. You'll find lots of working in the space around the web, so fear not, code reuse, either through Portable Libraries or via linked code files at compilation time is deeply possible. The game "Draw A Stickman Epic" achieved 95% code reuse by writing the game in C# with MonoGame!

.NET 4 or Windows Phone 7.5

If you want to use this HttpClient on .NET 4 or Windows Phone 7.5, note you might get a compile error if you use async and await.

Cannot await System.Threading.Task<HttpRequestMessage>

This is because .Net 4.0 and Windows Phone 7.5 did not support the async/await keywords. In order to fix this add a reference to the Microsoft.Bcl.Async nuget package, which adds the support for async and await in .NET 4 and WP7.5. Here's a post with more details on how this backport works.

Related Links

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by SherWeb

Moving old apps from IIS6 to IIS8 and why Classic Mode exists

March 20, '13 Comments [12] Posted in ASP.NET | IIS
Sponsored By

I had an interesting emailed question today. Here's a paraphrased one sentence version of the question:

Why does an ASP.NET Runtime issue surface in IIS 8.0 Integrated Pool for an application we have run successfully on previous versions of IIS and classic mode in IIS 8.0 ?

It's less interesting that they've moved from IIS6 to IIS8 and more significant that they've moved from Classic Mode to the Integrated Mode pipeline.

So, the short answer. These pipelines are different and if you write an app expecting the behavior of one pipeline and the move it, your observed behavior will likely be different when you move it to the new pipeline.

IIS6 was released in Windows Server 2003 over 10 years ago. Apps used the ISAPI pipeline. By its nature everything in IIS6 is what we think of today as "classic mode," rather than the newer Integrated Pipeline in IIS7.

IIS6 with ASP.NET had two pipelines - the IIS unmanaged one and the managed ASP.NET one. Here's a simplistic but mostly accurate diagram (as is the case with diagrams):

IIS Classic Pipeline is two pipelines. One for IIS and one for ASP.NET

IIS7 and 8 were re-architected with the superior and faster Integrated Mode pipeline but retain "Classic" mode for compatibility.

IIS7 and up is one integrated pipeline

"Doctor, it hurts when I do that."

"Don't do that."

If you're moving an older app from IIS6 (which by definition was only "classic" mode) to IIS 8, the best near-term decision is to run in Classic Mode on IIS8.  Classic Mode is fully supported so you aren’t doing anything wrong by running in classic mode.  It's a mode that is there for a reason.

Think about your reasons and make an educated decision.

Remember that there can be incompatibilities and edge-case behavioral issues when ASP.NET apps attempt to move from the older ISAPI hosting model circa IIS 6 to the newer integrated mode that came in IIS7 without thinking about the ramifications.

I hope this helps.

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by SherWeb

Installing Sendy (a PHP app) on Windows Azure to send inexpensive newsletter email via Amazon SES

March 19, '13 Comments [28] Posted in Azure | Web Services
Sponsored By

TL;DR Summary

  • is a lovely and polished PHP app that uses Amazon's SES (Simple Email Service) to send email on the cheap.
  • It's easy to setup PHP apps on Windows Azure.
  • Azure Websites don't support mod_rewrite so you port the rules to a web.config. There's a great Sendy web.config for Windows in this post you are welcome to.
  • Sendy works well on Azure although they don't officially support Windows. I'm sure Sendy works great everywhere.
  • I'm now running my Newsletter on Windows Azure with mails sent my Amazon SES
  • Technical details below.

In search of a Cheaper Newsletter Solution

Why not a Rube Goldbergian solution? Well, it's not THAT bad. Here's the back story.

I started a little link blog newsletter a few months back, just for fun. You can subscribe at if you like. It's a low-traffic once-or-twice-a-month little ditty, mostly to share the things I've bumped into on the internet with friends.

I started at which is brilliant for little low-traffic newsletters. However, this one has picked up steam and now it's hit the maximum number of subscribers that TinyLetter allows.  TinyLetter is a front for MailChimp, so I look at their pricing. Looks like 5k-10k subscribers is $75 a month! Eek. Let me check SendGrid. They have a $79 a month option for up to 100k emails, but that's still $960 a year for a newsletter that sells nothing and serves no useful purpose. Yet.

I suppose I could charge people or get sponsors, but, meh, that takes work. I just want to send my list out. I could use my blog. Well, I do, but I like the high connectivity that a direct letter offers so I post the letter a few weeks letter so subscribers get the early scoop. Cleary folks dig it or they wouldn't sign up.

A twitter person told me about It's a PHP app that you host yourself. It fronts Amazon's Simple Email Service (SES) which is dirt cheap for email. The app is REALLY polished and just lovely. It's $59 to buy, but they said on their site "If you encounter problems, we will help you. If it doesn't work out, we'll refund you." That matters to me, so I bought it on the spot.

I know nothing about PHP, though, but I know the web, so I'm sure I can figure this out.

The Sendy site says this MASSIVE DISCLAIMER:

What are the requirements?

You need PHP & mySQL support on a Unix like server, eg. Linux on Apache. Almost all hosting companies support them. IT ISN'T SUPPORTED ON WINDOWS AND YOU'RE A FOOL TO TRY.

Ok, I admit, I added that part at the end myself. But, I don't really feel like spinning up a Linode as I have Azure credits I'm not using each month. I'm sure this will work. Plus, if it doesn't, I'll spin up a PHP app at any of a thousand little hosts for minimum money. If it works, it'll be nice to have everything in once place.

Making a Sendy PHP app instance on the Windows Azure Cloud

I go over to Azure and make a new website with a MySQL database:

Making a new website with a new MySQL DB

Next, inside of Azure I download the publish profile for my site. I also view the connection strings to the database because I'll need them to connect to the Sendy instance.

Connection Strings and publish profile

Then I download Sendy (after paying), unblock the zip and unzip it into a folder. I open the folder in WebMatrix. It installs PHP on my local machine so I can run it locally (even though I won't bother). I am using WebMatrix in this instance as a super easy way to publish to Azure directly.

The Sendy PHP app opens nicely in WebMatrix

I hit the Remote tab, then Settings to Import the publish profile I downloaded. Don't publish yet! I need to add a web.config since we are running this PHP app on Windows.

Sendy on Windows - .htaccess vs. web.config URL rewrite

I noticed there's an .htaccess file in my Sendy install. That means they've likely got mod_rewrite stuff going on to make the URLs pretty. Here's their file:

ErrorDocument 404 "[404 error] If you're seeing this error after install, check this thread on our forum for the fix:"

Options +FollowSymLinks
Options -Multiviews

RewriteEngine On
RewriteCond %{SCRIPT_FILENAME} !-d
RewriteCond %{SCRIPT_FILENAME} !-f
RewriteRule ^([a-zA-Z0-9-]+)$ $1.php [L]

# Link tracker
RewriteRule ^l/([a-zA-Z0-9/]+)$ l.php?i=$1 [L]

# Open tracker
RewriteRule ^t/([a-zA-Z0-9/]+)$ t.php?i=$1 [L]

# Web version
RewriteRule ^w/([a-zA-Z0-9/]+)$ w.php?i=$1 [L]

# unsubscribe
RewriteRule ^unsubscribe/(.*)$ unsubscribe.php?i=$1 [L]

# subscribe
RewriteRule ^subscribe/(.*)$ subscribe.php?i=$1 [L]

Windows Azure Websites with PHP doesn't (yet?) support mod_rewrite so I have to make web.config that does the same thing with UrlRewrite.  Fortunately importing mod_rewrite rules into web.config files has been straightforward for over 5 years in IIS.

Since I put Sendy in the root of a site, I don't have any sub-directories or paths. If it was in /sendy or something I might have to be a little more specific in my Regular Expressions below. You have to consider where Sendy is and where your web.config is. In this case, the easiest thing was the root and putting Sendy in its own site then adding this web.config. You'll notice is a pretty straight port.

<rule name="Sendy all" stopProcessing="true">
<match url="^([a-zA-Z0-9-]+)$" ignoreCase="true" />
<conditions logicalGrouping="MatchAll" trackAllCaptures="false">
<add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
<action type="Rewrite" url="{R:1}.php" appendQueryString="true" />

<rule name="Sendy: link tracker" stopProcessing="true">
<match url="^l/([a-zA-Z0-9/]+)$" ignoreCase="true" />
<action type="Rewrite" url="l.php?i={R:1}" appendQueryString="true" />

<rule name="Sendy: open tracker" stopProcessing="true">
<match url="^t/([a-zA-Z0-9/]+)$" ignoreCase="true" />
<action type="Rewrite" url="t.php?i={R:1}" appendQueryString="true" />

<rule name="Sendy: web version" stopProcessing="true">
<match url="^w/([a-zA-Z0-9/]+)$" ignoreCase="true" />
<action type="Rewrite" url="w.php?i={R:1}" appendQueryString="true" />

<rule name="Sendy: unsubscribe" stopProcessing="true">
<match url="^unsubscribe/(.*)$" ignoreCase="true" />
<action type="Rewrite" url="unsubscribe.php?i={R:1}" appendQueryString="true" />

<rule name="Sendy: subscribe" stopProcessing="true">
<match url="^subscribe/([a-zA-Z0-9/]+)$" ignoreCase="true" />
<action type="Rewrite" url="subscribe.php?i={R:1}" appendQueryString="true" />

I mentioned to the Azure Websites team that we should either directly support mod_rewrite or automatically turn existing ones into a format like this.

NOTE: This web.config is different, better, and much improved over the one that is mentioned on the Sendy Forums. It also works nicely and completely. The one on the forums is more than little iffy.

This web.config needs to be in the same folder as the Sendy app. I just made this file from WebMatrix directly.

Windows Azure Sendy Configuration

Next, change the includes/config.php and include the details on your application's location as well as your database connection details.

    // Configuration
    //path to your Sendy installation (without the trailing slash)
    define('APP_PATH', '');
    //database connection credentials
    $dbHost = ''; //mySQL Hostname
    $dbUser = 'ladaladalada'; //mySQL Username
    $dbPass = 'pardypary'; //mySQL Password
    $dbName = 'sendyDBName'; //mySQL Database Name
    //$dbPort = 3306; //mySQL port (only if you need to change it)
    //domain of cookie (99.99% chance you don't need to edit this)
    define('COOKIE_DOMAIN', '');

I chose to scale my site up to Shared mode so I could add a custom CNAME for the domain. I just went over to DNSimple where I host my DNS and added a CNAME for that pointed to my site. Then in Azure I hit Manage Domains and added this new subdomain.

NOTE: The domain name that you tell the Sendy guys must be the same one that's in your config.php and the same one you run under. I bought it for so it will only run there. Phrased differently, I couldn't get Sendy to run until the CNAME subdomain resolved correctly.

Finishing Sendy Installation

The Getting Started checklist at Sendy is REALLY well written. Follow it carefully.

I hit my URL and tell Sendy about my license key. You'll know the app ISN'T installed correctly if you can't see any CSS or images or you get 404s. That means the web.config (my fake mod_rewrite) isn't there.

I skipped Step 4 of their Getting Started as I believe it's already done for me, even though I don't plan on uploading any files. I setup Amazon SES and verified my email addresses. Getting this right, as well as bounce and complaint handling is super important so read carefully.

You'll want to make sure your Amazon SES emails are verified, and that Sendy has endpoints setup for complaints and bounces.

Gotcha: I had to make sure both the SES and SNS were in Amazon East 1.

Once these endpoints are setup, again as the Getting Started checklist at Sendy explains, you're ready to do some tests.

Setting up a Newsletter Campaign

The Sendy application is really nice, easy to use and easy to move around in.  I found it as easy as using TinyLetter, while it's clear there's more power underneath I have yet to tap into.


I was able to move my subscribers over with minimal trouble. I exported them from TinyLetter and imported them into Sendy. I wonder how long until the MySQL database gets big enough that I have to pay for it? Right now I'm still using the free MySQL database I created with my website.

Making a Subscribe Form

There isn't a Subscribe Form out of the box that I can find built-in to the Sendy app (can you?) so I made one at that just posts to the Sendy API subscribe endpoint. Details here, it's just an HTTP POST! You can integrate it with whatever you like. I just made a simple form, myself.

Sending mails!

Hopefully this will be a reasonable and economical solution for for the foreseeable future!

* SOME DISCLAIMERS AND DETAILS: The Sendy links are a referral link, but they don't know me over at Sendy. I just like them. Maybe I'll get some soda money if you buy it. Also, note again that installing Sendy on Windows is explicitly not supported until they say it is. Don't bother those nice people with your Windows questions. I am assuming that you are reasonably technical and are willing to fiddle. I installed it on Azure because I've already got 12 sites at Azure. You might have success on a Linux machine at Amazon or at Linode. Good luck!

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by SherWeb

Disclaimer: The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.