A simple Magic 8 Ball costume that actually works

A working Magic 8 Ball costume.

A working Magic 8 Ball costume.

I was keeping my usual self quite busy this past Halloween. I wanted to do an original costume that I created myself for wearing to weekend parties. Unfortunately, with my busy schedule, I put it off until pretty much the last couple of nights. With Halloween actually coming early during the week, I lost the luxury of the weekend to work away on it. So, having to do a rush, last-minute job after work, and wanting to do something novel, I thought of how the iPad has accelerometers and I could take advantage of it to make a working Magic 8 Ball costume! You shake the belly, you see the answer float away and back with a new random answer. Totally do-able, easy! Oh … and you don’t even need to be a software developer to do this. You don’t need a Mac nor Xcode to create the app, as this app is just a simple webpage and some JavaScript.

So here are the things that you will need:

  • An iPad (could also reasonably be done with other tablets)
  • A black shirt – preferably long sleeved – and at least one size larger than you normally wear (room for a bump, or just be pregnant)
  • Shirt paint of some sort – glow in the dark can be neat!
  • Eyelets – at least 8 of them – mine were 5/32″ (3.9mm)
  • An eyelet tool of equivalent size to your eyelets
  • Some black string, strong enough to hold your iPad to the shirt
  • A hammer and a pair of scissors
  • Something to use for the ball shape under the shirt – I duct-taped a pillow to myself
  • Optional: Stencils to help with the paint

The paint will need some time to dry, so make sure to do the painting at least a day before you intend to wear the costume. If you don’t have that kind of time, maybe check around for a local shop that can print direct to garment. I am not used to painting on shirts like this, but the paint did mostly work. The results weren’t coming out as great as I had hoped. But it was acceptable for what it was. If you’re not familiar with painting then I really recommend starting on this part at least a couple of days early to ensure that you have time to touch up any issues with the paint. Or again, just find another way to get that part taken care of.

The rest of the process is pretty straight forward. You’ll need to make sure that you have enough space above the Magic 8 Ball for the iPad to rest comfortably atop of it but not at your throat. I really have to suggest putting on the shirt, stuffing a pillow or something under it, and then resting the iPad on top so that you can get a feel for where the iPad needs to be and how far down the 8 needs to be.

I’ll get to the app in a moment, but I developed it first before attaching it to the shirt. Once you’re ready to attach the iPad to the shirt, test fit it again first! After you’re quite certain of where it needs to go, work on putting in your eyelets. You’ll want to use enough eyelets and thread that you feel comfortable that you won’t have your iPad come dropping off your shirt, crashing to the floor. I found that, with 8 eyelets in the shirt (2 per corner) with threads going across each side (bottom, top, left, right) and tied tightly, I had no issues with the iPad coming out. The threads going in various directions kept the iPad secure and in control, and the thick border on the iPad kept any of the threads from being in the way of any of the image surface of the iPad. Just make sure that the eyelets are back behind the iPad, forcing the string to come over and around the sides. This keeps pressure drawn tight and keeps the iPad from moving.

I’m sure there are other ways to secure the iPad even better to your shirt. This is just what I did in a pinch to get it done fast and cheap. If you have the time, try searching around for other ideas on how to strongly secure it. I take no responsibility if you drop your device and destroy it!

Now for the code; I decided it would be fastest to make a quick web based app and utilize JavaScript APIs for working with the accelerometer. I quickly prototyped and tested something to see what kind of animation speed I could get out of it. It initially was running slowly, but i was running an old iOS 5x version of the operating system and Safari browser. After upgrading to iOS 6, I saw a giant speed boost in the animation. So this convinced me that it would work, and easily so!

I have posted my code for the Magic 8 Ball iPad web page to a public GitHub repository. You can go snag the code there, fork it, whatever. It really just consists of:

  • index.html – The page that hosts the main HTML structure
  • magic8ball.appcache – used to make the iPad cache the page for offline usage; so that it still worked when I wasn’t on my home network
  • magic8ball.css – real simple styling
  • magic8ball.js – which contains the code that does the magic
  • triangle.png – the white triangle image used for the answer floater

The other files in the project are other sources that aren’t required, a NetBeans project or GitHub files.

The index.html page is very short. It mainly just includes the other needed items and lays out a basic structure to host the application in. There is also, commented out, a log DIV tag that was used for calculating the maths.

<!DOCTYPE html>
<html manifest="magic8ball.appcache">
    <head>
        <title>Magic 8 Ball</title>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <meta name="apple-mobile-web-app-capable" content="yes">
        <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js"></script>
        <script src="magic8ball.js" type="text/javascript"></script>
        <link href="magic8ball.css" type="text/css" rel="stylesheet"/>
    </head>
    <body>
        <div id="wrapper">
            <div id="answer_container">
                <span id="answer">Concentrate and ask again</span>
            </div>
            <!--div id="log"></div-->
        </div>
    </body>
</html>

In the magic8ball.js file, the list of possible answers is stored as a simple array of strings. You’ll find that you can add your own custom answer options by just adding to this array.

    var answers = [
        'It is certain',
        'It is decidedly so',
        'Without a doubt',
        'Yes - definitely',
        'You may rely on it',
        'As I see it, yes',
        'Most likely',
        'Outlook good',
        'Yes',
        'Signs point to yes',
        'Reply hazy, try again',
        'Ask again later',
        'Better not tell you now',
        'Cannot predict now',
        'Concentrate and ask again',
        "Don't count on it",
        'My reply is no',
        'My sources say no',
        'Outlook not so good',
        'Very doubtful'
    ];

The magic works by tracking the movement values given by the accelerometer in any direction. Because this is quick and dirty, I just sum up the different values and use that as the basis for determining the amount of force being applied to the Magic 8 Ball from it being shaken around or moved. I divide out the amount of time that the interval is updating me about (e.interval) so that we get a normalized value of sum-rate/interval. I ignore events that are just too low, so that it takes a bit of deliberate motion for it to do anything at all with it. As you can see below, I found that it was reasonable to ignore events of less than 1,000. If it is more than my threshold then I apply it to a force value that I track. This force is used to determine how quickly the answer is floating towards or away from being displayed.

    window.addEventListener('devicemotion', function (e) {
        var a = e.rotationRate.alpha;
        var b = e.rotationRate.beta;
        var g = e.rotationRate.gamma;
        var t = (Math.abs(a) + Math.abs(b) + Math.abs(g)) / e.interval;
        if(t < 1000) return;
        
        force -= (t / 500000);

The animation happens in a timer loop that is scheduled to fire every 33ms. This gives it roughly a 30fps display rate, plenty enough for this purpose, and hopefully not so much that it kills your battery. I give the floater a constant buoyancy value that gets added back to the force on each frame, so that eventually the force turns around and floats back up faster and faster. I also define a bottom, that is the maximum depth it can go down to, so that it won’t end up taking forever to float back up if you shake a lot or really hard. Real magic 8 balls don’t have infinite depth, and neither does mine. When it hits the bottom, we dramatically reduce the force and then roll a random new phrase to display. There is also a little bit of code so that when it floats back up, it can gently bounce off the screen, causing it to slightly float down and back up again. This was all to give a better presentation.

    window.setInterval(function(){
        if(1 == opacity && force == 0) return;
        
        const buoyancy = 0.001; // the positive up force constantly applied
        const bottom = -0.5; // going beyond 0 gives it some depth to disappear into for a bit
        
        force += buoyancy;
        opacity += force;
        
        if(opacity <= bottom)
        {
            opacity = bottom;
            if(force < 0)
                force = (force / 10) + buoyancy;
            $(answer).text(answers[Math.floor(Math.random()*answers.length)]);
        }
        if(opacity >= 1)
        {
            opacity = 1;
            force = (force > 0.06) ? -(force / 2) : 0; // bounce on hit at top
        }

        $(container).css({opacity:opacity});
    }, 33)

As you can see above, the appearance of it floating away is done by changing the opacity. This gives a similar effect to it disappearing behind the liquid that is in a real magic 8 ball. I tried playing around with adjusting the scale as well, but this lead to odd issues with the text resize not going smoothly, text wrapping changing, etc. So I dropped my efforts with that and left well enough alone. The code above is not a great example of how to do proper animation for games or anything serious. I wrote this in about an hour, the night before I wore the costume; it’s far from perfect. But it works for this flaky little costume effort!

So with the code in place, now you need to make it some sort of app for the iPad. How I did that was by first running a web server on my computer to host it, and then creating an app link from the webpage on the iPad. Run a web server however you’d like, I prefer installing the XAMPP package and using it, but that’s way overkill for this (albeit dead simple). You just need something to serve up the html, css, png, js, and appcache files. You also need to configure Apache to serve the correct MIME type for the .appcache file.

After you have your HTML server hosting the app, fire up the Safari browser on the iPad and go to it (it should be something http://192.168.0.101/magic8ball – depending on what your computer’s local IP address is and what folder you put the code in for your server). Now to make it an icon, just like a real app, you just hit the Share button (the little box and arrow icon) in Safari and choose the “Add to Home Screen” option. This will place it on your desktop and you can launch it. As long as your configuration for the appcache was correct, you will be able to end your web server or disconnect from your network on the ipad and still bring the “app” up.

Safari Add to Home Screen button

What you will also notice by running it this way, as an app icon from the home screen, is that you can no longer tell that it is just an HTML page in Safari. You won’t have the rest of the browser around it. It will run full screen, other than the time and other status bar info that is normally along the top. I recommend that you lock your screen rotation so that you don’t have it accidentally rotate on you while moving around in your costume!

Make sure to test that you can still use the app while disconnected from the network. If not, there is probably something wrong with your .appcache file. If you know what you’re doing, you can use the Chrome web browser to access your Magic 8 Ball webpage and see what content type the .appcache file is being transferred as. Remember, your .appcache file must be served as type text/cache-manifest for it to cache the app for use when the web server isn’t available (you’re not on your network, your server is stopped or off, etc).

One final note: If you decide to make changes to the app after you have pulled it up on your iOS 6 device, you will need to clear the cache to pick up the changes. They have made some changes on how to clear your cache, so just realize that you’ll have to do this each time you change it since Safari on iOS 6 is much more cache happy.

Source code: https://github.com/bspotswood/magic-8-ball

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s