The Famo.us JavaScript library was released on 4/10/2014. The idea behind it is simple yet powerful: make HTML5/JavaScript/CSS web pages perform and feel like native mobile apps. There have been many companies that tried and failed to use HTML5 to build apps. Facebook famously decided to ditch HTML5 for its apps in favor of true native code to drastically improve user experience.
Famo.us was born to address the performance concerns when using JavaScript to build native apps. I got hooked on it when I saw their Periodic Table example. I had never seen something built in JavaScript that looked so…well, native. I tried it on my iPad 2, and it was blazing fast. I tried it on my iPhone 5s, and it felt native. I even tried it on an Eee laptop, and it was great.
The Famo.us code is now publicly available on Github. I’ve been tinkering with it for the past few days on a POC: a web-based guitar tuner. This blog post will be my thoughts and feelings while developing this simple app.
The “Magic”
When I first saw the Famo.us periodic table, I immediately opened up my Dev Tools and inspected the DOM elements flying around in front of me. I couldn’t believe that they were able to make something so graceful using a platform that was supposedly incompatible with this level of beauty. What I saw were DIVs, CSS, and changing properties. That’s when I was sure this thing was for real.
That was almost two years ago. Now that I finally have access to the source code, I’ve dug deep to figure out the “magic” behind how they make their performance so responsive. Famo.us works by ditching the DOM positioning model in favor of CSS3 transformations.
Their argument is that the DOM was made for displaying text documents, not building complex data-driven apps. Setting the absolute/relative top/left/bottom/right position works fine for displaying text. When it comes to beautiful web apps, something different is required.
The library leverages CSS3 transformations to move content around the page. It takes a little getting used to, but once you wrap your head around it, the model makes sense. Updates to the DOM are also minimized to lessen the effect of reflows/repaints. These together represent a noticeable shift in the way programmers write JavaScript to give the user a clean, responsive feeling application.
Famo.us Structure
Minimalism is the name of the game for Famo.us’ core code. The library is about as unopinionated as you can get.
One of the few opinions they have is to use AMD to separate the code off into modules. I’ve seen this pattern become more and more popular with newer JavaScript libraries, so it’s no surprise they went this route too.
Their code does have inline JSDoc, which is helpful. The docs and guides are also essential to wrapping your head around this extremely new library.
Getting Started
There are two basic options that a developer can choose from when they want to start writing a Famo.us app. The first is to include it as a Bower dependency from their GitHub, then start hacking away. This is the recommended approach for pre-existing projects with a well-defined structure that want to replace the views with Famo.us.
The second option is to use their Yeoman generator to build a project for you. This is the preferred option when starting a new Famo.us project from scratch. This is the route I chose, and I was up and running within a minute.
As a side note, the Famo.us team is very receptive to feedback. I pointed out that their Gruntfile.js generated through Yeoman could be simplified with the load-grunt-config task. They pushed the change just a few days later. (This article from HTML5 Rocks on supercharging your Gruntfile is a must read for projects that use Grunt.)
Key Concepts
The best way to learn a new library is to get your feet wet and start writing code. That is exactly what I did, and this is what I learned.
The Engine is a singleton that is analogous to an “application” object in other libraries. It is the backbone that keeps the whole show running. It handles how animations are timed, and other infrastructure tasks like that.
A Context represents a self-contained view state of the application. If this were a SPA (Single Page Application), there would be one context. If the application can switch between X multiple, distinct views, then there would be X contexts.
The Surface class allows the developer to put renderable content onto the page. It maps to a DIV
, and can take in HTML, CSS, event listeners, etc. To render a Surface, they must be added to a Context.
A Modifier (StateModifier is the most commonly used) can be added to a Context before a Surface to manage how the surface is positioned. The order that classes are added to a Context matters in Famo.us. It is up to the developer to make sure that the Modifier is added before the Surface, then they need to hang on to a reference to both.
The Transform allows a Surface to be moved around a Context. This is where the CSS3 transformation magic comes into play. The Modifier allows the Transform to be changed, which affects the Surface the Modifier is bound to.
Guitar Tuner Implementation
With the core concepts out of the way, the way I’ve chosen to implement the Guitar Tuner can now be explored. The code is hosted on the Keyhole Software GitHub in the khs-famo.us-guitar-tuner repository.
All of the code I wrote for this application is in app/src. Everything else in the project was generated by the Famo.us Yeoman generator. This got me running quickly so I could focus on writing features rather than the architecture.
I chose to use vanilla JavaScript for this initial implementation so I could focus on the library itself. If I get the itch to rewrite it, I’ll use Backbone to give it a little more structure. For this first pass though, I wanted as much exposure as possible. I’ll step through the code in the way that the browser interprets it.
When the page is loaded, main.js gets immediately executed. This loads the Famo.us engine, requires the GuitarContext
, and adds the context to the engine.
The GuitarContext is really a controller for the tuner itself. It contains the context that the subviews render themselves to as well as managing the strings and pick. This class manages two other controllers: StringsController
and PickController
.
StringsController is responsible for creating six StringSurface views, adding them to the GuitarContext
, and managing the interaction between the StringSurface
and the GuitarContext
.
When a StringSurface
is “plucked”, the PickController sends a PickSurface to the plucked string
.
I tried to keep the structure of it as simple as possible. The only real gotcha is that the eventing layer in Famo.us is not a mixin, so I had to implement .on()
and .emit()
methods in StringsController
.
Lessons Learned
The most obvious lesson is that if Famo.us needs to be used to build a complex application, another library is needed to give it an MVC structure. It is easy when working with plain JavaScript to skip some of the most important software design principles. This will work for a small project for a small amount of time, but as the code base expands so will complexity and maintainability. Using Famo.us by itself does not tie the developer to a structure, so the structure needs to be chosen by using another library.
The second lesson I learned is that Famo.us needs to be the one that manages all DOM interaction on the page. If the code makes a change to the DOM outside of the Famo.us Engine, performance may take a hit as well as the overall appearance of the page. This means that frameworks that generate DOM behind the scenes (e.g. ExtJS) may have issues working with Famo.us. Libraries like Bootstrap should be fine since the HTML is static
and well defined.
The third lesson that I learned is that Famo.us is still young. There are growing pains associated with enterprise-level software that they’ll have to go through. I’m excited to be an early adopter and member of the community to lend my experience to them during this process of growth.
– Zach Gardner, asktheteam@keyholesoftware.com