How to make a Hogwarts Sorting Hat using Windows Phone
In the world of Harry Potter, when a new student entered the Hogwarts School of Witchcraft and Wizardry, they are “sorted” by a Sorting Hat into one of four houses. The student puts the Sorting Hat on his/her head, and the Sorting Hat yells out the house to which the magical students will belong during their time at school.
When I was asked to keynote the Lansing Makers Week, I wanted to make something to show. After a few cups of coffee, a Sorting Hat seemed like a good idea. My design requirements were (in rough order of importance to me):
- I needed text-to-speech support for the Hat to call out the chosen house.
- I wanted the algorithm to choose the house to be deterministic, not random. Since I was demoing it at a keynote, I wanted to be able to control what house it would pick for me. (I mean, how embarrassing would it be to get sorted into Hufflepuff live on stage?)
- Optimally, I would prefer that the Hat use a male British accent.
- I was thinking about using the accelerometer to determine the house, but that wasn’t a hard-and-fast requirement.
I recently received a Galileo board and I’ve been looking for a good project to build with it, but the more I thought about it, it made sense to me to build a Windows Phone app. Here’s why:
- The Galileo board would need extra components and likely cloud integration. With Window Phone, I could do it all on-device in a single app, and Windows Phone already has support for text-to-speech and an accelerometer.
- The Galileo board seems more breakable than my phone. I wanted to let people try on the Hat, and they might drop it accidentally. I’ve dropped my phone a couple of times and it’s been okay. The Galileo board has many more exposed parts that could be damaged.
So, I built a Hogwarts Sorting Hat. A Windows Phone 8.1 app in a pocket in the hat uses an accelerometer to pick the house and text-to-speech to yell out the house name. Currently, the trigger is touching a button, but I may change that. I built in a 3-second pause for dramatic effect, and then the Hat announces your house.
To try this yourself:
- Download the Sorting Hat application from the Windows Phone Store at https://www.windowsphone.com/en-us/store/app/sorting-hat/ab9d9e52-5df7-40fd-aac8-a85119b0d053.
- Make a hat. I bought a witch’s hat for around $5 USD on https://amazon.com. I turned the hat inside out to make it look more straggly-looking (the Hogwarts Sorting Hat is supposed to be in rough shape) and used duct tape to make a pocket on the hat to hold the phone. I colored the grey duct tape black with a Sharpie permanent marker so it would blend in more nicely. I thought I would need to counterbalance the weight of the phone with a deck of cards or something on the back, but it was pretty stable without it. Here is what it looks like:
- Finally, put your Windows Phone in the Sorting Hat. For best results, turn your phone’s volume up all the way. Touch the button to activate it, wait 3 seconds, and the Sorting Hat will announce your house. There is a quick video demonstration at https://www.youtube.com/watch?v=kY0Igi-vcmc.
Now, some code samples! This is a pretty simple app, but I’ll highlight some of the interesting bits.
First, here is the voice initialization code. It contains logic to choose the voice (with a preference for a male British voice), and it sets up the dropdown ComboBox with all of the installed voices so you can select a different voice if you prefer.
private void ComboBoxVoiceChooser_Initialize()
{
// Get all of the installed voices.
var voices = Windows.Media.SpeechSynthesis.SpeechSynthesizer.AllVoices;
// Get the currently selected voice.
VoiceInformation currentVoice = this.synth.Voice;
VoiceInformation updatedVoice = null;
// First, try to use a British male voice.
updatedVoice = voices.FirstOrDefault(voice => voice.Language.Equals("en-GB", StringComparison.OrdinalIgnoreCase) && voice.Gender == VoiceGender.Male);
// If not available, try another English-speaking male voice.
if (updatedVoice == null)
{
updatedVoice = voices.FirstOrDefault(voice => voice.Gender == VoiceGender.Male && voice.Language.StartsWith("en"));
}
// Finally, fall back to any male voice.
if (updatedVoice == null && currentVoice.Gender == VoiceGender.Female)
{
updatedVoice = voices.FirstOrDefault(voice => voice.Gender == VoiceGender.Male);
}
// If we have updated the voice in the above logic, set the speech synthesizer to use it.
if (updatedVoice != null)
{
this.synth.Voice = updatedVoice;
}
// Add each voice to the ComboBox display
foreach (VoiceInformation voice in voices)
{
System.Diagnostics.Debug.WriteLine(voice.DisplayName + ": " + voice.Gender + "" + voice.Language);
ComboBoxItem item = new ComboBoxItem();
item.Name = voice.DisplayName;
item.Tag = voice;
item.Content = voice.DisplayName;
this.comboBoxVoiceChooser.Items.Add(item);
// Check to see if this is the current voice, so that we can set it to be selected
if (this.synth.Voice.Id == voice.Id)
{
item.IsSelected = true;
this.comboBoxVoiceChooser.SelectedItem = item;
}
}
}
Currently, when you press the big button in the UI, it starts a timer. After this dramatic 3-second pause, the below code is invoked to read the accelerometer values and choose a house based on that.
private void DisplayCurrentReading(object sender, object args)
{
AccelerometerReading reading = acc.GetCurrentReading();
if (reading != null)
{
timer.Stop(); // so it only grabs one reading
double x = reading.AccelerationX;
double y = reading.AccelerationY;
double z = reading.AccelerationZ;
ChooseHouse(x, y, z);
btnReady.Content = "Ready";
}
}
private async void ChooseHouse(double x, double y, double z)
{
// Logic:
// Strongest in -Z direction: Ravenclaw
// Strongest in Z direction: Gryffindor
// Strongest in X direction: Hufflepuff
// Strongest in -X direction: Slytherin
if (Math.Abs(z) >= Math.Abs(x)) // check if accleration is stronger in the X or Z direction
{
if (z < 0) house = "Ravenclaw";
else house = "Gryffindor";
}
else
{
if (x < 0) house = "Slytherin";
else house = "Hufflepuff";
}
await Speak(house);
}
Finally, here is the text-to-speech.
private async Task Speak(string textToSay)
{
SpeechSynthesisStream stream = await synth.SynthesizeTextToStreamAsync(textToSay);
MediaElement mediaElement = new MediaElement();
mediaElement.SetSource(stream, stream.ContentType);
mediaElement.Play();
}
At Lansing Maker Week, I was ridiculously excited to get sorted into Ravenclaw. :) I hope you enjoy too!