Posts Tagged ‘Javascript’

Code Once, Adapt Everywhere

CORE


For the sake of not repeating myself I’m going to refer to the phrase “code once, run everywhere” as CORE from here on in. Who knows, maybe it’ll catch on. And on to the blog post…

So as you may have heard by now, I’ll be starting my new job as an Appcelerator platform evangelist on Monday. If you’ve read some of my past blog posts, you’ve probably noted that I’ve been pretty critical of cross platform mobile solutions. From a developer’s perspective, we are expecting the coveted CORE, but are often left wanting.

What you’ll quickly find in the world of mobile development is that cross platform does not always equal CORE.

Rather than bemoan the shortcomings of each mobile framework, I’d rather talk about something I heard say during the Appcelerator online training videos. He stated that Appcelerator does not aim to be a CORE technology, but instead a “code once, adapt everywhere” one. Not quite as sexy, but perhaps an even more intriguing philosophy. Let’s discuss why.

Web Based vs. Appcelerator


For a quick summary of how Appcelerator is fundamentally different than web-based cross platform mobile frameworks, read here.

Aside from near-native performance, what’s the biggest advantage of using Appcelerator over web based mobile frameworks like Phonegap or Rhomobile? Its ability to use the device’s native UI components. And no, I don’t mean it has UI components skinned to look like native components, like many of the web-based solutions. I mean it actually uses the platform’s native, performant UI in the app.

With native UI we can build apps that are indistinguishable from apps built with Objective-C or Java. The look, feel, performance, and behavior will be exactly what is expected of the given platform. Plus, we don’t have to build them ourselves.

To achieve this level of quality, though, you need to be willing to adapt your app, not just design for the lowest common denominator, as is often the mentality with CORE apps. Sure, you can use the iPhone’s slick Navigation Controller bar on all versions of your app, but is that what Android users are expecting? Nor would an Android Spinner be befitting of an iPhone app.

Spinner Navigation Bar

You see, in some cases, CORE apps come at the expense of the most important factor: the user experience.

Why Bother?


Many people, particularly proponents of web based mobile development, are of the opinion that native UI components are not necessary to deliver a high quality user experience. I agree, in certain circumstances. Games, novelty utilities (think Flashlight), and otherwise simple apps are good examples that probably don’t benefit much from a native experience.

In my opinion, though, it’s a necessity for more complex apps, particularly ones leveraging native APIs, to use the UI that is familiar to the device. They need to work in a simple, intuitive manner as mobile users can be quick on the trigger in deeming an app unfriendly. Those who have spent time developing for multiple platforms understand that the users of each platform have different expectations.

I don’t want a navigation bar in my Android app. I want my tabs at the bottom on iPhone, the top on Android. I want to press my menu button on Android to get my app’s options. I want my system buttons to look familiar. I want to pull to refresh on my iPhone.

Let me be clear that both Appcelerator and web-based frameworks have the ability to adapt their apps to supported platforms. And I don’t just mean churning out a basic app, I mean creating a high quality, native app. Depending on your point of view, however, one may be much more appealing than the other.

Attending to the UI (Web-based)


jQuery Mobile Sencha Touch

With web-based solutions, the app exists in a web view container. This means that you are effectively building a native app that consists of only a web view which hosts a web application. You have no native components with which to work. This leaves us with 2 options for building the UI of the app (super quick assessment coming):

  1. Use a 3rd party framework like jQuery Mobile or Sencha Touch
    • Pros

      • Lots of functionality and UI components
      • Speeds up development process
      • Some, like Sencha Touch, have a very native look to their components.
    • Cons
      • Web based framework UI is generally less responsive than ones created natively or with Appcelerator on mobile devices.
      • Additional learning curve for the added framework
      • You are even further removed from the native app. You now have a UI framework which sits on a native web view wrapper which then becomes a native app. Lots to know and lots of places for things to go wrong.
      • Frameworks like Sencha Touch are limited to webkit based web views (iOS, Android, BB 6.0+). This essentially removes the biggest advantage of web based frameworks, which is their compatibility.
  2. Create the UI yourself with HTML/CSS/JS
    • Pros

      • Totally customizable for any platform
      • Its easier to manage performance and UI inconsistencies when using code for only what you need to achieve
      • No additional learning curve beyond basic web dev and understanding your web based framework of choice.
    • Cons
      • Much slower to develop, as you have to build your UI from scratch. The styling of the UI to look native all falls on you or external resources you can find.
      • Even for seasoned web devs, managing cross platform mobile CSS can be a daunting task.
      • All cross browser inconsistencies become your job to address, unless you use a light JS framework like xuijs or zeptojs.

So as you can see, web based mobile development encounters many of the same issues that traditional web development does. And the problem is compounded when you are trying to make these web based solutions look, feel, perform, and behave natively.

Attending to the UI (Appcelerator)


Appcelerator apps are built differently. The extremely short version is that Appcelerator Javascript code is mapped to native symbols of the target platform. Any code that can’t be mapped to a native symbol is run through a Javascript interpreter. , CEO of Appcelerator, does a much better job of explaining it in this StackOverflow post.

What this means that there are no 3rd party tools or special code necessary to create totally native components. You want a button that has native appearance and behavior on both iPhone and Android?

var button = Ti.UI.createButton({title:'test button'});
Android button iPhone button

There you go, an Android and iPhone button respectively. How ’bout a table view populated with sample data?

var data = [{title:"Row 1"},{title:"Row 2"},{title:"Row 3"},{title:"Row 4"},{title:"Row 5"},{title:"Row 6"}];
var table = Titanium.UI.createTableView({data:data});
Android table view iPhone table view

Yep, it’s that simple. The iPhone table will even have the bounce scrolling users expect. You have the same simplicity that web based UI frameworks solutions provide, except you are additionally getting native look, feel, performance, and behavior. The components are even designable as you would expect them to be.

The one drawback to this simplicity is that without careful attention to your code, you can end up with a mess of interlacing UI and logic. Android has XML for layout, iOS has .nib/.xib files, web based solutions have HTML/CSS. Appcelerator, for the time being, relies solely on your Javascript code. Javascript MVC frameworks, like PureMVC, and attention to best practices (as mentioned in the online training videos) can mitigate this risk. There are even some vague rumblings of a more declarative syntax for UI design in the future…

Adaptation


So now that we know how UIs are built in both Appcelerator and web-based frameworks, how do we adapt them in such a way to deliver a native user experience? Despite the differences between the frameworks mentioned so far, the solution is fairly common among all frameworks.

While I will confidently say that Appcelerator has the abstraction that delivers the most familiar and device-specific experience, it too needs to account for usability that is not necessarily CORE. And even saying it is not CORE can be a bit of a misnomer as the same code base can be used by multiple platforms. It just requires the clever and judicious insertion of platform specific code facilitated by your mobile framework’s device identification APIs.

Let’s take a quick look at how Appcelerator identifies your device and can act on the information:

var osname = Titanium.Platform.osname;
if (osname == 'android') {
    // android specific code
} else if (osname == 'iphone') {
    // iphone specific code
} else if (osname == 'ipad') {
    // ipad specific code
}

For a more in depth example of how you can use this logic to create truly cross platform components and functionality, check out the 6 minute screencast “Forging Titanium: A Cross-Platform Navigation Controller.” Or just watch this:

And for reference, let’s look at PhoneGap’s adaptation method as well, just to show the similarities:

var platform = device.platform;
if platform == 'Android') {
    // android specific code
} else if platform == 'iPhone') {
    // iphone specific code
} else if platform == 'BlackBerry') {
    // blackberry specific code
}

Very similar indeed, but you need to consider the 2 prior “Attending to the UI” sections before calling them equal. Its the frequency with which you are required to apply this and other types of adaptation that affects the maintainability of your app as it grows.

Minimizing Adaptation


It doesn’t take an expert software engineer to see that conditional checks on the device’s platform throughout your code isn’t ideal. It gets harder to maintain the more conditionals you include. It becomes apparent that we need our development framework to do most of this work for us.

In the case of Appcelerator, the need for conditional adaptation is minimized by the fact that you can utilize native UI components. Look back at our examples of the buttons and table views. There was no conditional code, no special handling. You get system specific components with no extra effort.

You really only need conditional code when you want to leverage native components that don’t have an equivalent on your other target platforms. For example, if you haven’t already, check out the Cross-Platform Navigation Controller video above. It shows how you can use these conditionals to create navigation code that you can use seamlessly between iOS or Android.

Web based platforms also do a great job of creating an abstraction so that you don’t need to use conditionals for each supported platform. The problem, as discussed earlier, is that these abstractions don’t represent actual native components. They most often represent HTML/CSS/JS that are attempting to mimic native components. Worse yet, sometimes they are components that have no relation to existing native components, yet find themselves in native apps. As I said, this is a point of contention among mobile developers, and I’ll leave further discussion for the comments if necessary.

What web based frameworks can’t give you in native components, they provide in CSS, often applied dynamically via Javascript. The use of CSS is a double-edged sword. On one hand, you have a method of styling that not only allows you to skin your whole app, but also affords you the opportunity to gracefully degrade the styling based on the user’s device. This is why web based solutions typically support more platforms than ones like Appcelerator. Add all the bells and whistles like rounded corners, drop shadows, webkit transitions, etc… and if the device doesn’t support them, they will disappear without interrupting the user experience.

On the other hand, unless you are a CSS wizard with existing knowledge of CSS3 and how it applies to mobile, using it can be difficult. You can find yourself with mountains of CSS attempting to mimic components that are created with a single line of code in Appcelerator. For example, here’s a shiny iPhone button in pure CSS:

input[type=button] {
  font-family: "Helvetica Neue", Helvetica, sans-serif;
  font-size: 1.3em;
  font-weight: bold;
  width: 97%;
  height: 50px;
  border: 3px solid #282726;
  background: -webkit-gradient( linear, left top, left bottom, from(#e2e2e2), to(#8c8a88), color-stop(0.5, #acadae), color-stop(0.5, #82807e) );
  margin: 0 0 3px 0;
  text-shadow: 0px 1px 0 #cecece;
  -webkit-background-origin: padding-box;
  -webkit-background-clip: border-box;
  -webkit-border-radius: 8px;
}
 
input[type=button]:hover, input[type=button].cancel,
input[type=button]:active, input[type=button].cancel:active {
  color: #fff;
  text-shadow: none;
}
 
input[type=button]:hover, input[type=button].cancel:hover {
  background: -webkit-gradient( linear, left top, left bottom, from(#aaaee5), to(#10006d), color-stop(0.5, #1F3B97), color-stop(0.5, #081f6f) );
}
 
input[type=button].cancel {
  background: -webkit-gradient( linear, left top, left bottom, from(#5c5c5b), to(#1e1b16), color-stop(0.2, #1e1b16) );
  margin-top: 6px;
}

It does the job, but man, it is really cumbersome. Again, this is all a factor of wanting to create a native experience. Some will contest that it does not need to be this complex, that as long as the UI is uniform it does not need to conform to the native expectations. This mentality, though, is typically only held by those who back mobile frameworks that are incapable of delivering that native experience. As the local radio sports caster in Pittsburgh likes to say, “Not hatin’, just sayin’.”

Summary


You can’t beat web based mobile development for platform compatibility. Every mobile device has a browser that supports HTML/CSS/JS, right? You can create UIs that work on many platforms and degrade gracefully to handle lower end devices. Quality, usable apps are totally possible with these frameworks.

But the user doesn’t care how compatible your app is. They just want it to work, as they expect it to, on the device of their choice. In this respect, Appcelerator is unparalleled in the realm of cross platform solutions.

I have a strong suspicion that web based mobile technologies are only going to get better. I mean, let’s face it, the web isn’t going to be disappearing anytime soon. It will get faster, more functional, and closer to the expectations of the mobile user, just like desktop web browsers. And I, as a soon-to-be Appcelerator employee, welcome this.

As web based mobile development ups it game, so shall Appcelerator. Whether you’re an Appcelerator, web based, or native developer, it’s an exciting time… no matter what side of the fence you’re on.

Review: PhoneGap is Web-based, Appcelerator is Pure Javascript

What’s The Difference?

I’ve seen a lot of confusion out there on what the actual distinction is between PhoneGap and Appcelerator Titanium in terms of programming. Both state that they provide cross-platform mobile development frameworks driven by a Javascript core.  How different can they be?  Turns out, very.

The fundamental difference is that PhoneGap is a web based solution where Appcelerator Titanium is a pure Javascript API that creates native code.  As I’ve gone over the differences between these 2 in detail before, I’m going to very strictly stick to the topic of how their code works. Since people seem to love charts so much, here’s a quick review to show the divergence between the two frameworks:

  PhoneGap Appcelerator Titanium Notes
Javascript API PhoneGap’s API interacts as typical JS does in your web code. Appcelerator Titanium API is NOT web code, it is used to interact with native code.
Supports HTML5/CSS3 PhoneGap is a web app that runs in a native web browser view.
Supports Web Standards PhoneGap looks, feels, and develops like a standard web page. It is also subject to the same browser compatibility concerns.
Supports DOM based
JS libraries
JS libraries that reference the DOM, like jQuery, Prototype, or any of the new based libs will only work with Appcelerator Titanium webviews
Native Code Appcelerator Titanium creates a truly native app via a JS API that maps to native code.
Native UI/Performance Appcelerator Titanium performance is limited only by the device. PhoneGap’s is limited by the device’s web view.

What Does This Mean?

  • Web developers will have a much easier transition going to PhoneGap than they would Appcelerator Titanium.
  • Application developers without serious web development chops will likely gravitate towards Appcelerator Titanium. Why learn HTML, CSS, and Javascript when you can just learn Javascript?
  • Designer work will be tougher to integrate into an Appcelerator project as all the layouts and assets are done programmatically. PhoneGap, on the other hand is effectively web development, which designers have been working with for a very long time.
  • Appcelerator is always going to win on performance.
  • There will be an inevitable flood of web developers calling themselves mobile developers because they are familiar with PhoneGap. Beware.
  • Appcelerator has a much deeper and more complex integration with each mobile platform.
    • Pros: Native look, feel, and performance
    • Cons: Platform compatibility will be achieved more slowly. Much harder to “code once, deploy everywhere”.

Summary

The above is a hyper-condensed review of the whole story. As always, I encourage you to try both of these platforms. They both excel in many areas and offer unique features. Neither is the wrong choice, but depending on your scenario, one might be better suited than the other.

Are You Actually Saving Time With Mobile Frameworks?

The Short, Blunt Version

DISCLAIMER: Don’t leave me nasty comments if this is all you read.

Most of the available “cross platform” mobile frameworks are not as universally and uniformly compatible as they would be announced. There are inconsistencies that make “code once, run everywhere” very unlikely without at least some tweaking. The time spent fitting an app to one, universal mold can often force a sacrifice of even more time, quality, and usability.

Using HTML/CSS/JS for mobile development is seen as an advantage only to those who already have a great deal of experience with it. I say this from a pure language perspective as it carries the obvious advantage of providing portable, standards based code. If you don’t have this experience with Javascript, but do have the talent and/or diversity of background necessary to learn the languages to develop natively, do so.

I’m not saying you can’t build a multi-platform, high quality app with a mobile framework. I am saying that it is not quite as easy as it may appear on paper. This may seem counter-intuitive, but as the complexity of your project grows, the less effective a “code once, run everywhere” approach will be. That said, most mobile frameworks do a terrific job of making small to medium sized projects available to a large audience in a short amount of time.

For the quick feature comparison of the mobile frameworks I’ll talk about below, check out my prior post, “Appcelerator vs. PhoneGap vs. Adobe Air”.


Naivity

I approached the world of mobile development with the thought that a cross platform development framework was the one true path. I mean, how else could a single developer keep up with the plethora of mobile OSes, SDKs, and devices? And with that notion, I went forward in search of the silver bullet framework that would give me the coveted “code once, run everywhere” solution.

But as you may have expected, I haven’t found it. With my tunnel-vision focus on cross platform development I neglected to pay attention to the multitude of other key factors that can cost developers time. I also neglected the fact that deploying to every mobile device imaginable is almost never the goal of a particular app.

So what are the problems I’ve run into? How have they cost me time? Does this outweigh the potential time saved with the “code once, run everywhere”? Is “code once, run everywhere” really achievable for most apps? Let’s dig into these topics and see how they could impact your potential mobile development workflow.


Most frameworks are babies

Let’s take a look at the 3 frameworks I’ve looked at so far:

  • Appcelerator – mobile SDK started in 2009
  • PhoneGap – started in 2008
  • Adobe Air & Flex Hero – Air for Android and the iOS packager came out in 2010. Flex “Hero” is still beta.

As you can see, all of these frameworks are very young. And with that youth comes ambition, which in turn can lead to cut corners. The competion is fierce in a new and growing market. They know what they need to stand out: features. Stability and adherence to standards aren’t quite as eye-catching as an app that can leverage cool mobile APIs like geolocation and accelerometers.

It seems that in many cases the frameworks are spreading themselves thin at the expense of solid compatibility and stability.

How “Cross Platform” Are These Frameworks?

Here’s the mobile OSes aforementioned claim to support:

  • Appcelerator – iOS, Android, Blackberry (beta)
  • PhoneGap – iOS, Android, Blackberry, WebOS, Symbian
  • Adobe Air & Flex Hero – iOS, Android, Blackberry Playbook

Now here’s been my experience with each so far, your mileage may vary. I will try to keep it as high level as possible.

  • Appcelerator – iOS is their flagship. You can tell by the very Mac-ish look to their Titanium Developer project application that this is their focus. Appcelerator does iOs very well. Android on the other hand has been a bit frustrating. Many features and even core functionality are lagging behind in Android in terms of stability. Simple things like orientation lock and layouts work well in iOS but not so much in Android. This leads to inconsistent behavior between the two platforms, which is kinda the whole point of the framework.

    I have not yet tried Blackberry development, but if Android is a production release and has these issues, I worry what I may run into.

  • PhoneGapThis is the one framework I’ve experimented with so far that has a relatively uniform experience across all its supported platforms. The core functionality is there, operates the same, and with a consistent level of performance. Granted PhoneGap is an entirely contained web app and has no native UI component access, though most APIs are supported. This, though, should be the expected experience with a cross platform mobile app. Give me the same thing on every device to which I deploy!
  • Adobe Air & Flex Hero – Because these mobile apps run against the Adobe Air runtime you get a nearly identical experience across the supported platforms. Flex Hero comes with a wide array of mobile components for your use, though some are notable missing. The big hitch with consistency is performance.

    Blackberry Playbook development churns out great, performant content. Blackberry made available an AS3 SDK along with the ability to use Flex Hero. Because of this forethought and respect for the existing technology, Flash/Flex developers are really enjoying this platform and I think users will too.

    On Android, Air performs OK. There’s just enough lag on transitions and load time to drive you nuts as a developer, but likely won’t be a deal breaker for users.

    iOS seems to be a sore point for some developers. The performance is reported to be less than desirable when compared with the other supported platforms. Also, the Adobe iOS packager that allows you to deploy Adobe Air mobile applications to iOS currently only supports Air 2.0, leaving out some popular APIs like camera and video. We are all familiar with the battles between Apple and Adobe and why these performance issues exist. The users don’t care though and are likely to turn away from apps that seem sluggish as these might.

So as you can see, “cross platform” and “code once, run everywhere” aren’t quite as cut and dry as we would be led to believe. I’m not saying that frameworks should be dismissed for these reasons, but they simplicity of universal deployment and satisfactory user experience in many cases is overstated.

Do You Really Need To Support All Those Mobile OSes

I could ramble on about market share, market viability, and targeted deployment, but I’ll just let these few charts do the talking and let you render your own decision:

 

 

 

Javascript

I’m not here to start a holy war about whether or not Javascript is quality language. We all know the effectiveness of a programming language is largely determined by the person wielding it. But let’s be honest, Javascript is no Java, C#, or even Objective-C. And with the exception of Adobe Air and Flex Hero, almost all mobile frameworks use HTML/CSS/JS as their technology stack.

There’s no classes, no types, and the available IDEs and debugging facilities are weak in comparison to its native language SDK counterparts. As I hear many of my Flash/Flex friends saying from time to time, its a reminder of all that was wrong with the original version of Actionscript. jQuery and other frameworks make it more tolerable, but its still tough sledding when you are accustomed to a more full featured language.

Debugging

This is where you are ging to spend the majority of your life as a developer. If you don’t, you’re a better dev than I. And let me tell you something: debugging sucks in Javascript. Compound this with the fact that it is pretty much impossible to debug Javascript on a mobile device and you have a recipe for may, many lost hours chasing bugs.

Let’s look at Appcelerator and PhoneGap: They have no ability to debug on a device (I’ve had suggestions to try and JSConsole). Appcelerator often refers to a “debug” function you can use, but it is simply a logging capability called debugging. With PhoneGap the suggested method of debugging is running your code against the desktop version of Webkit and using its debugging capabilities. Neither of these situation is very ideal.

Adobe Air and Flex Hero debug the way you would expect an application to be debugged. Air can do full integrated, on-device debugging in a number of IDEs (Flash Builder and FDT spring to mind). This is what should be expected. While Javascript based mobile frameworks are using web technology, they can no longer expect developer to abide by the web’s workflow. When developing native apps we want application debugging, not web debugging.

Do not underestimate the impact that poor debugging capabilities will have on not only the timeline of your project, but also the quality. In both cases, you just might be better served going the native route.

2 Layers Of Potential Bugs

We all know that Android, iOS, Blackberry, and any other mobile OS you may encounter is going to have bugs and quirks in its SDK. The same could be said of all software. If you are using a mobile framework to develop against these SDKs, you now have 2 layers of potential bugs and quirks. It can often be difficult and time consuming to determine which one is the culprit… after you’ve ruled out your own code of course!

While PhoneGap and Adobe Air suffer from this to a degree, the impact is less on these two frameworks. This is because they make no attempt to handle native components. They rely on your own HTML/CSS/JS or Flex/AS3 components respectively to serve your UI. Some see this as a drawback and taking away from the native user experience. I, on the other hand, see it as a step towards true uniformity in your app. You need to make a decision: Do I want a native or uniform experience?

Appcelerator suffers more from this situation. By taking their unique approach of offering a Javascript SDK by which you can create native components, you are given the opportunity to create a native experience without having to engage in native development. This is very appealing to many developers. I know it was to me. The unfortunate side-effect of this is that you many times will not get exactly what you would expect out of a component. Due to the need to abstract functionality in order to make one code set apply to many different mobile OS components, you may find configurability difficult. Determining whether something is misbahving in your app because of your code, your framework, or your SDK becomes quite troubling.


Summary

I gave the summary at the beginning, since I don’t know if a lot of you have the stomach for this much negativity in one blog post.  There was a lot to say and detail, but the short, blunt intro summary is my current sentiment on the state of mobile development.

Just to be clear, its not all gloom and doom for mobile frameworks.  Check out this post I did a little while back detailing all the areas where these mobile frameworks can make your life much easier.  And these frameworks are getting better everyday.  PhoneGap is quickly closing the gap (pun) between itself and the other mobile frameworks in terms of features.  Appcelerator is making some major moves by creating a community of Titans to bring more notoriety to the cause.  They are also due to release the first public beta of their new IDE, a fusion between Titanium Developer and Aptana, sure to help the current state of Javascript development.  And Adobe is aggressively garnering support for its pre-release programs for Air, Flash Player, and Flash Builder.  I believe new features along with improved performance across all platforms is going to be the fruit of this labor.

So I haven’t given up yet and will continue to actively pursue these platforms.  I may wait for a bit more dust to settle though before engaging in a large scale project with any of them.  I’m sure folks braver than I are more than able to have success doing so right now.

Shit My Twitter Says: Xmas Edition

This is my list of favorites from Twitter for the past week. Be sure to take a look at not just the links, but the people posting up the great content. Some heavy hitters on the list this week.

Quake 3D Environment running in Flash

tweeted by:
Aerys shows off Minko, their tool for developing 3D applications for the Flash platform. This demo in particular is cool because they were members of the Adobe “Molehill” 3D API prerelease program.

Take a look to see the future of 3D in Flash, in a very familiar format. That’s right, Quake 3D right there in your browser.


AutoHotkey

tweeted by:
AutoHotkey is a free, open source utility to let you hotkey just about anything in Windows. Let’s you streamline and customize your Windows experience as much as you want. Here’s a brief list of hightlights:

  • Automate almost anything by sending keystrokes and mouse clicks.
  • You can write a mouse or keyboard macro by hand or use the macro recorder.
  • Expand abbreviations as you type them. For example, typing “btw” can automatically produce “by the way”.
  • Remap keys and buttons on your keyboard, joystick, and mouse.
  • Create custom data-entry forms, user interfaces, and menu bars.

Control Your Computer With Your Brain in AS3

tweeted by:

Sean Moore blogs about his exploits with NeuroSky’s Mindset headset, a USB device that actually reads brainwaves (EEG) as user input. It comes complete with a free, open source API for developing your own applications that use the headset. Also there’s a sort of brain emulator that allows you to develop against the API without even buying the headset.

Check out this blog post, and his previous one on the topic, to get a look at this incredibly cool technology.


Write high quality unit tested AS3 for fun and profit…

tweeted by:

A great assessment of unit testing resources, particularly as they relate the Actionscript 3 programming. The insights on the high points and pitfalls of test driven development are well worth the read.

“As with all code, complexity is easy. It is the default. Elegant simplicity is much more difficult.”


jQuery Mobile

tweeted by:

From the framework that made Javascript tolerable comes a version specially built for the unique needs of mobile developers. Enter jQuery Mobile.

My favorite part about this framework, other than the familiar syntax, is the upfront honesty about its compatibility. Check out the compatibility chart on the “Platforms” page for more details.


Rockbox

tweeted by:
Rockbox is an open source firmware for mp3 players, written from scratch. What this means is that you can load open source, totally customizable firmware onto the supported MP3 player of your choice. This includes the following (with many more unstable ports):

  • Apple iPods (lots of versions)
  • Archos
  • Cowon
  • iriver
  • Olympus
  • Packard Bell
  • SanDisk
  • Toshiba

So what can you do with it? You can customize the interface, use themes, watch movies, or even play Doom. Yes, Doom on your MP3 player. Badass.


Shit My Twitter Says #1

This is the first installment in what will hopefully be a regular occurrence on my blog. Its simply just a digest of interesting things from my twitter feed, complete with the source articles and the people behind them. Without further ado, let’s light this candle.

Incredible Javascript Performance metrics

tweeted by:
This was my favorite this week. A tremendously huge list of common Javascript tasks (conversions, HTML5, DOM access, etc…) broken into the different ways to perform them. Each of the methods is then tested millions of times against your current browser to tell you the most effecient way to perform them.

I know I am not doing this site justice. Go to it and check it out. This site should be a must have tool for any serious Javascript developer.

Mandelbrot Plotter

tweeted by:
This Flash-based Mandelbrot Plotter application allows you to create and save beautiful images of the Mandelbrot set. A must for you generative art junkies.

If you got a few spare minutes, I’d encourage you to check out the rest of flashandmath’s content as well. Some great visual content based just in code.

Blackberry giving out free playbooks to developers!

tweeted by:
Click the link for details. The short version is that if you build an approved app for the Blackberry App World by Feb 1st, 2011 you get a free PlayBook. Pretty sweet deal.

PlayBook App template using FDT and ANT

tweeted by:
Martin Rädlinger gives FDT users a head start on their road to a free Blackberry PlayBook with this extremely helpful app template.

Understanding Accelerometer Development (AS3)

tweeted by:
A great breakdown of how to use the accelerometer in mobile development, as it relates to AS3. Simple and concise with source code to go with it.

Top 50 Programming Quotes Of All Time

tweeted by:
Nothing like a little truth wrapped up in wit and sarcasm. Especially for us geeks. Enjoy some of these pearls of wisdom from a few programming greats you may recognize.

List of the Best Animation/Tweening Packages for AS3

tweeted by:
A seemingly comprehensive list of available tweening and animation libraries available for AS3. It includes who made it, where to find it, and the pros and cons of each. This is great place to start for a Flash newbie or a terrific reference for a seasoned pro.

Using OAuth with Adobe Air and Flex

tweeted by:
Need to access a 3rd party web API in your Adobe Air application. Check out this extremely useful blog post from Christophe Coenraets that includes a description, application, and full source code.

Blackberry PlayBook will support C++ extensions for Adobe Air

tweeted by:
This isn’t some Adobe Labs Alchemy magic, Blackberry will have a legit part of their mobile development SDK that will support C++ extension use by Adobe Air. This is the best of both worlds: The flexibility and ease of mobile development with Adobe Air and the power and efficiency of native SDK development. Blackberry really is making some strong moves to engage the developer community with its latest mobile offerings.

Create Your Own QR Code

The Generator

Text for QR Code:

The Overview

QR codes are basically just bar codes on steroids. They allow you to encode up to 4,296 characters in a format that can be read by most modern bar code scanning devices. More specifically, QR codes make it much simpler to direct smart phone users to your website (or anywhere else you want).

So why do you want to do this? Here’s a few reasons that range in practicality:

  • Direct mobile phones to personally hosted mobile applications (no need for market fees)
  • Direct users from physical storefronts or magazine articles to your website
  • Leave encoded messages for mobile users
  • To be super cool and trendy

The Code

Here’s the Google Chart API URL you hit in order to create your own QR code. Pop it into a browser and it will return your image. There are more optional criteria you can send, which are detailed in the . Be sure to change the sections highlighted in RED to the values that fit your needs.

http://chart.apis.google.com/chart?cht=qr&chs=HEIGHTxWIDTH&chl=YOURTEXT

Now let’s say you wanted to copy my website and have your own generator. It’s pretty simple. Here’s the tiny bit of HTML and javascript I used to make it happen:

<table>
  <tr>
    <td>
      <strong>Text for QR Code:</strong> 
      <form onsubmit="return false;">
        <input type="text" id="qrvalue" value="https://savagelook.com" style="width:250px;"/>
        <button onclick="document.getElementById('qrimg').src = 'http://chart.apis.google.com/chart?cht=qr&chs=150x150&chl=' + document.getElementById('qrvalue').value;">Encode</button>
      </form>
    </td>
    <td>
      <img id="qrimg" src="http://chart.apis.google.com/chart?cht=qr&chs=150x150&chl=https://savagelook.com"/>
    </td>
  </tr>
</table>

And here’s another version from Eric Harrison with no tables

<div style="padding:15px 50px;"> 
  <img id="qrimg" src="http://chart.apis.google.com/chart?cht=qr&chs=150x150&chl=https://savagelook.com" style="float:left;margin-right:25px;" /> 
  <form onsubmit="return false;"> 
    <input type="text" id="qrvalue" value="https://savagelook.com" style="width:60%;font-size:125%;" /><br /> 
    <input type="button" style="padding:5px;font-size:125%;margin-top:10px;" onclick="document.getElementById('qrimg').src = 'http://chart.apis.google.com/chart?cht=qr&chs=150x150&chl=' + document.getElementById('qrvalue').value;" value="Encode" /> 
  </form> 
  <br style="clear:both;" /> 
</div>

As you can see, its not rocket science, but it sure can add a little techie flare. Have fun and be sure to let me know if you add a QR coding to your geek repetoire!

Box2D JS – Physics in HTML5 & Javascript Guide

Box2D JS Hello World

I hate to do it, but I highly recommend the Google Chrome browser for this demo. In other browsers it may take a while to load and runs slower.

→ Click on the demo above or here, then right click to view source
→ Download the Box2D JS library
→ Download the concatenated version (~350KB)
→ Download the minified version (~170 KB)

The Overview

Some of you might remember the Box2DFlashAS3 demo I did a while ago. Well here’s its HTML5 counter-part, with the help of Box2D JS. Box2D is the Javascript port of the Box2DFlashAS3 library, which in turn is a port of the Box2D C++ library. Put simply, this library allows you to apply 2 dimensional physics to objects on your HTML5 canvas element. Just click anywhere on the demo above to see what I mean.

The code behind this is a condensed, easier-to-follow version of the demo code available by viewing the page source at the Box2D JS site. I personally found it a little tough to follow initially and really wanted to avoid the large amount of individual includes necessary to get it working. To that end I created the concatenated and minified versions downloadable above. So basically the includes necessary go from this:

 
<script src="lib/prototype-1.6.0.2.js">script> 
 
 
<script src='js/box2d/common/b2Settings.js'>script> 
<script src='js/box2d/common/math/b2Vec2.js'>script> 
<script src='js/box2d/common/math/b2Mat22.js'>script> 
<script src='js/box2d/common/math/b2Math.js'>script> 
<script src='js/box2d/collision/b2AABB.js'>script> 
<script src='js/box2d/collision/b2Bound.js'>script> 
<script src='js/box2d/collision/b2BoundValues.js'>script> 
<script src='js/box2d/collision/b2Pair.js'>script> 
<script src='js/box2d/collision/b2PairCallback.js'>script> 
<script src='js/box2d/collision/b2BufferedPair.js'>script> 
<script src='js/box2d/collision/b2PairManager.js'>script> 
<script src='js/box2d/collision/b2BroadPhase.js'>script> 
<script src='js/box2d/collision/b2Collision.js'>script> 
<script src='js/box2d/collision/Features.js'>script> 
<script src='js/box2d/collision/b2ContactID.js'>script> 
<script src='js/box2d/collision/b2ContactPoint.js'>script> 
<script src='js/box2d/collision/b2Distance.js'>script> 
<script src='js/box2d/collision/b2Manifold.js'>script> 
<script src='js/box2d/collision/b2OBB.js'>script> 
<script src='js/box2d/collision/b2Proxy.js'>script> 
<script src='js/box2d/collision/ClipVertex.js'>script> 
<script src='js/box2d/collision/shapes/b2Shape.js'>script> 
<script src='js/box2d/collision/shapes/b2ShapeDef.js'>script> 
<script src='js/box2d/collision/shapes/b2BoxDef.js'>script> 
<script src='js/box2d/collision/shapes/b2CircleDef.js'>script> 
<script src='js/box2d/collision/shapes/b2CircleShape.js'>script> 
<script src='js/box2d/collision/shapes/b2MassData.js'>script> 
<script src='js/box2d/collision/shapes/b2PolyDef.js'>script> 
<script src='js/box2d/collision/shapes/b2PolyShape.js'>script> 
<script src='js/box2d/dynamics/b2Body.js'>script> 
<script src='js/box2d/dynamics/b2BodyDef.js'>script> 
<script src='js/box2d/dynamics/b2CollisionFilter.js'>script> 
<script src='js/box2d/dynamics/b2Island.js'>script> 
<script src='js/box2d/dynamics/b2TimeStep.js'>script> 
<script src='js/box2d/dynamics/contacts/b2ContactNode.js'>script> 
<script src='js/box2d/dynamics/contacts/b2Contact.js'>script> 
<script src='js/box2d/dynamics/contacts/b2ContactConstraint.js'>script> 
<script src='js/box2d/dynamics/contacts/b2ContactConstraintPoint.js'>script> 
<script src='js/box2d/dynamics/contacts/b2ContactRegister.js'>script> 
<script src='js/box2d/dynamics/contacts/b2ContactSolver.js'>script> 
<script src='js/box2d/dynamics/contacts/b2CircleContact.js'>script> 
<script src='js/box2d/dynamics/contacts/b2Conservative.js'>script> 
<script src='js/box2d/dynamics/contacts/b2NullContact.js'>script> 
<script src='js/box2d/dynamics/contacts/b2PolyAndCircleContact.js'>script> 
<script src='js/box2d/dynamics/contacts/b2PolyContact.js'>script> 
<script src='js/box2d/dynamics/b2ContactManager.js'>script> 
<script src='js/box2d/dynamics/b2World.js'>script> 
<script src='js/box2d/dynamics/b2WorldListener.js'>script> 
<script src='js/box2d/dynamics/joints/b2JointNode.js'>script> 
<script src='js/box2d/dynamics/joints/b2Joint.js'>script> 
<script src='js/box2d/dynamics/joints/b2JointDef.js'>script> 
<script src='js/box2d/dynamics/joints/b2DistanceJoint.js'>script> 
<script src='js/box2d/dynamics/joints/b2DistanceJointDef.js'>script> 
<script src='js/box2d/dynamics/joints/b2Jacobian.js'>script> 
<script src='js/box2d/dynamics/joints/b2GearJoint.js'>script> 
<script src='js/box2d/dynamics/joints/b2GearJointDef.js'>script> 
<script src='js/box2d/dynamics/joints/b2MouseJoint.js'>script> 
<script src='js/box2d/dynamics/joints/b2MouseJointDef.js'>script> 
<script src='js/box2d/dynamics/joints/b2PrismaticJoint.js'>script> 
<script src='js/box2d/dynamics/joints/b2PrismaticJointDef.js'>script> 
<script src='js/box2d/dynamics/joints/b2PulleyJoint.js'>script> 
<script src='js/box2d/dynamics/joints/b2PulleyJointDef.js'>script> 
<script src='js/box2d/dynamics/joints/b2RevoluteJoint.js'>script> 
<script src='js/box2d/dynamics/joints/b2RevoluteJointDef.js'>script>

to this:

 
<script src="lib/prototype-1.6.0.2.js">script> 
<script src="box2djs.min.js">script>

You still need to download Box2D JS for its dependencies on Prototype and excanvas, but using my single file versions of the library will make getting started much cleaner and easier. OK, enough of the back story, on to the code.

The Code


    
        
        SavageLook.com - Box2D JS Hello World
        
        
        
 
        <script type="text/javascript">
        var world;
        var ctx;
        var canvasWidth;
        var canvasHeight;
        var canvasTop;
        var canvasLeft;
 
        function drawWorld(world, context) {
                for (var j = world.m_jointList; j; j = j.m_next) {
                        drawJoint(j, context);
                }
                for (var b = world.m_bodyList; b; b = b.m_next) {
                        for (var s = b.GetShapeList(); s != null; s = s.GetNext()) {
                                drawShape(s, context);
                        }
                }
 
                ctx.font = 'bold 18px arial';
                ctx.textAlign = 'center';
                ctx.fillStyle = '#000000';
                ctx.fillText("Click the screen to add more objects", 400, 20);
                ctx.font = 'bold 14px arial';
                ctx.fillText("Performance will vary by browser", 400, 40);
 
        }
 
        function drawJoint(joint, context) {
                var b1 = joint.m_body1;
                var b2 = joint.m_body2;
                var x1 = b1.m_position;
                var x2 = b2.m_position;
                var p1 = joint.GetAnchor1();
                var p2 = joint.GetAnchor2();
                context.strokeStyle = '#00eeee';
                context.beginPath();
                switch (joint.m_type) {
                case b2Joint.e_distanceJoint:
                        context.moveTo(p1.x, p1.y);
                        context.lineTo(p2.x, p2.y);
                        break;
 
                case b2Joint.e_pulleyJoint:
                        // TODO
                        break;
 
                default:
                        if (b1 == world.m_groundBody) {
                                context.moveTo(p1.x, p1.y);
                                context.lineTo(x2.x, x2.y);
                        }
                        else if (b2 == world.m_groundBody) {
                                context.moveTo(p1.x, p1.y);
                                context.lineTo(x1.x, x1.y);
                        }
                        else {
                                context.moveTo(x1.x, x1.y);
                                context.lineTo(p1.x, p1.y);
                                context.lineTo(x2.x, x2.y);
                                context.lineTo(p2.x, p2.y);
                        }
                        break;
                }
                context.stroke();
        }
 
        function drawShape(shape, context) {
                context.strokeStyle = '#ffffff';
                if (shape.density == 1.0) {
                        context.fillStyle = "red";
                } else {
                        context.fillStyle = "black";
                }
                context.beginPath();
                switch (shape.m_type) {
                case b2Shape.e_circleShape:
                        {
                                var circle = shape;
                                var pos = circle.m_position;
                                var r = circle.m_radius;
                                var segments = 16.0;
                                var theta = 0.0;
                                var dtheta = 2.0 * Math.PI / segments;
 
                                // draw circle
                                context.moveTo(pos.x + r, pos.y);
                                for (var i = 0; i < segments; i++) {
                                        var d = new b2Vec2(r * Math.cos(theta), r * Math.sin(theta));
                                        var v = b2Math.AddVV(pos, d);
                                        context.lineTo(v.x, v.y);
                                        theta += dtheta;
                                }
                                context.lineTo(pos.x + r, pos.y);
 
                                // draw radius
                                context.moveTo(pos.x, pos.y);
                                var ax = circle.m_R.col1;
                                var pos2 = new b2Vec2(pos.x + r * ax.x, pos.y + r * ax.y);
                                context.lineTo(pos2.x, pos2.y);
                        }
                        break;
                case b2Shape.e_polyShape:
                        {
                                var poly = shape;
                                var tV = b2Math.AddVV(poly.m_position, b2Math.b2MulMV(poly.m_R, poly.m_vertices[0]));
                                context.moveTo(tV.x, tV.y);
                                for (var i = 0; i < poly.m_vertexCount; i++) {
                                        var v = b2Math.AddVV(poly.m_position, b2Math.b2MulMV(poly.m_R, poly.m_vertices[i]));
                                        context.lineTo(v.x, v.y);
                                }
                                context.lineTo(tV.x, tV.y);
                        }
                        break;
                }
                context.fill();
                context.stroke();
        }
 
        function createWorld() {
                var worldAABB = new b2AABB();
                worldAABB.minVertex.Set(-1000, -1000);
                worldAABB.maxVertex.Set(1000, 1000);
                var gravity = new b2Vec2(0, 300);
                var doSleep = true;
                world = new b2World(worldAABB, gravity, doSleep);
                createGround(world);
                return world;
        }             
 
        function createGround(world) {
                var groundSd = new b2BoxDef();
                groundSd.extents.Set(400, 30);
                groundSd.restitution = 0.0;
                var groundBd = new b2BodyDef();
                groundBd.AddShape(groundSd);
                groundBd.position.Set(400, 470);
                return world.CreateBody(groundBd);
        }
 
        function createBall(world, x, y) {
                var ballSd = new b2CircleDef();
                ballSd.density = 1.0;
                ballSd.radius = 20;
                ballSd.restitution = 0.5;
                ballSd.friction = 0.5;
                var ballBd = new b2BodyDef();
                ballBd.AddShape(ballSd);
                ballBd.position.Set(x,y);
                return world.CreateBody(ballBd);
        }
 
        function createHelloWorld() {
                // H
                createBox(world, 50, 420, 10, 20, false);
                createBox(world, 90, 420, 10, 20, false);
                createBox(world, 70, 395, 30, 5, false);
                createBox(world, 50, 370, 10, 20, false);
                createBox(world, 90, 370, 10, 20, false);
 
                // E
                createBox(world, 140, 435, 30, 5, false);
                createBox(world, 120, 420, 10, 10, false);
                createBox(world, 130, 405, 20, 5, false);
                createBox(world, 120, 390, 10, 10, false);
                createBox(world, 140, 375, 30, 5, true);
 
                // L
                createBox(world, 200, 435, 20, 5, false);
                createBox(world, 185, 400, 5, 30, false);
 
                // L
                createBox(world, 250, 435, 20, 5, false);
                createBox(world, 235, 400, 5, 30, false);
 
                // O
                createBox(world, 300, 435, 20, 5, false);
                createBox(world, 285, 405, 5, 25, false);
                createBox(world, 315, 405, 5, 25, false);
                createBox(world, 300, 375, 20, 5, false);
 
                // W
                createBox(world, 390, 435, 40, 5, false);
                createBox(world, 360, 390, 10, 40, false);
                createBox(world, 420, 390, 10, 40, false);
                createBox(world, 390, 415, 5, 15, false);
 
                // O
                createBox(world, 460, 435, 20, 5, false);
                createBox(world, 445, 405, 5, 25, false);
                createBox(world, 475, 405, 5, 25, false);
                createBox(world, 460, 375, 20, 5, false);
 
                // R
                createBox(world, 495, 410, 5, 30, false);
                createBox(world, 518, 425, 5, 15, false);
                createBox(world, 515, 405, 15, 5, false);
                createBox(world, 525, 390, 5, 10, false);
                createBox(world, 510, 375, 20, 5, false);
 
                // L
                createBox(world, 560, 435, 20, 5, false);
                createBox(world, 545, 400, 5, 30, false);
 
                // D
                createBox(world, 610, 435, 20, 5, false);
                createBox(world, 595, 405, 5, 25, false);
                createBox(world, 625, 405, 5, 25, false);
                createBox(world, 610, 375, 20, 5, false);
 
                // !
                createBox(world, 650, 430, 10, 10, false);
                createBox(world, 650, 380, 10, 40, false);
        }
 
        function createBox(world, x, y, width, height, fixed) {
                if (typeof(fixed) == 'undefined') fixed = true;
                var boxSd = new b2BoxDef();
                if (!fixed) boxSd.density = 1.0; 
                boxSd.restitution = 0.0;
                boxSd.friction = 1.0;
                boxSd.extents.Set(width, height);
                var boxBd = new b2BodyDef();
                boxBd.AddShape(boxSd);
                boxBd.position.Set(x,y);
                return world.CreateBody(boxBd);
        }
 
        function step(cnt) {
                var stepping = false;
                var timeStep = 1.0/60;
                var iteration = 1;
                world.Step(timeStep, iteration);
                ctx.clearRect(0, 0, canvasWidth, canvasHeight);
                drawWorld(world, ctx);
                setTimeout('step(' + (cnt || 0) + ')', 10);
        }
 
        // main entry point
        Event.observe(window, 'load', function() {
                world = createWorld();
                ctx = $('canvas').getContext('2d');
                var canvasElm = $('canvas');
                canvasWidth = parseInt(canvasElm.width);
                canvasHeight = parseInt(canvasElm.height);
                canvasTop = parseInt(canvasElm.style.top);
                canvasLeft = parseInt(canvasElm.style.left);
 
                createHelloWorld();
 
                Event.observe('canvas', 'click', function(e) {
                                if (Math.random() > 0.5) {
                                        //createBox(world, Event.pointerX(e), Event.pointerY(e), 10, 10, false);
                                        createBox(world, e.clientX, e.clientY, 10, 10, false);
                                } else {
                                        createBall(world, Event.pointerX(e), Event.pointerY(e));
                                }
                });
                step();
        });
        script>
    
    
        
    
 

The Breakdown

DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
        <title>SavageLook.com - Box2D JS Hello Worldtitle>
        
        <script src="lib/prototype-1.6.0.2.js">script>
        <script src="box2djs.min.js">script>

We start by including the scripts necessary to make Box2D JS work. In order, we need excanvas (included in the Box2D JS distribution) in order to account for the fact that all current released version of Internet Explorer do not support the HTML canvas element. Next we include the Prototype Javascript framework, also included with Box2D JS. Finally we include my minified version of the library. Now we can get started building physics into our canvas element.


<script type="text/javascript">
        var world;
        var ctx;
        var canvasWidth;
        var canvasHeight;
        var canvasTop;
        var canvasLeft;
 
        function drawWorld(world, context) {
                for (var j = world.m_jointList; j; j = j.m_next) {
                        drawJoint(j, context);
                }
                for (var b = world.m_bodyList; b; b = b.m_next) {
                        for (var s = b.GetShapeList(); s != null; s = s.GetNext()) {
                                drawShape(s, context);
                        }
                }
 
                ctx.font = 'bold 18px arial';
                ctx.textAlign = 'center';
                ctx.fillStyle = '#000000';
                ctx.fillText("Click the screen to add more objects", 400, 20);
                ctx.font = 'bold 14px arial';
                ctx.fillText("Performance will vary by browser", 400, 40);
 
        }

Here we declare our global variables that define the “world” the physics exist in and the context, dimensions, and position of the canvas element.

Also we have our drawWorld() function that will, as the name implies, draw the shapes and joints that compose the Box2D JS world. Each of these objects is iterated through and drawn individually. They are added to the world with the createBody() function. An important thing to note is that in the case of this demo, drawWorld() will be called with each “step”. Think of your canvas as an animation and each call to drawWorld() as a frame. Should be a simple concept for you Flash devs out there ;)


        function drawJoint(joint, context) {
                var b1 = joint.m_body1;
                var b2 = joint.m_body2;
                var x1 = b1.m_position;
                var x2 = b2.m_position;
                var p1 = joint.GetAnchor1();
                var p2 = joint.GetAnchor2();
                context.strokeStyle = '#00eeee';
                context.beginPath();
                switch (joint.m_type) {
                case b2Joint.e_distanceJoint:
                        context.moveTo(p1.x, p1.y);
                        context.lineTo(p2.x, p2.y);
                        break;
 
                case b2Joint.e_pulleyJoint:
                        // TODO
                        break;
 
                default:
                        if (b1 == world.m_groundBody) {
                                context.moveTo(p1.x, p1.y);
                                context.lineTo(x2.x, x2.y);
                        }
                        else if (b2 == world.m_groundBody) {
                                context.moveTo(p1.x, p1.y);
                                context.lineTo(x1.x, x1.y);
                        }
                        else {
                                context.moveTo(x1.x, x1.y);
                                context.lineTo(p1.x, p1.y);
                                context.lineTo(x2.x, x2.y);
                                context.lineTo(p2.x, p2.y);
                        }
                        break;
                }
                context.stroke();
        }
 
        function drawShape(shape, context) {
                context.strokeStyle = '#ffffff';
                if (shape.density == 1.0) {
                        context.fillStyle = "red";
                } else {
                        context.fillStyle = "black";
                }
                context.beginPath();
                switch (shape.m_type) {
                case b2Shape.e_circleShape:
                        {
                                var circle = shape;
                                var pos = circle.m_position;
                                var r = circle.m_radius;
                                var segments = 16.0;
                                var theta = 0.0;
                                var dtheta = 2.0 * Math.PI / segments;
 
                                // draw circle
                                context.moveTo(pos.x + r, pos.y);
                                for (var i = 0; i < segments; i++) {
                                        var d = new b2Vec2(r * Math.cos(theta), r * Math.sin(theta));
                                        var v = b2Math.AddVV(pos, d);
                                        context.lineTo(v.x, v.y);
                                        theta += dtheta;
                                }
                                context.lineTo(pos.x + r, pos.y);
 
                                // draw radius
                                context.moveTo(pos.x, pos.y);
                                var ax = circle.m_R.col1;
                                var pos2 = new b2Vec2(pos.x + r * ax.x, pos.y + r * ax.y);
                                context.lineTo(pos2.x, pos2.y);
                        }
                        break;
                case b2Shape.e_polyShape:
                        {
                                var poly = shape;
                                var tV = b2Math.AddVV(poly.m_position, b2Math.b2MulMV(poly.m_R, poly.m_vertices[0]));
                                context.moveTo(tV.x, tV.y);
                                for (var i = 0; i < poly.m_vertexCount; i++) {
                                        var v = b2Math.AddVV(poly.m_position, b2Math.b2MulMV(poly.m_R, poly.m_vertices[i]));
                                        context.lineTo(v.x, v.y);
                                }
                                context.lineTo(tV.x, tV.y);
                        }
                        break;
                }
                context.fill();
                context.stroke();
        }

Above are the body drawing functions: drawJoint() and drawShape(). I’m not going to get into much detail here, but just know that these functions are responsible for taking the physics bodies and giving them a visual representation. They make calls to the canvas context 2D drawing API to create the shapes that give us our falling rectangles and circles. This is the simplest case and requires no external dependencies. In practical cases though, you will more likely find images or other more clever uses like this one:


        function createWorld() {
                var worldAABB = new b2AABB();
                worldAABB.minVertex.Set(-1000, -1000);
                worldAABB.maxVertex.Set(1000, 1000);
                var gravity = new b2Vec2(0, 300);
                var doSleep = true;
                world = new b2World(worldAABB, gravity, doSleep);
                createGround(world);
                return world;
        }

This is where the Box2D JS physics world is created. We define the bounds for the AABB physics with the minVertex and maxVertex properties and set the vector of gravity. After we create those we apply them to a newly created world and create the ground that will be the base of our scene.


        function createGround(world) {
                var groundSd = new b2BoxDef();
                groundSd.extents.Set(400, 30);
                groundSd.restitution = 0.0;
                var groundBd = new b2BodyDef();
                groundBd.AddShape(groundSd);
                groundBd.position.Set(400, 470);
                return world.CreateBody(groundBd);
        }
 
        function createBall(world, x, y) {
                var ballSd = new b2CircleDef();
                ballSd.density = 1.0;
                ballSd.radius = 20;
                ballSd.restitution = 0.5;
                ballSd.friction = 0.5;
                var ballBd = new b2BodyDef();
                ballBd.AddShape(ballSd);
                ballBd.position.Set(x,y);
                return world.CreateBody(ballBd);
        }
 
        function createBox(world, x, y, width, height, fixed) {
                if (typeof(fixed) == 'undefined') fixed = true;
                var boxSd = new b2BoxDef();
                if (!fixed) boxSd.density = 1.0; 
                boxSd.restitution = 0.0;
                boxSd.friction = 1.0;
                boxSd.extents.Set(width, height);
                var boxBd = new b2BodyDef();
                boxBd.AddShape(boxSd);
                boxBd.position.Set(x,y);
                return world.CreateBody(boxBd);
        }

Above we have the body create functions: createGround(), createBall(), and createBox(). Other than the obvious, let’s talk about a few things going on here. Each body is defined by a shape definition, and each shape definition has a number of properties that dictate how it will behave in the Box2D JS world (see the Box2D docs for details). Restitution, friction, and density affect how the shapes fall, move, and react.

The extents define the dimensions of the shapes, but probably not how you are accustomed. Extents represent the distance from one corner of the shape to its center. So a 100×100 box is actually defined by the extents shapeDef.extents.Set(50,50).

The shape definition is then used to define a body definition. The body is then positioned in the world. The positioning, like extents, is also based on the center of the body, not its corner. Finally, the newly defined body, based on the shape definition, is added the world with the CreateBody() function.


        function createHelloWorld() {
                // H
                createBox(world, 50, 420, 10, 20, false);
                createBox(world, 90, 420, 10, 20, false);
                createBox(world, 70, 395, 30, 5, false);
                createBox(world, 50, 370, 10, 20, false);
                createBox(world, 90, 370, 10, 20, false);
 
                // E
                createBox(world, 140, 435, 30, 5, false);
                createBox(world, 120, 420, 10, 10, false);
                createBox(world, 130, 405, 20, 5, false);
                createBox(world, 120, 390, 10, 10, false);
                createBox(world, 140, 375, 30, 5, true);
 
                // L
                createBox(world, 200, 435, 20, 5, false);
                createBox(world, 185, 400, 5, 30, false);
 
                // L
                createBox(world, 250, 435, 20, 5, false);
                createBox(world, 235, 400, 5, 30, false);
 
                // O
                createBox(world, 300, 435, 20, 5, false);
                createBox(world, 285, 405, 5, 25, false);
                createBox(world, 315, 405, 5, 25, false);
                createBox(world, 300, 375, 20, 5, false);
 
                // W
                createBox(world, 390, 435, 40, 5, false);
                createBox(world, 360, 390, 10, 40, false);
                createBox(world, 420, 390, 10, 40, false);
                createBox(world, 390, 415, 5, 15, false);
 
                // O
                createBox(world, 460, 435, 20, 5, false);
                createBox(world, 445, 405, 5, 25, false);
                createBox(world, 475, 405, 5, 25, false);
                createBox(world, 460, 375, 20, 5, false);
 
                // R
                createBox(world, 495, 410, 5, 30, false);
                createBox(world, 518, 425, 5, 15, false);
                createBox(world, 515, 405, 15, 5, false);
                createBox(world, 525, 390, 5, 10, false);
                createBox(world, 510, 375, 20, 5, false);
 
                // L
                createBox(world, 560, 435, 20, 5, false);
                createBox(world, 545, 400, 5, 30, false);
 
                // D
                createBox(world, 610, 435, 20, 5, false);
                createBox(world, 595, 405, 5, 25, false);
                createBox(world, 625, 405, 5, 25, false);
                createBox(world, 610, 375, 20, 5, false);
 
                // !
                createBox(world, 650, 430, 10, 10, false);
                createBox(world, 650, 380, 10, 40, false);
        }

Here’s my addition to the Box2D JS code. By using a series of stacked boxes I create the infamous programmer’s first message, “Hello World!” (minus the comma, sorry). No science or mystery here, just a lot of extents and positions for the boxes that compose my message. And yes, I did cheat on the “E” and make it fixed, but not even I can defy the laws of physics for the sake of a code demo.


        function step(cnt) {
                var stepping = false;
                var timeStep = 1.0/60;
                var iteration = 1;
                world.Step(timeStep, iteration);
                ctx.clearRect(0, 0, canvasWidth, canvasHeight);
                drawWorld(world, ctx);
                setTimeout('step(' + (cnt || 0) + ')', 10);
        }

The step() function is the what makes the whole thing work. step() is called over and over, at specified intervals, to create the animation of our scene on the canvas. The world’s Step() function is first called to apply one iteration of physics to our world’s bodies. Next we clear the visual representation of the scene so that it can be redrawn by our drawWorld() function. Finally we set the interval timer so that step() will be called again. Again, Flash devs will recognize this as a similar methodology as using the ENTER_FRAME event.


        // main entry point
        Event.observe(window, 'load', function() {
                world = createWorld();
                ctx = $('canvas').getContext('2d');
                var canvasElm = $('canvas');
                canvasWidth = parseInt(canvasElm.width);
                canvasHeight = parseInt(canvasElm.height);
                canvasTop = parseInt(canvasElm.style.top);
                canvasLeft = parseInt(canvasElm.style.left);
 
                createHelloWorld();
 
                Event.observe('canvas', 'click', function(e) {
                                if (Math.random() > 0.5) {
                                        //createBox(world, Event.pointerX(e), Event.pointerY(e), 10, 10, false);
                                        createBox(world, e.clientX, e.clientY, 10, 10, false);
                                } else {
                                        createBall(world, Event.pointerX(e), Event.pointerY(e));
                                }
                });
                step();
        });
        script>

And this is where we kick everything off. We use the Prototype event handling mechanism to wait until the window is loaded to start our code. We first create the Box2D JS world and get the 2D context, dimensions, and position of our canvas. After that I create the boxes that make up the Hello World message. To finish up we listen for mouse clicks so that we can add more falling objects to the scene, since what good is a meticulously stacked Hello World if you can’t turn it into a pile of rubble? The first step() is kicked off and our scene is ready to go!


    head>
    <body style="margin:0px;">
        <canvas id="canvas" width='800' height='500' style="background-color:#eeeeee;">canvas>
    body>
 html>

And to round out the breakdown, here’s the actual instance of the canvas element. Its a very simple container and allows for all the heavy lifting to be done in the Javascript. Just a reminder, the canvas element will NOT work in Internet Explorer unless you have the conditional check that includes excanvas if necessary, or if you happen to be test driving the IE9 beta.

The Summary

OK, well that turned out a lot longer and wordier than I was expecting, but 2D physics being utilized in HTML5 and Javascript is a simple topic. Hopefully, though, if you made it all the way through you have a much better understanding of how they all play together and how you will create Newtonian worlds of your own.

The difference in performance between different browsers is enough to drive you crazy. In my personal experience, Chrome > Firefox > IE. I know its early in the game and that IE 9 and Firefox 4 will likely be right up to par with Chrome, but its this lag and inconsistency across the board that has me spending most of my web development time in Flash and AS3.

Also, I had trouble making this work in all versions of IE, so if you run into any problems with a particular browser, please let me know.

NOTE: The concatenated version of Box2D JS is a convenience I offer so that you don’t have to do all the individual includes shown on the Box2D JS site’s demos. The minified version is that same concatenated version run through JSMin. Both versions are based on version 0.1.0 of Box2D JS. I am not a contributor to the Box2D JS project, just someone making it simpler to get into.