<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">

  <title>Bad films are bad</title>
  <link href="http://goodfil.ms/blog/atom.xml" rel="self"/>
  <link href="http://goodfil.ms/blog/"/>
  <updated>2012-12-06T11:28:05+11:00</updated>
  <id>http://goodfil.ms/blog/</id>
  <author>
    <name>Goodfilms</name>
    
  </author>
  <generator uri="http://octopress.org/">Octopress</generator>

  
  <entry>
    <title>New profile pages &amp; Netflix giveaway</title>
    <link href="http://goodfil.ms/blog/posts/2012/12/05/new-profiles-pages-netflix-giveaway/"/>
    <updated>2012-12-05T12:00:00+11:00</updated>
    <id>http://goodfil.ms/blog/posts/2012/12/05/new-profiles-pages-netflix-giveaway</id>
    <content type="html">&lt;p&gt;&lt;img src=&quot;http://files.goodfil.ms/marketing/user_profile_splash.jpeg&quot; alt=&quot;New Profile Pages&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Everyone&amp;#8217;s profile page just got a massive overhaul. Check out the team&amp;#8217;s profiles at &lt;a href=&quot;http://goodfil.ms/john&quot;&gt;John&lt;/a&gt;, &lt;a href=&quot;http://goodfil.ms/glen&quot;&gt;Glen&lt;/a&gt; and &lt;a href=&quot;http://goodfil.ms/superhighfives&quot;&gt;Charlie&lt;/a&gt; for an example.&lt;/p&gt;

&lt;p&gt;You now have a personalised ratings graph to share with friends, a list of your favourite and worst films and a public queue of movies you&amp;#8217;re itching to see.&lt;/p&gt;

&lt;p&gt;As an extra sweetener we&amp;#8217;re giving away three 6 month  Netflix memberships throughout December. That should help get you through your queued backlog of films.&lt;/p&gt;

&lt;!&#8211; more &#8211;&gt;


&lt;h2&gt;How to Enter&lt;/h2&gt;

&lt;p&gt;Simply share your profile page to Facebook and you&amp;#8217;re done. Just sign into &lt;a href=&quot;http://goodfil.ms&quot;&gt;Goodfilms&lt;/a&gt;, go to your profile page, and click the share link.&lt;/p&gt;

&lt;p&gt;We&amp;#8217;ll be announcing winners on the 22nd December.&lt;/p&gt;

&lt;p style=&#8217;font-weight: bold; text-align:center; font-size: 1.5em;&#8217;&gt;Good luck!&lt;/p&gt;


&lt;h2&gt;Goodfilms&lt;/h2&gt;

&lt;p&gt;Goodfilms is a way to share the movies you watch with your friends. We rate movies on two criteria - &amp;#8216;quality&amp;#8217; and &amp;#8216;rewatchability&amp;#8217;, so you can admit to your guilty pleasures and properly capture the feeling you get when a film leaves you exhausted. Sign up now and keep track of the films you love, and find great, challenging or silly new ones to watch.&lt;/p&gt;

&lt;div class=&quot;okgo&quot;&gt;
  &lt;a class=&quot;btn-auth btn-facebook large&quot; href=&quot;http://goodfil.ms/users/auth/facebook&quot;&gt;
    Sign up with
    &lt;strong&gt;Facebook&lt;/strong&gt;
  &lt;/a&gt;
  &lt;a class=&quot;btn-auth btn-twitter large&quot; href=&quot;http://goodfil.ms/users/auth/twitter&quot;&gt;
    Sign up with
    &lt;strong&gt;Twitter&lt;/strong&gt;
  &lt;/a&gt;
&lt;/div&gt;

</content>
  </entry>
  
  <entry>
    <title>Goodfilms &amp; Hopscotch Movie Pass Giveaway</title>
    <link href="http://goodfil.ms/blog/posts/2012/10/12/hopscotch-movie-pass-giveaway/"/>
    <updated>2012-10-12T16:00:00+11:00</updated>
    <id>http://goodfil.ms/blog/posts/2012/10/12/hopscotch-movie-pass-giveaway</id>
    <content type="html">&lt;p&gt;&lt;img src=&quot;http://files.goodfil.ms/hopscotch-giveaway-oct-2012/passes.jpg&quot; alt=&quot;Hopscotch Movie Passes&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Thanks to the generosity of Australian film distributor &lt;a href=&quot;http://www.hopscotchfilms.com.au/&quot;&gt;Hopscotch&lt;/a&gt; we&amp;#8217;ve got 5 double passes to &lt;a href=&quot;http://goodfil.ms/film/257939-to-rome-with-love&quot;&gt;To Rome With Love&lt;/a&gt; and 5 double passes to &lt;a href=&quot;http://goodfil.ms/film/258130-seven-psychopaths&quot;&gt;Seven Psychopaths&lt;/a&gt; to give away to some of our lucky Australian users.&lt;/p&gt;

&lt;!&#8211; more &#8211;&gt;


&lt;h2&gt;How to Enter&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;To go in to the running&lt;/strong&gt;, just add &lt;a href=&quot;http://goodfil.ms/film/257939-to-rome-with-love&quot;&gt;To Rome With Love&lt;/a&gt; or &lt;a href=&quot;http://goodfil.ms/film/258130-seven-psychopaths&quot;&gt;Seven Psychopaths&lt;/a&gt; to your queue before 5pm Thursday the 18th Melbourne time.&lt;/p&gt;

&lt;p&gt;For an even bigger chance to win, we re-enter you in the draw every time you get a friend to sign up to Goodfilms between now and the end of the contest.&lt;/p&gt;

&lt;p&gt;&lt;a style=&quot;margin-top: 15px; margin-right: 15px;&quot; href=&quot;http://hopscotchfilms.com.au&quot;&gt;&lt;img src=&quot;http://files.goodfil.ms/hopscotch-giveaway-oct-2012/HopscotchLogoblack.png&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;Goodfilms&lt;/h2&gt;

&lt;p&gt;Goodfilms is a way to share the movies you watch with your friends. We rate movies on two criteria - &amp;#8216;quality&amp;#8217; and &amp;#8216;rewatchability&amp;#8217;, so you can admit to your guilty pleasures and properly capture the feeling you get when a film leaves you exhausted. Sign up now and keep track of the films you love, and find great, challenging or silly new ones to watch.&lt;/p&gt;

&lt;div class=&quot;okgo&quot;&gt;
  &lt;a class=&quot;btn-auth btn-facebook large&quot; href=&quot;http://goodfil.ms/users/auth/facebook&quot;&gt;
    Sign up with
    &lt;strong&gt;Facebook&lt;/strong&gt;
  &lt;/a&gt;
  &lt;a class=&quot;btn-auth btn-twitter large&quot; href=&quot;http://goodfil.ms/users/auth/twitter&quot;&gt;
    Sign up with
    &lt;strong&gt;Twitter&lt;/strong&gt;
  &lt;/a&gt;
&lt;/div&gt;

</content>
  </entry>
  
  <entry>
    <title>The Minimum Viable Rails stack, now Reddit frontpage approved.</title>
    <link href="http://goodfil.ms/blog/posts/2012/09/15/minimum-viable-rails-stack/"/>
    <updated>2012-09-15T13:00:00+10:00</updated>
    <id>http://goodfil.ms/blog/posts/2012/09/15/minimum-viable-rails-stack</id>
    <content type="html">&lt;p&gt;This week we hit a bit of a StumbleUpon bonanza, landed on the Reddit front page, and got featured on Lifehacker. These three things threw &lt;em&gt;quite a bit&lt;/em&gt; of traffic our way, and we got through it all with around 3 hours of degraded performance, and patches of intermittent bad gateway errors. Not too shabby.&lt;/p&gt;

&lt;p&gt;With alarmingly serendipitous timing, two weeks ago I did a &lt;a href=&quot;http://jrb.tumblr.com/post/30570014929/scaling-rails-at-melbourne-roro&quot;&gt;presentation at the Melbourne Ruby group on Scaling Rails&lt;/a&gt;. I posted the talk and my explanatory notes on my personal tumblr for two reasons. The first is that it includes a bit of my personal, subjective, and somewhat unsubstantiated ideas on developer types and ops, and I prefer to keep those kinds of musings off the team blog.&lt;/p&gt;

&lt;p&gt;The second was that while I&amp;#8217;d promoted the idea of the &amp;#8220;minimum viable stack&amp;#8221; which, while I thought pretty sound (and bet Goodfilms&amp;#8217; tech direction on), was untested. This week we gave it a solid thrashing, so it&amp;#8217;s time to promote it as &amp;#8220;official Goodfilms policy&amp;#8221;.&lt;/p&gt;

&lt;p&gt;The TL; DR is that it worked, Mostly and I&amp;#8217;m now even more comfortable recommending the stack as a good starting point.&lt;/p&gt;

&lt;h2&gt;The &amp;#8220;Minimum Viable Stack&amp;#8221; for your just-past &amp;#8220;Minimum Viable Product&amp;#8221;&lt;/h2&gt;

&lt;p&gt;In &lt;a href=&quot;http://jrb.tumblr.com/post/30570014929/scaling-rails-at-melbourne-roro&quot;&gt;my presentation&lt;/a&gt; I suggested that the &amp;#8220;golden stack&amp;#8221; for a rails app just out of its&amp;#8217; MVP stage is roughly:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Deployed to a cloud provider like Amazon or Rackspace&lt;/li&gt;
&lt;li&gt;Uses MySQL or Postgres as the datastore, deployed to a single instance, with frequent data backups to cloud storage&lt;/li&gt;
&lt;li&gt;Has two &amp;#8220;app instances&amp;#8221;, which host both your web processes and delayed job workers. Each instance should be capable of holding &lt;em&gt;all&lt;/em&gt; your regular traffic&lt;/li&gt;
&lt;li&gt;Load balances web requests between the two app servers (with health checks enabled) using whichever magic load balancer your cloud provider gives you&lt;/li&gt;
&lt;li&gt;Performance monitored using either &lt;a href=&quot;http://scoutapp.com/&quot;&gt;Scout&lt;/a&gt; or &lt;a href=&quot;http://newrelic.com/&quot;&gt;NewRelic&lt;/a&gt;, or both if you like&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;There&amp;#8217;s more detail over on my original post, but the key inputs into that design is were:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Low chance of &amp;#8220;off the air&amp;#8221; downtime, despite cloud servers being notoriously &amp;#8220;ephemeral&amp;#8221;&lt;/li&gt;
&lt;li&gt;Lowest operational cost, except where it grossly impacts the above point&lt;/li&gt;
&lt;li&gt;Favouring simplicity, except where it grossly impacts the above two points&lt;/li&gt;
&lt;li&gt;Favouring traditional SQL databases as it&amp;#8217;s the storage paradigm Rails &amp;#8220;grew up with&amp;#8221;, so has the best tooling and knowledge in the community&lt;/li&gt;
&lt;li&gt;Favouring SQL again as if it becomes the bottleneck you can vertically scale it just long enough to get you out of trouble, and easy to hire experts to help&lt;/li&gt;
&lt;li&gt;Avoiding vendor lock in where possible&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;The Goodfilms Stack&lt;/h2&gt;

&lt;p&gt;Goodfilms&amp;#8217; setup is almost exactly what I listed above. We run Rails 3.2, use Postgres as our datastore, host on Rackspace cloud using both their load balancers and cloud storage/cdn on top of regular cloud servers, and monitoring the whole setup using NewRelic.&lt;/p&gt;

&lt;p&gt;The only departure from the stack listed above is that we have a third server setup as a general utility box. It has two main jobs: import catalog data from Netflix, iTunes, and the Movie DB, and also to run our &lt;a href=&quot;http://goodfil.ms/blog/posts/2012/04/20/project-ingen/&quot;&gt;elaborate taste comparison engine codenamed &amp;#8220;Project Ingen&amp;#8221;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So, that&amp;#8217;s the background, lets talk about what happened, what worked well for us with the stack, and what didn&amp;#8217;t&lt;/p&gt;

&lt;h2&gt;What Happened?&lt;/h2&gt;

&lt;p&gt;I&amp;#8217;m just going to focus on the Reddit front page part of the story, because that&amp;#8217;s where the interesting things happened.&lt;/p&gt;

&lt;p&gt;For a long time, we&amp;#8217;ve thought that there a lot of people who want to pay for film content, and that one of the things Goodfilms can do is help those people find the best things to watch that are available legally. To test that assumption, we did a couple of MVP (minimum viable product pages) for both &lt;a href=&quot;http://goodfil.ms/films/on_itunes&quot;&gt;iTunes&lt;/a&gt; and &lt;a href=&quot;http://goodfil.ms/films/on_netflix&quot;&gt;Netflix&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you look at the iTunes page, you can see how minimal our minimum is, as we&amp;#8217;ve not yet upgraded that page. Seeing that the Netflix page was pulling in enough organic search, we tasked our talented new designer &lt;a href=&quot;http://twitter.com/superhighfives&quot;&gt;Charlie&lt;/a&gt; with taking the Netflix page to the next level and see how far we can take it.&lt;/p&gt;

&lt;p&gt;Once Charlie was done with it, it looked like we had a winner. Realising that a lot of people might want to use the page, I took a full day to double check all the performance of the queries in the page and fix up what I could.&lt;/p&gt;

&lt;p&gt;NewRelic had shown us that the MVP page was only just scraping through performance wise, which is fine for a proof of concept, but not fine for a page you want to load. Once everything was looking OK in the front and back end, I gave the thumbs up to &lt;a href=&quot;http://twitter.com/glenmaddern&quot;&gt;Glen&lt;/a&gt; to &amp;#8220;throw as many people at it as he can find&amp;#8221;.&lt;/p&gt;

&lt;p&gt;There are quite a few people on Reddit, and Glen found them, and got a good section of them to come and use it.&lt;/p&gt;

&lt;p&gt;What might not be immediately obvious is that we&amp;#8217;re based in Australia. The bulk of the Redditing happened in the middle of the night, and so Glen rang me up, pulled me out of bed, and we babysat the servers.&lt;/p&gt;

&lt;p&gt;A few hours later we both went back to bed, confident that the site was going to stay up, and very pleased with the influx of new users. In those hours, we added a couple more app servers, and set up full page caching for signed out users.&lt;/p&gt;

&lt;h2&gt;What went well?&lt;/h2&gt;

&lt;p&gt;The first thing that went well was taking the time to look at the performance of our new features, doing some work, but not overdoing it.&lt;/p&gt;

&lt;p&gt;Defining &amp;#8220;good enough&amp;#8221; performance for as yet unproven features is tricky, especially when taking into account the opportunity cost of the dev time that can go into other features.&lt;/p&gt;

&lt;p&gt;In this case, we got the balance right. I spent about a day getting performance to OK, and it was good enough to degrade gracefully under load, rather than explode. That gave us the breathing room to do the rest of our work in the middle of the night, without having wasted too much time beforehand when we were unsure of it&amp;#8217;s success.&lt;/p&gt;

&lt;p&gt;The other thing that worked really well was a direct result of three stack decisions that interrelated: host on the cloud, and split your app servers early, and monitor performance using NewRelic.&lt;/p&gt;

&lt;p&gt;When traffic started climbing, we quickly switched our NewRelic subscription up to the pro level. We can&amp;#8217;t afford to run it all the time, but it&amp;#8217;s easy to turn it up when you do need it.&lt;/p&gt;

&lt;p&gt;Under heavy load, there are generally only two things I want to look at in NewRelic: is there any page that is ridiculously broken from a performance perspective, and do we have enough capacity. If performance is broken somewhere, fix it. If you don&amp;#8217;t have capacity, add it.&lt;/p&gt;

&lt;p&gt;This time, all the pages were OK (because of the small upfront investment of work), but we did have a capacity problem. This did not take us long to figure out, because this is what NewRelic is good at. First design decision validated.&lt;/p&gt;

&lt;p&gt;To add capacity, we just took a snapshot image of one of our app servers, and then spun up new servers directly from the image. This was pretty easy, but if we weren&amp;#8217;t on commodity cloud hosting, it couldn&amp;#8217;t have happened and we would have been boned. Second design decision validated.&lt;/p&gt;

&lt;p&gt;Cloning the servers &amp;#8220;just worked&amp;#8221; because by keeping the database away from the app servers made sure there wasn&amp;#8217;t any hidden coupling there. Moving to two app servers early made sure we weren&amp;#8217;t accidentally relying on shared state. It&amp;#8217;s the old programming truism about there only being three numbers in computing: zero, one, and many. Once you have &amp;#8220;many&amp;#8221; servers, adding new ones is no stress.&lt;/p&gt;

&lt;p&gt;The final thing that worked well was following the idea of stack simplicity. I had worked a long day polishing the Netflix page code, then had my final game of indoor soccer for the season (with a win), and then went to the pub to celebrate with my team.&lt;/p&gt;

&lt;p&gt;The only things that made working with the stack tricky that night were personal, not technical: I was tired, and not 100% sober. If simplicity wasn&amp;#8217;t a core tenet of the stack, we could have kissed that uptime goodbye.&lt;/p&gt;

&lt;h2&gt;What went poorly?&lt;/h2&gt;

&lt;p&gt;In the presentation to the Ruby group, I stressed that you really needed to understand what the workload for your app is, and find the best bang for buck scaling strategy and use that.&lt;/p&gt;

&lt;p&gt;Until this week, I&amp;#8217;d been treating Goodfilms purely as a social network, and picked all our strategies to match that. The realisation that we&amp;#8217;ve had as a team lately is that Goodfilms really is two sites living side by side: a community site/social network for films, and a rich content site for browsing for films.&lt;/p&gt;

&lt;p&gt;With content based sites, full HTML page caches are your most cost effective technique for dealing with load. We didn&amp;#8217;t have any page caching in place at all.&lt;/p&gt;

&lt;p&gt;Glen&amp;#8217;s flatmate &lt;a href=&quot;http://twitter.com/ben_h&quot;&gt;Ben&lt;/a&gt;, de-facto ops guy for &lt;a href=&quot;http://theconversation.edu.au&quot;&gt;theconversation.edu.au&lt;/a&gt; and expert in content based sites chipped in and got us our first cut of page caching in place. This got load under control, but meant that signed in users weren&amp;#8217;t getting the proper experience for that page.&lt;/p&gt;

&lt;p&gt;The next step was to set things up so we could do signed in vs. signed out caches on demand. This was a little tricky half asleep, but we got there in the end. If you&amp;#8217;re interested in how it works, you can &lt;a href=&quot;https://gist.github.com/3711251&quot;&gt;check out the specific nginx config and capistrano task I wrote here&lt;/a&gt; and it might help you out of a jam.&lt;/p&gt;

&lt;p&gt;The &amp;#8220;frequent database backups&amp;#8221; policy caused us a few headaches. Doing a full dump of a growing database while it&amp;#8217;s under load isn&amp;#8217;t what I would call &amp;#8220;ideal&amp;#8221;. That said, I&amp;#8217;d rather have a periodical couple of bad gateway errors than risk data loss, so I&amp;#8217;d still do it the exact same way again.&lt;/p&gt;

&lt;p&gt;If I come up with a better balance of low cost/good uptime/good performance I&amp;#8217;ll refine my suggestion for the datastore, but for the mean time I&amp;#8217;m sticking with it, but flagging it as an explicit trade off you&amp;#8217;ll be making if you follow our stack suggestions.&lt;/p&gt;

&lt;h2&gt;Final thoughts&lt;/h2&gt;

&lt;p&gt;I can&amp;#8217;t say we&amp;#8217;re 100% web scale. Staying up half the night with your servers means you&amp;#8217;re not there yet. I think this is OK for where we&amp;#8217;re at as a business. We&amp;#8217;re still feeling our way through to the final feature set of the product, and learning what our market is like. Too much engineering now would be premature.&lt;/p&gt;

&lt;p&gt;I strongly believe that there are no &amp;#8220;right&amp;#8221; answers in scaling a web app, but after giving this stack a solid thrashing, I feel very comfortable putting it forward as a good starting point. It&amp;#8217;s a solid base from which you can respond to growth, and evolve it to match your situation well.&lt;/p&gt;

&lt;p&gt;Make sure you put the machinery in place for page caching early, even if you&amp;#8217;re not using it. The code is easy to write when you&amp;#8217;re relaxed in the middle of the day, and a royal pain in the ass in the middle of the night.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Updated:&lt;/em&gt; If you found this useful please &lt;a href=&quot;http://news.ycombinator.com/item?id=4525092&quot;&gt;discuss or upvote over on Hacker News&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;Goodfilms&lt;/h2&gt;

&lt;p&gt;Goodfilms is a way to share the movies you watch with your friends. We rate movies on two criteria - &amp;#8216;quality&amp;#8217; and &amp;#8216;rewatchability&amp;#8217;, so you can admit to your guilty pleasures and properly capture the feeling you get when a film leaves you exhausted. Sign up now and keep track of the films you love, and find great, challenging or silly new ones to watch.&lt;/p&gt;

&lt;div class=&quot;okgo&quot;&gt;
  &lt;a class=&quot;btn-auth btn-facebook large&quot; href=&quot;http://goodfil.ms/users/auth/facebook&quot;&gt;
    Sign up with
    &lt;strong&gt;Facebook&lt;/strong&gt;
  &lt;/a&gt;
  &lt;a class=&quot;btn-auth btn-twitter large&quot; href=&quot;http://goodfil.ms/users/auth/twitter&quot;&gt;
    Sign up with
    &lt;strong&gt;Twitter&lt;/strong&gt;
  &lt;/a&gt;
&lt;/div&gt;

</content>
  </entry>
  
  <entry>
    <title>Why ratings systems don&#8217;t work</title>
    <link href="http://goodfil.ms/blog/posts/2012/08/22/why-ratings-systems-dont-work/"/>
    <updated>2012-08-22T14:00:00+10:00</updated>
    <id>http://goodfil.ms/blog/posts/2012/08/22/why-ratings-systems-dont-work</id>
    <content type="html">&lt;p&gt;Today&amp;#8217;s &lt;a href=&quot;http://xkcd.com/1098/&quot;&gt;XKCD&lt;/a&gt; reminded us of a problem we&amp;#8217;ve known about for a while:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://imgs.xkcd.com/comics/star_ratings.png&quot; alt=&quot;XKCD&#8217;s recent summary of online ratings&quot; /&gt;&lt;/p&gt;

&lt;p&gt;A little over a year ago, we started building a movie site with this exact problem in our sights - how could we make film ratings more useful? Bad ratings are all over the internet, and as someone who&amp;#8217;s tried to make an improvement, I thought it was worth responding.&lt;/p&gt;

&lt;!&#8211; more &#8211;&gt;


&lt;h2&gt;The problem with average ratings&lt;/h2&gt;

&lt;p&gt;Average ratings are, in their essence, an attempt to distil the entire set of ratings about an object (in our case, films), down to a single number. Which seems useful - after all, it saves you so much time deciding whether something&amp;#8217;s any good. The problem is that &lt;strong&gt;that&amp;#8217;s all it&amp;#8217;s good for&lt;/strong&gt; - telling you whether something&amp;#8217;s good or not.&lt;/p&gt;

&lt;p&gt;If you come across a film and see it has a 7.5+ average rating on IMDB, it&amp;#8217;s a sure bet it&amp;#8217;s going to be well made, and plenty of people must like it (whether you will, or not, is another matter). If the rating is below a 6.5, you should probably be wary. The rating scale might have 100 points on it, but all the useful ratings are crammed into the top third, and suddenly things are looking like XKCD&amp;#8217;s example above. This is the same situation for Rotten Tomatoes ratings, too - &lt;strong&gt;great for the &amp;#8216;is it decent/is it awful&amp;#8217; discussion, but without any depth&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;Histograms are even worse&lt;/h2&gt;

&lt;p&gt;So, if a single average rating isn&amp;#8217;t enough, maybe it&amp;#8217;s valuable to show the spread of people&amp;#8217;s opinions, to try to give a bit of &amp;#8216;colour&amp;#8217; to the ratings. After all, if something was polarizing, there&amp;#8217;s a chance you&amp;#8217;ll hate it. The problem is, &lt;strong&gt;histograms are just. awful.&lt;/strong&gt; Let&amp;#8217;s look at some real world examples:&lt;/p&gt;

&lt;figure&gt;
  &lt;img title=&#8217;Starship Troopers - iTunes&#8217; src=&#8217;http://files.goodfil.ms/starship troopers - itunes histogram.png&#8217;&gt;
  &lt;img title=&#8217;Starship Troopers - Amazon&#8217; src=&#8217;http://files.goodfil.ms/starship troopers - amazon histogram.png&#8217;&gt;
  &lt;figcaption&gt;Starship Troopers&lt;/figcaption&gt;
&lt;/figure&gt;




&lt;figure&gt;
  &lt;img title=&#8217;Fifth Element - iTunes&#8217; src=&#8217;http://files.goodfil.ms/fifth element - itunes histogram.png&#8217;&gt;
  &lt;img title=&#8217;Fifth Element - Amazon&#8217; src=&#8217;http://files.goodfil.ms/fifth element - amazon histogram.png&#8217;&gt;
  &lt;figcaption&gt;Fifth Element&lt;/figcaption&gt;
&lt;/figure&gt;




&lt;figure&gt;
  &lt;img title=&#8217;Blade Runner - iTunes&#8217; src=&#8217;http://files.goodfil.ms/blade runner - itunes histogram.png&#8217;&gt;
  &lt;img title=&#8217;Blade Runner - Amazon&#8217; src=&#8217;http://files.goodfil.ms/blade runner - amazon histogram.png&#8217;&gt;
  &lt;figcaption&gt;Blade Runner&lt;/figcaption&gt;
&lt;/figure&gt;


&lt;p&gt;I don&amp;#8217;t know how this is supposed to help you beyond telling you &amp;#8216;these are all 3.5 to 4.5 star films&amp;#8217;, which you probably already knew. After all, these movies are superficially similar (well-known science fiction films), but anyone who has seen all three would agree that they have a vastly different character, and appeal to different kinds of people. The histograms aren&amp;#8217;t enough to capture that, they end up just adding confusion.&lt;/p&gt;

&lt;h2&gt;See patterns, not averages&lt;/h2&gt;

&lt;p&gt;Within Goodfilms, films are rated on two axes - first there&amp;#8217;s &lt;strong&gt;quality&lt;/strong&gt;, which is the 5-star system everyone&amp;#8217;s used to; and then there&amp;#8217;s &lt;strong&gt;rewatchability&lt;/strong&gt;, how much you&amp;#8217;d enjoy watching that film again. It means we can display ratings on a graph, instead of a line, and it&amp;#8217;s able to convey a lot more information. It takes a little getting used to, but it can capture both the &amp;#8216;good or not&amp;#8217; question of an average rating, as well as an understanding of the kind of film it might be. The key is that &lt;strong&gt;people are really good at seeing patterns&lt;/strong&gt;, and a scatter plot caters to that. Let&amp;#8217;s compare the examples from before:&lt;/p&gt;

&lt;figure&gt;
  &lt;img title=&#8217;Starship Troopers - Goodfilms&#8217; src=&#8217;http://files.goodfil.ms/starship troopers - goodfilms graph.png&#8217;&gt;
  &lt;figcaption&gt;Starship Troopers&lt;/figcaption&gt;
&lt;/figure&gt;


&lt;p&gt;Now, the top of the graph is &amp;#8216;highly rewatchable&amp;#8217;, and the right of the graph is &amp;#8216;high quality&amp;#8217;. So, these ratings say that there&amp;#8217;s a lot of disagreement over whether it&amp;#8217;s high quality of not, but generally this scores high-rewatchability. So, maybe not the most intelligent movie, but good fun.&lt;/p&gt;

&lt;figure&gt;
  &lt;img title=&#8217;Fifth Element - Goodfilms&#8217; src=&#8217;http://files.goodfil.ms/fifth element - goodfilms graph.png&#8217;&gt;
  &lt;figcaption&gt;Fifth Element&lt;/figcaption&gt;
&lt;/figure&gt;


&lt;p&gt;Here we have a cluster in the top right of the graph around 4/5 quality and 4/5 rewatchability. But more interestingly, almost nobody thinks this film is worse than 3/5 in either dimension. Which is great - it means you&amp;#8217;re likely to enjoy this movie.&lt;/p&gt;

&lt;figure&gt;
  &lt;img title=&#8217;Blade Runner - Goodfilms&#8217; src=&#8217;http://files.goodfil.ms/blade runner - goodfilms graph.png&#8217;&gt;
  &lt;figcaption&gt;Blade Runner&lt;/figcaption&gt;
&lt;/figure&gt;


&lt;p&gt;Blade Runner clearly has the highest quality film of the three, but doesn&amp;#8217;t have as many high-rewatchability scores as Fifth Element. This happens with a lot of classics (which Blade Runner is, without a doubt) - they&amp;#8217;re very interesting and rewarding to watch, but you wouldn&amp;#8217;t rush out to see it again. So, watch this if you&amp;#8217;re in the mood for something really good.&lt;/p&gt;

&lt;h2&gt;We&amp;#8217;ve done better&lt;/h2&gt;

&lt;p&gt;The point of this is &lt;strong&gt;we can do better than a 5-star ratings system&lt;/strong&gt;, and at Goodfilms we think that two ratings, quality and rewatchability, is the right one for films. We&amp;#8217;d love to know if you agree, and we would really love to hear about other people who are trying their own novel ratings systems for their own domains. Let&amp;#8217;s move past the simple 5-star system, shall we?&lt;/p&gt;

&lt;h2&gt;Goodfilms&lt;/h2&gt;

&lt;p&gt;Goodfilms is a way to share the movies you watch with your friends. We rate movies on two criteria - &amp;#8216;quality&amp;#8217; and &amp;#8216;rewatchability&amp;#8217;, so you can admit to your guilty pleasures and properly capture the feeling you get when a film leaves you exhausted. Sign up now and keep track of the films you love, and find great, challenging or silly new ones to watch.&lt;/p&gt;

&lt;div class=&quot;okgo&quot;&gt;
  &lt;a class=&quot;btn-auth btn-facebook large&quot; href=&quot;http://goodfil.ms/users/auth/facebook&quot;&gt;
    Sign up with
    &lt;strong&gt;Facebook&lt;/strong&gt;
  &lt;/a&gt;
  &lt;a class=&quot;btn-auth btn-twitter large&quot; href=&quot;http://goodfil.ms/users/auth/twitter&quot;&gt;
    Sign up with
    &lt;strong&gt;Twitter&lt;/strong&gt;
  &lt;/a&gt;
&lt;/div&gt;

</content>
  </entry>
  
  <entry>
    <title>AngularJS and the Goodfilms mobile site - Part 1</title>
    <link href="http://goodfil.ms/blog/posts/2012/08/13/angularjs-and-the-goodfilms-mobile-site-part-1/"/>
    <updated>2012-08-13T14:00:00+10:00</updated>
    <id>http://goodfil.ms/blog/posts/2012/08/13/angularjs-and-the-goodfilms-mobile-site-part-1</id>
    <content type="html">&lt;p&gt;Following on from &lt;a href=&quot;http://goodfil.ms/blog/posts/2012/08/09/goodfilms-goes-mobile/&quot;&gt;the release of the Goodfilms mobile site&lt;/a&gt;, we received a lot of requests for a more detailed post on how we used AngularJS, and what we thought of it. In this Part 1 we&amp;#8217;ll talk about how we use Directives to isolate some mobile-specific logic and keep them maintainable.&lt;/p&gt;

&lt;p&gt;Before we get started, let&amp;#8217;s take a look at what the mobile site looks like:&lt;/p&gt;

&lt;iframe width=&quot;375&quot; height=&quot;500&quot; src=&quot;http://www.youtube.com/embed/OgpJu0vnFBU&quot; frameborder=&quot;0&quot; allowfullscreen&gt;&lt;/iframe&gt;




&lt;!&#8211; more &#8211;&gt;


&lt;h2&gt;Choosing AngularJS&lt;/h2&gt;

&lt;p&gt;We evaluated Backbone, BatmanJS and EmberJS before settling on AngularJS. Once we found Angular we stopped looking at alternatives - even after the first few experiments we found it to be way out in front in the client-side framework arms race.&lt;/p&gt;

&lt;p&gt;The most immediate difference was that your application is &lt;strong&gt;fundamentally described in the HTML&lt;/strong&gt;. Rather than defining an application as a hierarchy of JS &amp;#8216;classes&amp;#8217;, then an implicit or explicit render step to populate the DOM, you describe a great deal of the functionality of the site in HTML. This was a little jarring, and almost reminiscent of adding ugly snippets like &lt;code&gt;onclick=&#8217;callGlobalFunction(); return false;&#8217;&lt;/code&gt; to your &lt;code&gt;&amp;lt;a&amp;gt;&lt;/code&gt; tags. But in practice, it has none of the nastiness that approach leads to, due to the cleverness of coupling Controllers to sections of the DOM, and the genius of Directives.&lt;/p&gt;

&lt;p&gt;If you are interested in learning more about the concepts in AngularJS, the documentation and examples on the &lt;a href=&quot;http://angularjs.org&quot;&gt;official site&lt;/a&gt; are excellent. You could also check out the &lt;a href=&quot;http://sup-angularjs.herokuapp.com/&quot;&gt;presentation I gave to the Melbourne Rails meetup&lt;/a&gt;, which is itself an Angular app (&lt;a href=&quot;https://github.com/geelen/angular_presentation&quot;&gt;source here&lt;/a&gt;).&lt;/p&gt;

&lt;h2&gt;Controllers - your neatly scoped friend&lt;/h2&gt;

&lt;p&gt;The golden rule of Controllers is to keep DOM manipulation out of them. Instead, you update a &lt;code&gt;$scope&lt;/code&gt; object and Angular propagates those changes to the DOM. In practice, Controllers map really well to the functional components of your application - in Goodfilms mobile, there are six,1 three of which are visible here:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://files.goodfil.ms/goodfilms_iphone_components.png&quot; alt=&quot;AngularJS hierarchy view&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The other three are FilmDetail, Search and Queue. You can see them in action in the video at the top. We&amp;#8217;ll be mostly focussing on the top level of the application in this part - the FrameController:&lt;/p&gt;

&lt;figure class=&#8217;code&#8217;&gt;&lt;figcaption&gt;&lt;span&gt;The app structure&lt;/span&gt;&lt;/figcaption&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre class=&quot;line-numbers&quot;&gt;&lt;span class=&#8217;line-number&#8217;&gt;1&lt;/span&gt;
&lt;span class=&#8217;line-number&#8217;&gt;2&lt;/span&gt;
&lt;span class=&#8217;line-number&#8217;&gt;3&lt;/span&gt;
&lt;span class=&#8217;line-number&#8217;&gt;4&lt;/span&gt;
&lt;span class=&#8217;line-number&#8217;&gt;5&lt;/span&gt;
&lt;span class=&#8217;line-number&#8217;&gt;6&lt;/span&gt;
&lt;span class=&#8217;line-number&#8217;&gt;7&lt;/span&gt;
&lt;span class=&#8217;line-number&#8217;&gt;8&lt;/span&gt;
&lt;span class=&#8217;line-number&#8217;&gt;9&lt;/span&gt;
&lt;span class=&#8217;line-number&#8217;&gt;10&lt;/span&gt;
&lt;span class=&#8217;line-number&#8217;&gt;11&lt;/span&gt;
&lt;span class=&#8217;line-number&#8217;&gt;12&lt;/span&gt;
&lt;span class=&#8217;line-number&#8217;&gt;13&lt;/span&gt;
&lt;span class=&#8217;line-number&#8217;&gt;14&lt;/span&gt;
&lt;span class=&#8217;line-number&#8217;&gt;15&lt;/span&gt;
&lt;span class=&#8217;line-number&#8217;&gt;16&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#8217;code&#8217;&gt;&lt;pre&gt;&lt;code class=&#8217;html&#8217;&gt;&lt;span class=&#8217;line&#8217;&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;body&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;ng-controller=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;FrameController&amp;quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;span class=&#8217;line&#8217;&gt;
&lt;/span&gt;&lt;span class=&#8217;line&#8217;&gt;  &lt;span class=&quot;nt&quot;&gt;&amp;lt;header&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;ng-class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;{menuopen: menuopen}&amp;quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;span class=&#8217;line&#8217;&gt;    &lt;span class=&quot;nt&quot;&gt;&amp;lt;div&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;header&amp;quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt; &#8230; &lt;span class=&quot;nt&quot;&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;span class=&#8217;line&#8217;&gt;    &lt;span class=&quot;nt&quot;&gt;&amp;lt;nav&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;menu&amp;quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt; &#8230; &lt;span class=&quot;nt&quot;&gt;&amp;lt;/nav&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;span class=&#8217;line&#8217;&gt;  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/header&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;span class=&#8217;line&#8217;&gt;
&lt;/span&gt;&lt;span class=&#8217;line&#8217;&gt;  &lt;span class=&quot;nt&quot;&gt;&amp;lt;div&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;main&amp;quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;ng-class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;{menuopen: menuopen}&amp;quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;span class=&#8217;line&#8217;&gt;    &lt;span class=&quot;nt&quot;&gt;&amp;lt;div&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;stage&amp;quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;ng-view&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;span class=&#8217;line&#8217;&gt;  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;span class=&#8217;line&#8217;&gt;
&lt;/span&gt;&lt;span class=&#8217;line&#8217;&gt;  &lt;span class=&quot;nt&quot;&gt;&amp;lt;script &lt;/span&gt;&lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;text/ng-template&amp;quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;/views/feed.html&amp;quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&#8230;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;span class=&#8217;line&#8217;&gt;  &lt;span class=&quot;nt&quot;&gt;&amp;lt;script &lt;/span&gt;&lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;text/ng-template&amp;quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;/views/film.html&amp;quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&#8230;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;span class=&#8217;line&#8217;&gt;  &lt;span class=&quot;nt&quot;&gt;&amp;lt;script &lt;/span&gt;&lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;text/ng-template&amp;quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;/views/list.html&amp;quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&#8230;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;span class=&#8217;line&#8217;&gt;
&lt;/span&gt;&lt;span class=&#8217;line&#8217;&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;


&lt;p&gt;The FrameController is scoped to this section of the DOM, but not to any domain objects yet, which means its only job really is to update its &lt;code&gt;$scope&lt;/code&gt;. Let&amp;#8217;s look at a simple task - showing/hiding the left menu drawer.&lt;/p&gt;

&lt;p&gt;In the HTML above, we add the &lt;code&gt;ng-class=&quot;{menuopen: menuopen}&quot;&lt;/code&gt; declaration to a couple of elements, and Angular ensures that if variable &amp;#8216;menuopen&amp;#8217; is truthy, the class &amp;#8216;menuopen&amp;#8217; will be set on the header and main. So, with the following controller code and css:&lt;/p&gt;

&lt;figure class=&#8217;code&#8217;&gt;&lt;figcaption&gt;&lt;span&gt;frame_controller.js&lt;/span&gt;&lt;/figcaption&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre class=&quot;line-numbers&quot;&gt;&lt;span class=&#8217;line-number&#8217;&gt;1&lt;/span&gt;
&lt;span class=&#8217;line-number&#8217;&gt;2&lt;/span&gt;
&lt;span class=&#8217;line-number&#8217;&gt;3&lt;/span&gt;
&lt;span class=&#8217;line-number&#8217;&gt;4&lt;/span&gt;
&lt;span class=&#8217;line-number&#8217;&gt;5&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#8217;code&#8217;&gt;&lt;pre&gt;&lt;code class=&#8217;javascript&#8217;&gt;&lt;span class=&#8217;line&#8217;&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;FrameController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;$scope&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;span class=&#8217;line&#8217;&gt;  &lt;span class=&quot;nx&quot;&gt;$scope&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;toggleMenu&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;span class=&#8217;line&#8217;&gt;    &lt;span class=&quot;nx&quot;&gt;$scope&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;menuopen&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;$scope&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;menuopen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;span class=&#8217;line&#8217;&gt;  &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;span class=&#8217;line&#8217;&gt;&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;




&lt;figure class=&#8217;code&#8217;&gt;&lt;figcaption&gt;&lt;span&gt;frame.css&lt;/span&gt;&lt;/figcaption&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre class=&quot;line-numbers&quot;&gt;&lt;span class=&#8217;line-number&#8217;&gt;1&lt;/span&gt;
&lt;span class=&#8217;line-number&#8217;&gt;2&lt;/span&gt;
&lt;span class=&#8217;line-number&#8217;&gt;3&lt;/span&gt;
&lt;span class=&#8217;line-number&#8217;&gt;4&lt;/span&gt;
&lt;span class=&#8217;line-number&#8217;&gt;5&lt;/span&gt;
&lt;span class=&#8217;line-number&#8217;&gt;6&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#8217;code&#8217;&gt;&lt;pre&gt;&lt;code class=&#8217;css&#8217;&gt;&lt;span class=&#8217;line&#8217;&gt;&lt;span class=&quot;nt&quot;&gt;body&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;header&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;#main&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;span class=&#8217;line&#8217;&gt;  &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;webkit&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;transition&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0.3s&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;webkit&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;transform&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;span class=&#8217;line&#8217;&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;span class=&#8217;line&#8217;&gt;&lt;span class=&quot;nc&quot;&gt;.menuopen&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;span class=&#8217;line&#8217;&gt;  &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;webkit&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;transform&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;translate3d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;280px&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;span class=&#8217;line&#8217;&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;


&lt;p&gt;We have a nice way to slide the menu (part of the header tag) in from the left, and slide the main content away. But we need a way to call &lt;code&gt;toggleMenu()&lt;/code&gt; from the DOM. This is where AngularJS really shines.&lt;/p&gt;

&lt;h2&gt;Tapping vs Clicking&lt;/h2&gt;

&lt;p&gt;One of the most commonly-encountered differences between desktop and mobile is the handling of click events. We&amp;#8217;re primarily targeting iOS Safari, which will send three events on a simple tap. These are &amp;#8216;touchstart&amp;#8217; and &amp;#8216;touchend&amp;#8217;, which fire immediately as you, well, start and stop touching. Then, Safari waits 300ms to see if you&amp;#8217;re double tapping, and if not, sends a &amp;#8216;click&amp;#8217; event. If you only use click events, the user experience is a 300ms pause before the app responds to every tap. It&amp;#8217;s a big contributor to the sense of &amp;#8216;non-nativeness&amp;#8217; that mobile web apps can have, and we can do better.&lt;/p&gt;

&lt;p&gt;How does this look in an Angular app? Well, the normal, desktop way of toggling our menu would be to use &lt;code&gt;ng-click&lt;/code&gt;:&lt;/p&gt;

&lt;figure class=&#8217;code&#8217;&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre class=&quot;line-numbers&quot;&gt;&lt;span class=&#8217;line-number&#8217;&gt;1&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#8217;code&#8217;&gt;&lt;pre&gt;&lt;code class=&#8217;html&#8217;&gt;&lt;span class=&#8217;line&#8217;&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;div&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;icon menu&amp;quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;ng-click=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;#39;toggleMenu()&amp;#39;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;


&lt;p&gt;Clicking on this icon will fire the &lt;code&gt;toggleMenu()&lt;/code&gt; action on the controller and AngularJS will set the classes on our elements. And the CSS transition will neatly slide the elements into place. But this is for a click event - Angular provides no built-in way of handling touch events. Oh gnoes! What are we to do? Oh wait, it&amp;#8217;s unbelievably easy.&lt;/p&gt;

&lt;h2&gt;Directives - DOM magic begone!&lt;/h2&gt;

&lt;p&gt;Well, the DOM magic is still there, it&amp;#8217;s just beautifully isolated. Let&amp;#8217;s start with the naïve example, where you just bind to touchstart. This will at least get us around the 300ms delay:&lt;/p&gt;

&lt;figure class=&#8217;code&#8217;&gt;&lt;figcaption&gt;&lt;span&gt;Your first Directive&lt;/span&gt;&lt;/figcaption&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre class=&quot;line-numbers&quot;&gt;&lt;span class=&#8217;line-number&#8217;&gt;1&lt;/span&gt;
&lt;span class=&#8217;line-number&#8217;&gt;2&lt;/span&gt;
&lt;span class=&#8217;line-number&#8217;&gt;3&lt;/span&gt;
&lt;span class=&#8217;line-number&#8217;&gt;4&lt;/span&gt;
&lt;span class=&#8217;line-number&#8217;&gt;5&lt;/span&gt;
&lt;span class=&#8217;line-number&#8217;&gt;6&lt;/span&gt;
&lt;span class=&#8217;line-number&#8217;&gt;7&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#8217;code&#8217;&gt;&lt;pre&gt;&lt;code class=&#8217;javascript&#8217;&gt;&lt;span class=&#8217;line&#8217;&gt;&lt;span class=&quot;nx&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;directive&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;gfTap&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;span class=&#8217;line&#8217;&gt;  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;scope&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;element&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;attrs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;span class=&#8217;line&#8217;&gt;    &lt;span class=&quot;nx&quot;&gt;element&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;bind&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;touchstart&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;span class=&#8217;line&#8217;&gt;      &lt;span class=&quot;nx&quot;&gt;scope&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;$apply&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;attrs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;gfTap&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]);&lt;/span&gt;
&lt;/span&gt;&lt;span class=&#8217;line&#8217;&gt;    &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;span class=&#8217;line&#8217;&gt;  &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;span class=&#8217;line&#8217;&gt;&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;


&lt;p&gt;In the above, &lt;code&gt;app&lt;/code&gt; is the Angular app object, though it could just as easily be a service you mix in to your Controller if you want to isolate it. The signature &lt;code&gt;return function(scope, element, attrs) {&#8230;};&lt;/code&gt; is the simplest-possible directive syntax, very useful when all you want to do is bind an event.&lt;/p&gt;

&lt;p&gt;Before going on, I&amp;#8217;ll present the same code in Coffeescript, since that&amp;#8217;s how the Goodfilms app is written, and because I think the two work together so well. I disliked BatmanJS because it forced you to go full-coffeescript, and I appreciate that Angular has made an effort to make a beautiful Javascript library, which simply gets more concise by switching to coffeescript. Here&amp;#8217;s the same, naïve example:&lt;/p&gt;

&lt;figure class=&#8217;code&#8217;&gt;&lt;figcaption&gt;&lt;span&gt;Your first Directive, in Coffee&lt;/span&gt;&lt;/figcaption&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre class=&quot;line-numbers&quot;&gt;&lt;span class=&#8217;line-number&#8217;&gt;1&lt;/span&gt;
&lt;span class=&#8217;line-number&#8217;&gt;2&lt;/span&gt;
&lt;span class=&#8217;line-number&#8217;&gt;3&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#8217;code&#8217;&gt;&lt;pre&gt;&lt;code class=&#8217;coffeescript&#8217;&gt;&lt;span class=&#8217;line&#8217;&gt;&lt;span class=&quot;nx&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;directive&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;#39;gfTap&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;span class=&#8217;line&#8217;&gt;  &lt;span class=&quot;nf&quot;&gt;(scope, element, attrs) -&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;span class=&#8217;line&#8217;&gt;    &lt;span class=&quot;nx&quot;&gt;element&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;bind&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;#39;touchstart&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;scope&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;$apply&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;attrs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;#39;gfTap&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;


&lt;p&gt;This, I think, is easier to understand, and it says that the &amp;#8216;gfTap&amp;#8217; directive means execute &lt;code&gt;scope.$apply(attrs[&#8216;gfTap&#8217;])&lt;/code&gt; on &amp;#8216;touchstart&amp;#8217;. That snippet is how Angular internally calls a Controller function - it&amp;#8217;s the same for &lt;code&gt;ng-click&lt;/code&gt;. We can update our HTML to use our directive:&lt;/p&gt;

&lt;figure class=&#8217;code&#8217;&gt;&lt;figcaption&gt;&lt;span&gt;Using our directive&lt;/span&gt;&lt;/figcaption&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre class=&quot;line-numbers&quot;&gt;&lt;span class=&#8217;line-number&#8217;&gt;1&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#8217;code&#8217;&gt;&lt;pre&gt;&lt;code class=&#8217;html&#8217;&gt;&lt;span class=&#8217;line&#8217;&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;div&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;icon menu&amp;quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;gf-tap=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;#39;toggleMenu()&amp;#39;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;


&lt;p&gt;Now our &lt;code&gt;toggleMenu()&lt;/code&gt; method is being called but without waiting for a click event. In fact, anywhere you were going to use an &lt;code&gt;ng-click&lt;/code&gt; you can now use a &lt;code&gt;gf-tap&lt;/code&gt;, and you&amp;#8217;ve beaten the 300ms delay on mobile &lt;strong&gt;without complicating your HTML or your Controller&lt;/strong&gt;. Boom!&lt;/p&gt;

&lt;h2&gt;Directives isolate complexity&lt;/h2&gt;

&lt;p&gt;There&amp;#8217;s a problem with our naïve &lt;code&gt;gf-tap&lt;/code&gt; directive, however, which is that when scrolling or swiping, the &amp;#8216;touchstart&amp;#8217; event is still triggered, &lt;strong&gt;but it isn&amp;#8217;t a tap&lt;/strong&gt;. Safari will actually send a &amp;#8216;touchmove&amp;#8217; event in these cases, so we should prevent the Controller action from being fired if we detect one:&lt;/p&gt;

&lt;figure class=&#8217;code&#8217;&gt;&lt;figcaption&gt;&lt;span&gt;A smarter gf-tap&lt;/span&gt;&lt;/figcaption&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre class=&quot;line-numbers&quot;&gt;&lt;span class=&#8217;line-number&#8217;&gt;1&lt;/span&gt;
&lt;span class=&#8217;line-number&#8217;&gt;2&lt;/span&gt;
&lt;span class=&#8217;line-number&#8217;&gt;3&lt;/span&gt;
&lt;span class=&#8217;line-number&#8217;&gt;4&lt;/span&gt;
&lt;span class=&#8217;line-number&#8217;&gt;5&lt;/span&gt;
&lt;span class=&#8217;line-number&#8217;&gt;6&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#8217;code&#8217;&gt;&lt;pre&gt;&lt;code class=&#8217;coffeescript&#8217;&gt;&lt;span class=&#8217;line&#8217;&gt;&lt;span class=&quot;nx&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;directive&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;#39;gfTap&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;span class=&#8217;line&#8217;&gt;  &lt;span class=&quot;nf&quot;&gt;(scope, element, attrs) -&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;span class=&#8217;line&#8217;&gt;    &lt;span class=&quot;nv&quot;&gt;tapping = &lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;span class=&#8217;line&#8217;&gt;    &lt;span class=&quot;nx&quot;&gt;element&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;bind&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;#39;touchstart&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;tapping = &lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;span class=&#8217;line&#8217;&gt;    &lt;span class=&quot;nx&quot;&gt;element&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;bind&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;#39;touchmove&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;tapping = &lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;span class=&#8217;line&#8217;&gt;    &lt;span class=&quot;nx&quot;&gt;element&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;bind&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;#39;touchend&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;scope&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;$apply&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;attrs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;#39;gfTap&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;tapping&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;


&lt;p&gt;What we&amp;#8217;ve done here is a fixed a bug in our javascript by making our &lt;code&gt;gf-tap&lt;/code&gt; directive more sophisticated, but we &lt;strong&gt;didn&amp;#8217;t need to change anything else&lt;/strong&gt;. That&amp;#8217;s a sign of clean, maintainable code, and I love that Angular makes it so easy.&lt;/p&gt;

&lt;h2&gt;Once-off Directives&lt;/h2&gt;

&lt;p&gt;Directives aren&amp;#8217;t just for reusable event logic such as &lt;code&gt;gf-tap&lt;/code&gt; above, they&amp;#8217;re also ideal for interfacing with third-party libraries. We use &lt;a href=&quot;http://zeptojs.com/&quot;&gt;Zepto.js&lt;/a&gt; and its plugin &lt;a href=&quot;https://github.com/tomlongo/Flickable.js&quot;&gt;Flickable.js&lt;/a&gt; for the swiping of movie posters in the feed, and use a directive to wire it up. This is how it looks:&lt;/p&gt;

&lt;figure class=&#8217;code&#8217;&gt;&lt;figcaption&gt;&lt;span&gt;gf-flickable.js&lt;/span&gt;&lt;/figcaption&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre class=&quot;line-numbers&quot;&gt;&lt;span class=&#8217;line-number&#8217;&gt;1&lt;/span&gt;
&lt;span class=&#8217;line-number&#8217;&gt;2&lt;/span&gt;
&lt;span class=&#8217;line-number&#8217;&gt;3&lt;/span&gt;
&lt;span class=&#8217;line-number&#8217;&gt;4&lt;/span&gt;
&lt;span class=&#8217;line-number&#8217;&gt;5&lt;/span&gt;
&lt;span class=&#8217;line-number&#8217;&gt;6&lt;/span&gt;
&lt;span class=&#8217;line-number&#8217;&gt;7&lt;/span&gt;
&lt;span class=&#8217;line-number&#8217;&gt;8&lt;/span&gt;
&lt;span class=&#8217;line-number&#8217;&gt;9&lt;/span&gt;
&lt;span class=&#8217;line-number&#8217;&gt;10&lt;/span&gt;
&lt;span class=&#8217;line-number&#8217;&gt;11&lt;/span&gt;
&lt;span class=&#8217;line-number&#8217;&gt;12&lt;/span&gt;
&lt;span class=&#8217;line-number&#8217;&gt;13&lt;/span&gt;
&lt;span class=&#8217;line-number&#8217;&gt;14&lt;/span&gt;
&lt;span class=&#8217;line-number&#8217;&gt;15&lt;/span&gt;
&lt;span class=&#8217;line-number&#8217;&gt;16&lt;/span&gt;
&lt;span class=&#8217;line-number&#8217;&gt;17&lt;/span&gt;
&lt;span class=&#8217;line-number&#8217;&gt;18&lt;/span&gt;
&lt;span class=&#8217;line-number&#8217;&gt;19&lt;/span&gt;
&lt;span class=&#8217;line-number&#8217;&gt;20&lt;/span&gt;
&lt;span class=&#8217;line-number&#8217;&gt;21&lt;/span&gt;
&lt;span class=&#8217;line-number&#8217;&gt;22&lt;/span&gt;
&lt;span class=&#8217;line-number&#8217;&gt;23&lt;/span&gt;
&lt;span class=&#8217;line-number&#8217;&gt;24&lt;/span&gt;
&lt;span class=&#8217;line-number&#8217;&gt;25&lt;/span&gt;
&lt;span class=&#8217;line-number&#8217;&gt;26&lt;/span&gt;
&lt;span class=&#8217;line-number&#8217;&gt;27&lt;/span&gt;
&lt;span class=&#8217;line-number&#8217;&gt;28&lt;/span&gt;
&lt;span class=&#8217;line-number&#8217;&gt;29&lt;/span&gt;
&lt;span class=&#8217;line-number&#8217;&gt;30&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#8217;code&#8217;&gt;&lt;pre&gt;&lt;code class=&#8217;coffeescript&#8217;&gt;&lt;span class=&#8217;line&#8217;&gt;&lt;span class=&quot;nv&quot;&gt;flickable = &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;angular&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;module&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;flickable&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[])&lt;/span&gt;
&lt;/span&gt;&lt;span class=&#8217;line&#8217;&gt;&lt;span class=&quot;nx&quot;&gt;flickable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;directive&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;#39;gfFlickable&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;span class=&#8217;line&#8217;&gt;  &lt;span class=&quot;nf&quot;&gt;(scope, element, attrs) -&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;span class=&#8217;line&#8217;&gt;    &lt;span class=&quot;nv&quot;&gt;el = &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;element&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# get a Zepto element&lt;/span&gt;
&lt;/span&gt;&lt;span class=&#8217;line&#8217;&gt;
&lt;/span&gt;&lt;span class=&#8217;line&#8217;&gt;    &lt;span class=&quot;c1&quot;&gt;# initial state - the first is selected&lt;/span&gt;
&lt;/span&gt;&lt;span class=&#8217;line&#8217;&gt;    &lt;span class=&quot;nv&quot;&gt;scope.selectedIndex = &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;span class=&#8217;line&#8217;&gt;    &lt;span class=&quot;nv&quot;&gt;selectedEl = &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;el&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;children&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;scope&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;selectedIndex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
&lt;/span&gt;&lt;span class=&#8217;line&#8217;&gt;
&lt;/span&gt;&lt;span class=&#8217;line&#8217;&gt;    &lt;span class=&quot;nv&quot;&gt;tapping = &lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;span class=&#8217;line&#8217;&gt;    &lt;span class=&quot;nx&quot;&gt;el&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;flickable&lt;/span&gt;
&lt;/span&gt;&lt;span class=&#8217;line&#8217;&gt;      &lt;span class=&quot;nv&quot;&gt;segmentPx: &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;245&lt;/span&gt;
&lt;/span&gt;&lt;span class=&#8217;line&#8217;&gt;
&lt;/span&gt;&lt;span class=&#8217;line&#8217;&gt;      &lt;span class=&quot;c1&quot;&gt;# Once again, we detect a tap event and show/hide the buttons&lt;/span&gt;
&lt;/span&gt;&lt;span class=&#8217;line&#8217;&gt;      &lt;span class=&quot;nv&quot;&gt;onStart: &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;tapping = &lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;span class=&#8217;line&#8217;&gt;      &lt;span class=&quot;nv&quot;&gt;onMove: &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;tapping = &lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;span class=&#8217;line&#8217;&gt;      &lt;span class=&quot;nv&quot;&gt;onEnd: &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;selectedEl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;toggleClass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;#39;showbuttons&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;tapping&lt;/span&gt;
&lt;/span&gt;&lt;span class=&#8217;line&#8217;&gt;
&lt;/span&gt;&lt;span class=&#8217;line&#8217;&gt;      &lt;span class=&quot;c1&quot;&gt;# If we&amp;#39;re moving to a new element, update everything&lt;/span&gt;
&lt;/span&gt;&lt;span class=&#8217;line&#8217;&gt;      &lt;span class=&quot;nv&quot;&gt;onScroll: &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;(eventData, newSelectedIndex) -&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;span class=&#8217;line&#8217;&gt;        &lt;span class=&quot;c1&quot;&gt;# Hide the buttons on the old one&lt;/span&gt;
&lt;/span&gt;&lt;span class=&#8217;line&#8217;&gt;        &lt;span class=&quot;nx&quot;&gt;selectedEl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;removeClass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;#39;selected showbuttons&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class=&#8217;line&#8217;&gt;
&lt;/span&gt;&lt;span class=&#8217;line&#8217;&gt;        &lt;span class=&quot;c1&quot;&gt;# Show the buttons on the new one&lt;/span&gt;
&lt;/span&gt;&lt;span class=&#8217;line&#8217;&gt;        &lt;span class=&quot;nv&quot;&gt;scope.selectedIndex = &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;newSelectedIndex&lt;/span&gt;
&lt;/span&gt;&lt;span class=&#8217;line&#8217;&gt;        &lt;span class=&quot;nx&quot;&gt;selectedEl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;addClass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;#39;selected showbuttons&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class=&#8217;line&#8217;&gt;
&lt;/span&gt;&lt;span class=&#8217;line&#8217;&gt;        &lt;span class=&quot;c1&quot;&gt;# Tell the rest of our app&lt;/span&gt;
&lt;/span&gt;&lt;span class=&#8217;line&#8217;&gt;        &lt;span class=&quot;nx&quot;&gt;scope&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;didFlick&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;span class=&#8217;line&#8217;&gt;        &lt;span class=&quot;nx&quot;&gt;scope&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;$apply&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;


&lt;figure class=&#8217;code&#8217;&gt;&lt;figcaption&gt;&lt;span&gt;feed.html&lt;/span&gt;&lt;/figcaption&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre class=&quot;line-numbers&quot;&gt;&lt;span class=&#8217;line-number&#8217;&gt;1&lt;/span&gt;
&lt;span class=&#8217;line-number&#8217;&gt;2&lt;/span&gt;
&lt;span class=&#8217;line-number&#8217;&gt;3&lt;/span&gt;
&lt;span class=&#8217;line-number&#8217;&gt;4&lt;/span&gt;
&lt;span class=&#8217;line-number&#8217;&gt;5&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#8217;code&#8217;&gt;&lt;pre&gt;&lt;code class=&#8217;html&#8217;&gt;&lt;span class=&#8217;line&#8217;&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;ul&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;feed&amp;quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;gf-flickable&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;span class=&#8217;line&#8217;&gt;  &lt;span class=&quot;nt&quot;&gt;&amp;lt;li&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;event&amp;quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;ng-repeat=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;event in events&amp;quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;span class=&#8217;line&#8217;&gt;    &#8230;
&lt;/span&gt;&lt;span class=&#8217;line&#8217;&gt;  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;span class=&#8217;line&#8217;&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;/ul&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;


&lt;p&gt;The key here is that all the DOM manipulation is one place, and the controller action &lt;code&gt;didFlick()&lt;/code&gt; can do important stuff (in Goodfilms it lazily loads the next image to be displayed and fetches more events if you&amp;#8217;re near the end of the list) but &lt;strong&gt;the two don&amp;#8217;t overlap&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This example is good, I feel, because the code within &lt;code&gt;gf-flickable.js&lt;/code&gt; is pretty similar to code you might be writing already, but Angular lets you encapsulate it in a directive, making your app more maintainable with hardly any effort. It means that the id of &amp;#8216;feed&amp;#8217; on your &lt;code&gt;ul&lt;/code&gt; isn&amp;#8217;t both used to attach events and styles, too, which is a bonus.&lt;/p&gt;

&lt;h2&gt;Wrap-up&lt;/h2&gt;

&lt;p&gt;We got a big benefit from Angular - it helped us focus on dealing with the various challenges of mobile touch interactions separately from modelling our domain logic and separately from our presentation. Those goals can certainly be met with any good JS framework, or even with disciplined framework-free JS, but we found Angular repeatedly led us to good solutions quickly, and with minimal code.&lt;/p&gt;

&lt;p&gt;Next time, we&amp;#8217;ll start by talking about what happens when the AngularJS abstraction breaks down, and we have to break our golden rule. Until then, please let us know your feedback, and if you haven&amp;#8217;t done so already, sign up to Goodfilms!&lt;/p&gt;

&lt;h2&gt;Goodfilms&lt;/h2&gt;

&lt;p&gt;Goodfilms is a way to share the movies you watch with your friends. We rate movies on two criteria - &amp;#8216;quality&amp;#8217; and &amp;#8216;rewatchability&amp;#8217;, so you can admit to your guilty pleasures and properly capture the feeling you get when a film leaves you exhausted. Sign up now and keep track of the films you love, and find great, challenging or silly new ones to watch.&lt;/p&gt;

&lt;div class=&quot;okgo&quot;&gt;
  &lt;a class=&quot;btn-auth btn-facebook large&quot; href=&quot;http://goodfil.ms/users/auth/facebook&quot;&gt;
    Sign up with
    &lt;strong&gt;Facebook&lt;/strong&gt;
  &lt;/a&gt;
  &lt;a class=&quot;btn-auth btn-twitter large&quot; href=&quot;http://goodfil.ms/users/auth/twitter&quot;&gt;
    Sign up with
    &lt;strong&gt;Twitter&lt;/strong&gt;
  &lt;/a&gt;
&lt;/div&gt;

</content>
  </entry>
  
  <entry>
    <title>Goodfilms goes mobile</title>
    <link href="http://goodfil.ms/blog/posts/2012/08/09/goodfilms-goes-mobile/"/>
    <updated>2012-08-09T14:00:00+10:00</updated>
    <id>http://goodfil.ms/blog/posts/2012/08/09/goodfilms-goes-mobile</id>
    <content type="html">&lt;p&gt;We&amp;#8217;re excited to announce the beta version of the Goodfilms mobile site! We&amp;#8217;d like to talk a bit about the goals for building this, and some of the design inspiration behind it. We&amp;#8217;ll also talk a little about how its put together using the absolutely excellent AngularJS.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://files.goodfil.ms/goodfilms_iphone_screenshot_instagram.jpeg&quot; alt=&quot;Goodfilms Mobile Feed&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The site&amp;#8217;s optimised to give existing users of our site access to their Queue whenever they need it and the ability to rate a film when they&amp;#8217;ve just seen it. And, of course, to help them keep up with what movies their friends have been watching, or want to go see.&lt;/p&gt;

&lt;!&#8211; more &#8211;&gt;


&lt;h2&gt;The process&lt;/h2&gt;

&lt;p&gt;We asked our users what they most wanted out of a mobile version of Goodfilms, and we came up with the following three use cases:&lt;/p&gt;

&lt;blockquote&gt;&lt;p&gt;I want to &lt;strong&gt;search for and rate&lt;/strong&gt; a film I&amp;#8217;ve just seen (while walking out of a cinema)&lt;/p&gt;

&lt;p&gt;I want to see &lt;strong&gt;what&amp;#8217;s in my queue&lt;/strong&gt; (while at the video shop or at a friend&amp;#8217;s house)&lt;/p&gt;

&lt;p&gt;I want to see &lt;strong&gt;what my friends have been watching&lt;/strong&gt; or queueing up, to give me ideas.&lt;/p&gt;&lt;/blockquote&gt;

&lt;p&gt;From this, we decided we&amp;#8217;d need 4 main elements:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A list of films, whether search results or those films in your queue.&lt;/li&gt;
&lt;li&gt;A detail view of a film.&lt;/li&gt;
&lt;li&gt;Navigation menu with search field&lt;/li&gt;
&lt;li&gt;The &amp;#8216;feed&amp;#8217;, much like the current Goodfilms home page.&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;Of these, the Feed presented the most variety of options, and the most challenges.&lt;/p&gt;

&lt;h2&gt;The Feed&lt;/h2&gt;

&lt;p&gt;As the main page in the desktop site, the Feed lets you follow which movies your friends are watching or plan to watch, and we wanted to maintain that as our users log in from their phone. This is what the feed looks like now:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://files.goodfil.ms/feed_screenshot_aug9.png&quot; alt=&quot;Goodfilms Feed&quot; /&gt;&lt;/p&gt;

&lt;p&gt;We love that the posters are the dominant visual element, and we wanted to carry that over to the mobile site. Layouts like Facebook work well for irregular information and conversations, but not necessarily posters.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://files.goodfil.ms/facebook_iphone_screenshot.png&quot; alt=&quot;Facebook Mobile Site&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Instagram, on the other hand, because each activity is based around a square image, devotes an entire screen to each update, placing the image right in the middle.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://files.goodfil.ms/instagram_iphone_screenshot.png&quot; alt=&quot;Instagram Mobile Site&quot; /&gt;&lt;/p&gt;

&lt;p&gt;We adopted a one-activity-on-screen approach, but because movie posters are taller than they are wide, we went with a sideways swipe to see the next film:&lt;/p&gt;

&lt;p&gt;&lt;img title=&quot;Instagram Mobile Site&quot; src=&quot;http://files.goodfil.ms/goodfilms_iphone_feed_screenshot.png&quot; width=&quot;368&quot;&gt;&lt;/p&gt;

&lt;h2&gt;The Platform&lt;/h2&gt;

&lt;p&gt;The site is built with AngularJS, which saved us an incredible amount of time. It allows us to express our interactive components very naturally. We&amp;#8217;ll be blogging in more detail about the build, but we thought we&amp;#8217;d show a quick example of how that works.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://files.goodfil.ms/goodfilms_iphone_components.png&quot; alt=&quot;AngularJS hierarchy view&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The corresponding HTML:&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/3302211.js?file=goodfilms_angular_app.html&quot;&gt;&lt;/script&gt;


&lt;p&gt;The clear mapping between interactive elements and the subsection of the HTML that it applies to makes it far easier to decompose an interactive application into managable parts.&lt;/p&gt;

&lt;h2&gt;Goodfilms&lt;/h2&gt;

&lt;p&gt;Goodfilms is a way to share the movies you watch with your friends. We rate movies on two criteria - &amp;#8216;quality&amp;#8217; and &amp;#8216;rewatchability&amp;#8217;, so you can admit to your guilty pleasures and properly capture the feeling you get when a film leaves you exhausted. Sign up now and keep track of the films you love, and find great, challenging or silly new ones to watch.&lt;/p&gt;

&lt;div class=&quot;okgo&quot;&gt;
  &lt;a class=&quot;btn-auth btn-facebook large&quot; href=&quot;http://goodfil.ms/users/auth/facebook&quot;&gt;
    Sign up with
    &lt;strong&gt;Facebook&lt;/strong&gt;
  &lt;/a&gt;
  &lt;a class=&quot;btn-auth btn-twitter large&quot; href=&quot;http://goodfil.ms/users/auth/twitter&quot;&gt;
    Sign up with
    &lt;strong&gt;Twitter&lt;/strong&gt;
  &lt;/a&gt;
&lt;/div&gt;

</content>
  </entry>
  
  <entry>
    <title>New Feature: Get Notified When Friends Rate &amp; Queue Films Based on Your Activity</title>
    <link href="http://goodfil.ms/blog/posts/2012/07/20/get-notified-of-your-film-influence/"/>
    <updated>2012-07-20T14:00:00+10:00</updated>
    <id>http://goodfil.ms/blog/posts/2012/07/20/get-notified-of-your-film-influence</id>
    <content type="html">&lt;p&gt;Here at Goodfilms, we&amp;#8217;ve always believed a couple of things about films ratings. The first, the obvious one, &lt;a href=&quot;http://goodfil.ms/blog/blog/posts/2011/10/07/a-better-way-to-rate-films/&quot;&gt;is that ordinary rating scales are broken&lt;/a&gt;. The second, which is just as big an influence on us, but that we write about less often, is that how you hear about a film (and from whom) matters a lot.&lt;/p&gt;

&lt;p&gt;We&amp;#8217;ve been recording the way people interact with films after their friends for a little while now, and it&amp;#8217;s been amazing to watch. We can draw little &amp;#8220;family trees&amp;#8221; of who originally reviewed a film, then which of their friends rated it next, and then which of their friends rate it. It&amp;#8217;s like watching our own little zombie outbreak inside the computers. Once we&amp;#8217;ve collated a bit more data, we&amp;#8217;ll write a blog post about some of the interesting ways different films move through social groups.&lt;/p&gt;

&lt;!&#8211; more &#8211;&gt;


&lt;p&gt;In the mean time, we thought we&amp;#8217;d set something up so you can know when you&amp;#8217;re influencing your friends&amp;#8217; behaivour on the site with a set of emails that look a bit like this:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://files.goodfil.ms/queue-email.jpg&quot; alt=&quot;Goodfilms Queue Email&quot; /&gt;&lt;/p&gt;

&lt;p&gt;These emails trigger when anyone directly interacts with one of your ratings, or films in your queue. We&amp;#8217;ve set it up with some time blocking code, so don&amp;#8217;t worry about getting spammed with heaps of these emails if a friend goes and rates 100 films straight from your activity page. We&amp;#8217;ve had these emails set up for us on the team and our friends for a while now, and it&amp;#8217;s a lot of fun knowing who is doing things on the site directly because of your activity.&lt;/p&gt;

&lt;p&gt;We&amp;#8217;ll be turning these on gradually for everyone in small blocks over the next few days. In conjunction with these new emails, we&amp;#8217;ve put together a more detailed user settings page which will allow you to opt out of emails with more granularity. You&amp;#8217;ll find it under the &amp;#8220;Me&amp;#8221; menu when you&amp;#8217;re logged in, and the email bit will look a bit like this:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://files.goodfil.ms/email-preferences.jpg&quot; alt=&quot;Goodfilms Queue Email&quot; /&gt;&lt;/p&gt;

&lt;p&gt;We hope you all have as much fun with these emails as we&amp;#8217;ve been having. What would you guys think if we put together a leaderboard of who was most influential amongst your group of friends? Good idea, or would you all get too competitive?&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>Netflix Quietly Smothers 3rd Party App Ecosystem (updated)</title>
    <link href="http://goodfil.ms/blog/posts/2012/06/18/netflix-quietly-smothers-3rd-party-app-ecosystem/"/>
    <updated>2012-06-18T12:00:00+10:00</updated>
    <id>http://goodfil.ms/blog/posts/2012/06/18/netflix-quietly-smothers-3rd-party-app-ecosystem</id>
    <content type="html">&lt;p&gt;&lt;strong&gt; Update:&lt;/strong&gt; We&amp;#8217;ve received word that the new API Terms of Use aren&amp;#8217;t as sinister as on first glance. From &lt;a href=&quot;http://venturebeat.com/2012/06/18/netflix-api/&quot;&gt;VentureBeat&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;&lt;p&gt;We are not prohibiting sites from showing competing services, however we do not want anyone to use Netflix content such as titles and descriptions to advertise a competing service.&lt;/p&gt;

&lt;p&gt;We’re not prohibiting developers from monetizing their applications by selling them directly to consumers. We will not, however, permit resale of our information in a business-to-business fashion.&lt;/p&gt;&lt;/blockquote&gt;

&lt;p&gt;This definitely goes a long way to remove the uncertainty around the new API Terms. We feel that the inability for users to retrieve their viewing history and ratings remains an issue, but we&amp;#8217;re glad to learn that existing, mutually-beneficial uses of the API (such as &lt;a href=&quot;http://goodfil.ms/films/on_netflix&quot;&gt;ours&lt;/a&gt;) appear to be still valid.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt; Update 2: &lt;/strong&gt; &lt;a href=&quot;http://techcrunch.com/2012/06/18/netflix-api-changes/&quot;&gt;TechCrunch&lt;/a&gt; and &lt;a href=&quot;http://thenextweb.com/media/2012/06/18/netflix-clarifies-api-and-terms-changes-will-continue-to-allow-developers-to-charge-for-apps/&quot;&gt;TheNextWeb&lt;/a&gt; have now confirmed this. I wouldn&amp;#8217;t be surprised if we see an updated Terms of Use from Netflix in the near future to put the issue beyond doubt. We&amp;#8217;ll update here again if that occurs.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;On Friday afternoon, Netflix published a blog post announcing a breaking change to their API, and dedicated a small paragraph to the fact that their API terms of use had been updated. On a technical level, these changes will cripple many apps currently integrating with Netflix, but the legal changes may be even more significant. Netflix customers should be aware of not only the upcoming changes to any 3rd party apps they might use, but what this says about Netflix as a business.&lt;/p&gt;

&lt;!&#8211; more &#8211;&gt;


&lt;p&gt;You can find the announcement &lt;a href=&quot;http://developer.netflix.com/blog/read/Upcoming_Changes_to_the_Netflix_API_Program&quot;&gt;on their developer blog&lt;/a&gt;. What they &lt;em&gt;are&lt;/em&gt; announcing is that from September 15th you will no longer be able to export your viewing history and associated ratings through their API. All API endpoints that expose rental history, and other parts of the API that tangentially supply that information will be removed. All they&amp;#8217;ve said regarding motivation for that change is that they are changing their API so that it &amp;#8220;is aligned with our broader objectives&amp;#8221;. It appears Netflix considers what films you&amp;#8217;ve watched and what you thought of them as their data, not yours.&lt;/p&gt;

&lt;p&gt;More significant are the changes to the API terms of use, softly mentioned in the post as: &amp;#8220;The Terms of Use has been updated [&amp;#8230;].  To view the revised Terms of Use, go to http://developer.netflix.com/page/Api_terms_of_use.&amp;#8221;
Checking the &lt;a href=&quot;http://developer.netflix.com/page/Api_terms_of_use&quot;&gt;latest version&lt;/a&gt; against the &lt;a href=&quot;http://webcache.googleusercontent.com/search?q=cache:EImwPvOZ5-YJ:developer.netflix.com/page/Api_terms_of_use+&amp;amp;cd=1&amp;amp;hl=en&amp;amp;ct=clnk&amp;amp;gl=au&quot;&gt;google cache&lt;/a&gt;, we noticed several significant additions under section 1.9&lt;/p&gt;

&lt;p&gt;Under the new terms, the following are no longer allowed (&lt;em&gt;Update:&lt;/em&gt; Netflix clarification indicates that these are not targeting consumer-facing applications - see update at top of post):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;distribute Content to any third party other than directly to end users through your own Application;&lt;/li&gt;
&lt;li&gt;charge, directly or indirectly, any fee (including any unique, specific, or premium charges) for access to the Content or your integration of the APIs in your Application, or use the APIs to build an enterprise application (e.g., that you distribute to other companies);&lt;/li&gt;
&lt;li&gt;use or display Titles in an Application for search and discovery of content linking to competing services;&lt;/li&gt;
&lt;li&gt;use or display Title Metadata in an Application unless it is used solely to facilitate or enable the search and discovery of Netflix services. For example, if your Application enables users to search for the availability of a movie or TV show from Netflix as well as from other services, you may only display Title Metadata in association with the availability of the movie or TV show from Netflix, not the other services;&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;The first implication of these additions: if you decide you just want to create a &amp;#8220;Netflix&amp;#8221; app, and add significant value on top of the Netflix service, you cannot charge your users for that value. You can do something positive for Netflix, but not for yourself. There is no incentive for you to build something useful for Netflix customers, and if you&amp;#8217;ve already built your app, you have three months left.&lt;/p&gt;

&lt;p&gt;The second implication of these additions: if you want to play with Netflix, you must close your eyes and pretend that no other online streaming services exist, or might have films your users want to see. We know that a lot of our users are Netflix customers, but we also understand that they don&amp;#8217;t &lt;em&gt;only&lt;/em&gt; use Netflix. So we integrated with Netflix to help people find the best films to watch in whichever way best suits them. We think that&amp;#8217;s of mutual benefit to Netflix and our users.&lt;/p&gt;

&lt;p&gt;This looks to us like a play to keep users from moving their viewing history onto competing services, and to stop their users from seeing gaps in their content library that may be available for purchase elsewhere. While this might be in their short term interest, in the longer term, making it harder for people who want to spend money watching films legally online cannot be a good business decision.&lt;/p&gt;

&lt;p&gt;In an industry as fragmented and combative as online streaming, the consumer&amp;#8217;s experience is, sadly, often the first to be compromised. Please let us know your thoughts in the comments.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>Iphone Sneak Peek</title>
    <link href="http://goodfil.ms/blog/posts/2012/06/07/iphone-sneak-peek/"/>
    <updated>2012-06-07T16:12:00+10:00</updated>
    <id>http://goodfil.ms/blog/posts/2012/06/07/iphone-sneak-peek</id>
    <content type="html">&lt;p&gt;We&amp;#8217;ve put together a short video with a sneak peek of how the iPhone-optimised site is shaping up. We&amp;#8217;re really excited about the different possibilities a touchscreen gives us.&lt;/p&gt;

&lt;iframe class=&quot;embed video&quot; width=&quot;450&quot; height=&quot;780&quot; src=&quot;http://www.youtube.com/embed/v_FsgsQyhJc&quot; frameborder=&quot;0&quot; allowfullscreen&gt;&lt;/iframe&gt;


&lt;p&gt;We love feedback, so please tell us what you think in the comments.&lt;/p&gt;

&lt;!&#8211; more &#8211;&gt;


&lt;p&gt;And of course, if you&amp;#8217;re not signed up already, do so now:&lt;/p&gt;

&lt;div class=&quot;okgo&quot;&gt;
  &lt;a class=&quot;btn-auth btn-facebook large&quot; href=&quot;http://goodfil.ms/users/auth/facebook&quot;&gt;
    Sign up with
    &lt;strong&gt;Facebook&lt;/strong&gt;
  &lt;/a&gt;
  &lt;a class=&quot;btn-auth btn-twitter large&quot; href=&quot;http://goodfil.ms/users/auth/twitter&quot;&gt;
    Sign up with
    &lt;strong&gt;Twitter&lt;/strong&gt;
  &lt;/a&gt;
&lt;/div&gt;

</content>
  </entry>
  
  <entry>
    <title>May Roundup</title>
    <link href="http://goodfil.ms/blog/posts/2012/06/04/may-roundup/"/>
    <updated>2012-06-04T16:12:00+10:00</updated>
    <id>http://goodfil.ms/blog/posts/2012/06/04/may-roundup</id>
    <content type="html">&lt;p&gt;Wow, what an exciting month May was. Heaps of new users and plenty of activity on the site, and a lot of new features to talk about. Here&amp;#8217;s a bit of a summary:&lt;/p&gt;

&lt;!&#8211;more&#8211;&gt;


&lt;h2&gt;Netflix integration&lt;/h2&gt;

&lt;p&gt;You can connect your Netflix account to Goodfilms now:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://files.goodfil.ms/netflix_integration.jpg&quot; alt=&quot;Connect to Netflix&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Now, your Goodfilms and Netflix queues will stay in sync (for films available on Netflix Instant). There&amp;#8217;s also:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A list of &lt;a href=&quot;http://goodfil.ms/films/on_netflix&quot;&gt;all the movies to watch on Netflix&lt;/a&gt;, sorted by popularity.&lt;/li&gt;
&lt;li&gt;The &lt;a href=&quot;http://goodfil.ms/queue/netflix&quot;&gt;films in your queue that are on Netflix&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;We hope that helps you get more out of your Netflix subscription each month. It certainly has done for us!&lt;/p&gt;

&lt;h2&gt;Mobile site&lt;/h2&gt;

&lt;p&gt;We&amp;#8217;ve been working hard on a mobile-accessible version of the site, so you can rate a film as soon as you walk out of a cinema, or access your queue from the couch or in the video store. It&amp;#8217;s been really fun to experiment with different designs that work on a touchscreen. Here&amp;#8217;s a sneak preview:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://files.goodfil.ms/iphone_interface.png&quot; alt=&quot;Mobile site preview&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Expect more on this in the coming weeks.&lt;/p&gt;

&lt;h2&gt;A great review on AppStorm&lt;/h2&gt;

&lt;p&gt;We were lucky enough to get &lt;a href=&quot;http://web.appstorm.net/reviews/social-networking/better-movie-rating-with-goodfil-ms/&quot;&gt;featured on Web.Appstorm&lt;/a&gt; that goes into an impressive amount of detail about our rating system and how the site works, and we&amp;#8217;re extremely humbled by the 10/10 score we were given.&lt;/p&gt;

&lt;p&gt;We were also interviewed by the Appstorm guys a little earlier, so if you&amp;#8217;d like to read about how two guys from Melbourne, Australia are trying to change the way films are rated and shared, you can read the full interview &lt;a href=&quot;http://web.appstorm.net/general/interviews/interview-meet-the-goodfilms-team/&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;International users&lt;/h2&gt;

&lt;p&gt;Buoyed by coverage by &lt;a href=&quot;http://wwwhatsnew.com/2012/05/15/goodfil-ms-vota-guarda-comenta-y-descubre-peliculas-con-los-amigos/#.T7LUMPzY-Dc.twitter&quot;&gt;wwhat&amp;#8217;s new&lt;/a&gt;, &lt;a href=&quot;http://www.tecnobita.com/2012/05/goodfil-ms-red-social-para-los-amantes-del-cine/?utm_source=dlvr.it&amp;amp;utm_medium=twitter&quot;&gt;Tecno Bita&lt;/a&gt; and &lt;a href=&quot;http://www.revistacafecolao.com/Mundo-Geek/goodfilms-la-red-social-para-los-amantes-del-cine.html&quot;&gt;Café Colao&lt;/a&gt; in Spain and &lt;a href=&quot;http://www.lifehacker.jp/2012/05/120524goodfilms.html&quot;&gt;Lifehacker Japan&lt;/a&gt; last month, we&amp;#8217;ve seen a huge influx in non-English-speaking users.&lt;/p&gt;

&lt;p&gt;It&amp;#8217;s been great to see so many users from around the world, and it&amp;#8217;s made us realise how important it is to translate Goodfilms into different languages. We&amp;#8217;re also planning to support localised movie posters and titles. Unfortunately we can&amp;#8217;t promise that this will be live soon, but we want everyone to know we&amp;#8217;re working on it!&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;That&amp;#8217;s it for the wrap-up this month. Expect many new exciting things in the months to come. And, of course, if you haven&amp;#8217;t already, sign up for Goodfilms now:&lt;/p&gt;

&lt;div class=&quot;okgo&quot;&gt;
  &lt;a class=&quot;btn-auth btn-facebook large&quot; href=&quot;http://goodfil.ms/users/auth/facebook&quot;&gt;
    Sign up with
    &lt;strong&gt;Facebook&lt;/strong&gt;
  &lt;/a&gt;
  &lt;a class=&quot;btn-auth btn-twitter large&quot; href=&quot;http://goodfil.ms/users/auth/twitter&quot;&gt;
    Sign up with
    &lt;strong&gt;Twitter&lt;/strong&gt;
  &lt;/a&gt;
&lt;/div&gt;

</content>
  </entry>
  
  <entry>
    <title>Netflix vs iTunes for Movies You&#8217;d Actually Watch</title>
    <link href="http://goodfil.ms/blog/posts/2012/05/16/netflix-vs-itunes-for-movies-youd-actually-watch/"/>
    <updated>2012-05-16T09:49:00+10:00</updated>
    <id>http://goodfil.ms/blog/posts/2012/05/16/netflix-vs-itunes-for-movies-youd-actually-watch</id>
    <content type="html">&lt;p&gt;We only recently started using Netflix to stream movies here at Goodfilms HQ, and we were surprised just how hard it is to find something decent to watch. When you do find one, the experience is excellent, but the number of movies that don&amp;#8217;t interest me got me thinking - how much of their library is stuff I&amp;#8217;d want to watch?&lt;/p&gt;

&lt;!&#8211;more&#8211;&gt;


&lt;p&gt;Goodfilms already integrates with iTunes to tell you what&amp;#8217;s available for streaming, and at what price, so we thought we&amp;#8217;d do a bit of a head to head. For those of you not familiar with our rating system, we have two axes: Quality and Rewatchability. A classic film (e.g. The Godfather) rates highly in both so will be up in the top-right, something fun but not particularly clever (e.g. Zoolander) will be up around the top, and something well-made but harrowing (e.g. Black Swan) will tend to be along the right hand side.&lt;/p&gt;

&lt;p&gt;Click through to the graph at &lt;a href=&quot;http://goodfil.ms/graph&quot;&gt;goodfil.ms/graph&lt;/a&gt; to explore.&lt;/p&gt;

&lt;p&gt;Here you see the average ratings of the most popular films on Goodfilms (~1150 films at the time of writing), and you can filter down by whether they&amp;#8217;re available on iTunes or Netflix. Click on the posters for links to start streaming them. Note: we&amp;#8217;re only looking at the Netflix library in the US for now, but the data for iTunes should be for your region&amp;#8217;s store.&lt;/p&gt;

&lt;h2&gt;The numbers&lt;/h2&gt;

&lt;p&gt;At the time of writing, Netflix has around 9300 films currently available for streaming in the US, but less than 150 of them appear on the graph. What that means is, for films that are popular on Goodfilms, there&amp;#8217;s only a &lt;strong&gt;12% chance they&amp;#8217;re available on Netflix Instant&lt;/strong&gt;. Or, to put in another way, people are watching &lt;strong&gt;only 1.5% of the films on Netflix Instant&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Contrast that to iTunes, which in the US has 730 films in the popular set - almost two-thirds! That gives it &lt;strong&gt;5 times more films that most people are watching&lt;/strong&gt;. According to our data, it has a similarly-sized total film count to Netflix, weighing in at over 11000. Flipping the calculation as we did previously, our users are watching just over 6% of iTunes&amp;#8217; offering. Still lower than we were expecting, but significantly higher than Netflix.&lt;/p&gt;

&lt;h2&gt;Caveats&lt;/h2&gt;

&lt;p&gt;We want to be clear that we&amp;#8217;re not calling Netflix or iTunes out for the quality of their offerings. They have entirely different business models and constraints to consider. We just wanted to present some data to show the massive gulf in what&amp;#8217;s available between the biggest transactional and subscription video-on-demand offerings right now.&lt;/p&gt;

&lt;p&gt;As the Goodfilms dataset grows, there&amp;#8217;ll be more activity in the &amp;#8216;long tail&amp;#8217; of content, so these percentages will increase. And we&amp;#8217;ll be keeping on top of new films being released by each service, as well as integrating more services like Hulu Plus and Amazon in the future. You can always get an up-to-date view on the situation at &lt;a href=&quot;http://goodfil.ms/explore?utm_source=blog&amp;amp;utm_medium=link&amp;amp;utm_campaign=netflix_v_itunes&quot;&gt;goodfil.ms/explore&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;Choose something to watch&lt;/h2&gt;

&lt;p&gt;The point of all this is to find something decent to watch, of course, so we built a couple of things to help. To get a full list of the films on Netflix, ranked by popularity, go to &lt;a href=&quot;http://goodfil.ms/films/on_netflix?utm_source=blog&amp;amp;utm_medium=link&amp;amp;utm_campaign=netflix_v_itunes&quot;&gt;goodfil.ms/films/on_netflix&lt;/a&gt;. For the same on iTunes, hit &lt;a href=&quot;http://goodfil.ms/films/on_itunes?utm_source=blog&amp;amp;utm_medium=link&amp;amp;utm_campaign=netflix_v_itunes&quot;&gt;goodfil.ms/films/on_itunes&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you&amp;#8217;re a Goodfilms user already, you can filter what&amp;#8217;s in your queue by Netflix (&lt;a href=&quot;http://goodfil.ms/queue/netflix?utm_source=blog&amp;amp;utm_medium=link&amp;amp;utm_campaign=netflix_v_itunes&quot;&gt;goodfil.ms/queue/netflix&lt;/a&gt;) or iTunes (&lt;a href=&quot;http://goodfil.ms/queue/netflix?utm_source=blog&amp;amp;utm_medium=link&amp;amp;utm_campaign=netflix_v_itunes&quot;&gt;goodfil.ms/queue/itunes&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;We hope this helps you avoid the duds and find films you&amp;#8217;ll enjoy.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>Project InGen</title>
    <link href="http://goodfil.ms/blog/posts/2012/04/20/project-ingen/"/>
    <updated>2012-04-20T16:12:00+10:00</updated>
    <id>http://goodfil.ms/blog/posts/2012/04/20/project-ingen</id>
    <content type="html">&lt;p&gt;Here at Goodfilms, we&amp;#8217;re all about doing interesting things with film ratings data. We&amp;#8217;ve developed a &lt;a href=&quot;http://goodfil.ms/blog/blog/posts/2011/10/07/a-better-way-to-rate-films/&quot;&gt;new way&lt;/a&gt; of rating movies that lets you express your opinions in a much more meaningful way, and we&amp;#8217;d like to introduce how we&amp;#8217;re using it.&lt;/p&gt;

&lt;p&gt;We&amp;#8217;re calling this project InGen after the corporation from &lt;a href=&quot;http://goodfil.ms/film/58247?utm_source=blog&amp;amp;utm_medium=link&amp;amp;utm_campaign=project_ingen&quot;&gt;Jurassic Park&lt;/a&gt; (one of our favourite films here at Goodfilm HQ). We admire the innovation and ambition of InGen from the film, and think that as long as we don&amp;#8217;t breed velociraptors, we&amp;#8217;ll be ok.&lt;/p&gt;

&lt;h1&gt;Ranking your friends&lt;/h1&gt;

&lt;p&gt;The first project to come out of InGen is about &lt;strong&gt;putting you and your friends&amp;#8217; tastes head-to-head&lt;/strong&gt;, to see how similar you are. And, by extension, figuring out who is the most and least similar to you, so you know who to go to the cinemas with, and who to make fun of.&lt;/p&gt;

&lt;!&#8211;more&#8211;&gt;


&lt;h1&gt;Comparing films&lt;/h1&gt;

&lt;p&gt;Because all of the ratings on Goodfilms are based on two separate criteria (quality and rewatchability), we can plot them on a graph (see &lt;a href=&quot;http://goodfil.ms/graph&quot;&gt;goodfil.ms/graph&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;We realised that placing the films on a graph makes it really clear where two films sit &lt;strong&gt;in relation to each other&lt;/strong&gt;, so we decided to classify every pair of films in one of 9 ways, which we call a Vector Victor (again, after one of &lt;a href=&quot;http://www.youtube.com/watch?v=fVq4_HhBK8Y&quot;&gt;our favourite films&lt;/a&gt; ). For example, Goodfilms users think the film Anchorman is more rewatchable but of lower quality than something like Black Swan. These are the 9 possible Vector Victors (hover for an example):&lt;/p&gt;

&lt;table class=&#8217;vectorvictor&#8217;&gt;
    &lt;tr class=&#8217;top&#8217;&gt;
        &lt;td class=&#8217;left&#8217; title=&#8217;E.g. Anchorman vs Black Swan&#8217;&gt;&lt;div class=&#8217;arrow&#8217;&gt;&lt;/div&gt;More rewatchable but lesser quality.&lt;/td&gt;
        &lt;td class=&#8217;middle&#8217; title=&quot;E.g. The King&#8217;s Speech vs The Wrestler&quot;&gt;&lt;div class=&#8217;arrow&#8217;&gt;&lt;/div&gt;Same quality but more rewatchable.&lt;/td&gt;
        &lt;td class=&#8217;right&#8217; title=&#8217;E.g. Every film ever made vs Indiana Jones and the Kingdom of the Crystal Skull&#8217;&gt;&lt;div class=&#8217;arrow&#8217;&gt;&lt;/div&gt;Better in both respects.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr class=&#8217;middle&#8217;&gt;
        &lt;td class=&#8217;left&#8217; title=&#8217;E.g. Spaceballs vs Blazing Saddles&#8217;&gt;&lt;div class=&#8217;arrow&#8217;&gt;&lt;/div&gt;Same rewatchability, lower quality.&lt;/td&gt;
        &lt;td class=&#8217;middle&#8217; title=&#8217;Most Nicholas Cage movies&#8217;&gt;&lt;div class=&#8217;arrow&#8217;&gt;&lt;/div&gt;Same in both dimensions.&lt;/td&gt;
        &lt;td class=&#8217;right&#8217; title=&#8217;E.g. Last King of Scotland vs Blood Diamond&#8217;&gt;&lt;div class=&#8217;arrow&#8217;&gt;&lt;/div&gt;Higher quality but same rewatchability.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr class=&#8217;bottom&#8217;&gt;
        &lt;td class=&#8217;left&#8217; title=&#8217;E.g. Star Wars Episode 1 vs Star Wars Episode 4&#8217;&gt;&lt;div class=&#8217;arrow&#8217;&gt;&lt;/div&gt;Worse in both dimensions.&lt;/td&gt;
        &lt;td class=&#8217;middle&#8217; title=&#8217;The Wrestler vs The King&#8217;s Speech&#8217;&gt;&lt;div class=&#8217;arrow&#8217;&gt;&lt;/div&gt;Same quality but less rewatchable.&lt;/td&gt;
        &lt;td class=&#8217;right&#8217; title=&#8217;Rambo: First Blood vs Rambo III&#8217;&gt;&lt;div class=&#8217;arrow&#8217;&gt;&lt;/div&gt;Higher quality but less rewatchable.&lt;/td&gt;
    &lt;/tr&gt;
&lt;/table&gt;


&lt;p&gt;This gives us quite a meaningful comparison of two films, but when we apply it to an individual&amp;#8217;s ratings, it gets even more interesting.&lt;/p&gt;

&lt;h1&gt;Judging your friends&lt;/h1&gt;

&lt;p&gt;We realised that we have a nice way of comparing you and your friends:&lt;/p&gt;

&lt;blockquote&gt;&lt;p&gt;For a pair of films, do you agree on their Vector Victor?&lt;/p&gt;&lt;/blockquote&gt;

&lt;p&gt;If you think that Black Swan &lt;strong&gt;is not&lt;/strong&gt; higher quality than Anchorman, you and I don&amp;#8217;t agree. And if you would enjoy rewatching a film about a ballet dancer&amp;#8217;s descent into psychosis more than a light-hearted Will Ferrell movie, we&amp;#8217;re not going to have a fun movie night.&lt;/p&gt;

&lt;p&gt;What&amp;#8217;s neat about this approach is that it &lt;strong&gt;the exact ratings don&amp;#8217;t have to match&lt;/strong&gt; for us to agree. Which works quite nicely because it doesn&amp;#8217;t matter how harshly you or your friend rate films, as long as you agree on their relativity.&lt;/p&gt;

&lt;p&gt;Also, since both quality and rewatchability are being considered, you are making quite a precise statement about how two films compare. The nine possible Vector Victors all represent something fairly specific, so the fact that you and a friend agree is quite meaningful.&lt;/p&gt;

&lt;h1&gt;The Result&lt;/h1&gt;

&lt;p&gt;&lt;img src=&quot;http://files.goodfil.ms/goodfilms_friends.png&quot; alt=&quot;Goodfilms friends ranking&quot; /&gt;&lt;/p&gt;

&lt;p&gt;This lists your friends in order of how close you align. The numbers on the right represent how many films you&amp;#8217;ve both rated, and, more interestingly, &lt;strong&gt;how many films are in both your queues&lt;/strong&gt;. Click on this and you have a great way to pick a film to watch with someone:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://files.goodfil.ms/in_both_queues.png&quot; alt=&quot;In both me and my friend&#8217;s queues&quot; /&gt;&lt;/p&gt;

&lt;p&gt;We hope you all find out something interesting about your friends with this. If you&amp;#8217;re already a user, hop on over to &lt;a href=&quot;http://goodfil.ms/people/friends?utm_source=blog&amp;amp;utm_medium=link&amp;amp;utm_campaign=project_ingen&quot;&gt;goodfil.ms/people/friends&lt;/a&gt; and check it out. If not, head straight to &lt;a href=&quot;http://goodfil.ms?utm_source=blog&amp;amp;utm_medium=link&amp;amp;utm_campaign=project_ingen&quot;&gt;goodfil.ms&lt;/a&gt; and sign up!&lt;/p&gt;

&lt;h1&gt;Footnotes&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;This is a work in progress. Things will change as we refine it.&lt;/li&gt;
&lt;li&gt;This is not the only signal we are using to compare you and your friends, but it&amp;#8217;s the most significant and the most original.&lt;/li&gt;
&lt;li&gt;Two ratings (in either dimension) don&amp;#8217;t need to be identical for us to consider them &amp;#8216;the same&amp;#8217;. We use a continuous rating system, so we account for a little noise in people&amp;#8217;s ratings by using a certain tolerance in our calculations. At the moment, that tolerance is roughly a half-star or half-smiley wide.&lt;/li&gt;
&lt;li&gt;We&amp;#8217;d like to thank &lt;a href=&quot;http://flickchart.com&quot;&gt;FlickChart&lt;/a&gt; for showing how informative it can be to directly compare two films.&lt;/li&gt;
&lt;li&gt;Evan Miller&amp;#8217;s &lt;a href=&quot;http://evanmiller.org/how-not-to-sort-by-average-rating.html&quot;&gt;blog post&lt;/a&gt; and the resulting &lt;a href=&quot;http://news.ycombinator.com/item?id=3792627&quot;&gt;Hacker News discussion&lt;/a&gt; also deserve thanks for helping to explain and recommend the Wilson interval for ranking algorithms like ours.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;We love feedback.&lt;/strong&gt; Comment here, post something on our &lt;a href=&quot;http://goodfilms.uservoice.com/forums/129751-general&quot;&gt;feedback forum&lt;/a&gt;, or hit us on &lt;a href=&quot;http://twitter.com/goodfilmshq&quot;&gt;Twitter&lt;/a&gt; or &lt;a href=&quot;http://www.facebook.com/we.love.goodfilms&quot;&gt;Facebook&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

</content>
  </entry>
  
  <entry>
    <title>Press Release - Goodfilms Raises Seed Funding</title>
    <link href="http://goodfil.ms/blog/posts/2012/02/27/press-release-goodfilms-raises-seed-funding/"/>
    <updated>2012-02-27T15:32:00+11:00</updated>
    <id>http://goodfil.ms/blog/posts/2012/02/27/press-release-goodfilms-raises-seed-funding</id>
    <content type="html">&lt;p&gt;&lt;a href=&quot;http://files.goodfil.ms/GoodfilmsPressRelease%2027thFeb2012.pdf&quot;&gt;Social movie reviews startup Goodfilms raises $150K.&lt;/a&gt; [PDF]&lt;/p&gt;

&lt;p&gt;For any enquiries, please contact us on &lt;a href=&quot;mailto:marketing@goodfil.ms&quot;&gt;marketing@goodfil.ms&lt;/a&gt;&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>AngelCube: 4 Weeks In</title>
    <link href="http://goodfil.ms/blog/posts/2011/10/18/angelcube-4-weeks-in/"/>
    <updated>2011-10-18T15:36:00+11:00</updated>
    <id>http://goodfil.ms/blog/posts/2011/10/18/angelcube-4-weeks-in</id>
    <content type="html">&lt;p&gt;A little over six weeks ago, I found out that &lt;a href=&quot;http://goodfil.ms&quot;&gt;Goodfilms&lt;/a&gt; had been selected as one of the first round of startups to be selected for the &lt;a href=&quot;http://angelcube.com&quot;&gt;AngelCube&lt;/a&gt; seed accelerator. Now, one-third of the way through the program I thought I&amp;#8217;d take some time and reflect on a major lesson I&amp;#8217;ve learned so far, and where Goodfilms is now.&lt;/p&gt;

&lt;p&gt;We&amp;#8217;ve had a variety of mentors sessions covering a range of topics, but I thought I&amp;#8217;d focus on one of the common themes - that you should be measuring everything.&lt;/p&gt;

&lt;!&#8211;more&#8211;&gt;


&lt;h1&gt;Measure Everything&lt;/h1&gt;

&lt;p&gt;When I started work at &lt;a href=&quot;http://envato.com/&quot;&gt;Envato&lt;/a&gt; last year, I couldn&amp;#8217;t believe how easily and freely they deployed code to production. At the time we were serving over a quarter of a million users, tens of millions of pageviews a week, and yet we would usually push code several times a day. It was a little daunting at first, but everybody in the team understood the implications - if you wrote code that broke, ran slowly or disrupted someone&amp;#8217;s workflow, it was on you. Nobody held your hand, everybody stepped up.&lt;/p&gt;

&lt;p&gt;It took some getting used to, but suddenly everything made sense - the only real way to know if your code performed well enough, if the feature was useful, and that nothing fell over was to &lt;em&gt;get the code into production&lt;/em&gt; &lt;a href=&#8217;#footnote1&#8217; class=&#8217;footnote&#8217;&gt;[1]&lt;/a&gt;. But that relies on you and your team paying constant attention to performance, feedback and uptime. In other words, if you don&amp;#8217;t measure your application well enough, you&amp;#8217;ll never know when you&amp;#8217;re doing badly.&lt;/p&gt;

&lt;p&gt;From the mentoring sessions we&amp;#8217;ve had as part of the AngelCube program, a similar principle has emerged - if you don&amp;#8217;t know precisely how you&amp;#8217;re doing, you won&amp;#8217;t know when you&amp;#8217;ve improved or regressed.&lt;/p&gt;

&lt;p&gt;From online marketing sessions to working on our lean canvas to discussing how to convey traction in a slide deck, the principles of measuring, testing and verifying have been paramount. I&amp;#8217;m a developer at heart, so my natural tendency is to build whatever&amp;#8217;s cool, but the wider implications must be understood, and that means being able to measure the business or customer need. It&amp;#8217;s the biggest departure from my comfort zone since moving from developing for a handful of users to hundreds of thousands.&lt;/p&gt;

&lt;p class=&#8217;footnote&#8217; id=&quot;footnote1&quot;&gt;[1] This topic is far more complex than this, so please excuse this oversimplification.&lt;/p&gt;


&lt;h1&gt;Measure What?&lt;/h1&gt;

&lt;p&gt;If I tried to distill the lesson behind the advice we&amp;#8217;ve received so far, it is this - understand every way people get on to your site, and what they do once they&amp;#8217;re there. &amp;#8216;Conversion&amp;#8217; is the general term, I suppose, but I prefer to work in specifics: Measure the proportion of people interested enough to click an ad, the proportion of those who sign up, the proportion of people reading this blog (i.e. you!) and how many of those &lt;a href=&quot;http://goodfil.ms&quot;&gt;sign up for an account&lt;/a&gt;. Find out how many people don&amp;#8217;t bother using their invitations. Find out how likely someone is to never rate a film, to rate 10 films, to rate 100 films. How many people invite friends? How many people leave feedback?&lt;/p&gt;

&lt;p&gt;By measuring this we can determine whether our landing page is convincing, whether our blog content is interesting and whether the you&amp;#8217;ve-been-invited email is confusing. We can deduce if people find Goodfilms useful without having any friends on the site yet, or if the first thing they want to do is get their friends on board. The better we can understand this, the &lt;em&gt;better product we can build&lt;/em&gt;.&lt;/p&gt;

&lt;h1&gt;Looking forward&lt;/h1&gt;

&lt;p&gt;Measurement doesn&amp;#8217;t happen for free, and like monitoring your code in production, adding each piece to the puzzle requires time and care, and that&amp;#8217;s where Goodfilms is now. We&amp;#8217;re working through what gives us a manageable amount of information, in the most straightforward way. And it needs to be as automatic as possible - any time spent collating measurements is time that can&amp;#8217;t be spent on other things.&lt;/p&gt;

&lt;p&gt;But it represents the clear value of participating in a seed accelerator like AngelCube - access to a range of mentors with differing experience and advise, but some common themes. Even after 4 weeks we&amp;#8217;re seeing meaningful improvements to the quality of the Goodfilms website and strength of the business behind it, and we&amp;#8217;re all looking forward to the next 2 months.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>A Better Way to Rate Films</title>
    <link href="http://goodfil.ms/blog/posts/2011/10/07/a-better-way-to-rate-films/"/>
    <updated>2011-10-07T10:11:00+11:00</updated>
    <id>http://goodfil.ms/blog/posts/2011/10/07/a-better-way-to-rate-films</id>
    <content type="html">&lt;p&gt;Whereby I try to convince you that all other ratings systems are the lose, and &lt;a href=&quot;http://goodfil.ms&quot;&gt;Goodfilms&lt;/a&gt;&amp;#8217; is the win. Let&amp;#8217;s begin.&lt;/p&gt;

&lt;h1&gt;First, an observation&lt;/h1&gt;

&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;Your favourite films probably didn&amp;#8217;t win Best Picture.&lt;/strong&gt;&lt;/p&gt;&lt;/blockquote&gt;

&lt;p&gt;In fact, I wonder how many &lt;a href=&quot;http://en.wikipedia.org/wiki/Academy_Award_for_Best_Picture&quot;&gt;Best Pictures&lt;/a&gt; you would happily watch over and over again? Critical acclaim doesn&amp;#8217;t guarantee that you&amp;#8217;ll love a film, and a film doesn&amp;#8217;t have to be objectively brilliant for you to list it among your favourites.&lt;/p&gt;

&lt;p&gt;Moreover, some films that were shocking or groundbreaking for their time might not seem that remarkabe now, and some that were panned when they were released may achieve popularity and recognition much later.&lt;/p&gt;

&lt;p&gt;The point is this - your enjoyment of a film is different to its critical reception. There&amp;#8217;s something else at work, and this post is about determining what that is.&lt;/p&gt;

&lt;!&#8211;more&#8211;&gt;


&lt;h1&gt;5-star ratings systems don&amp;#8217;t work&lt;/h1&gt;

&lt;p&gt;A few years ago, YouTube &lt;a href=&quot;http://youtube-global.blogspot.com/2009/09/five-stars-dominate-ratings.html&quot;&gt;blogged&lt;/a&gt; about the ineffectiveness of their 5-star ratings system, referring to this graph:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://i.imgur.com/AQzoB.jpg&quot; alt=&quot;Youtube ratings graph&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The number of 5-star ratings greatly outnumbered all others, with 1-star a distant second. They decided to switch to a thumbs-up/thumbs-down system, which by all reports is working well. Amazon also uses a 5-star system and similarly skewed ratings are often seen. In fact, they now define 4+ stars as &amp;#8216;favorable&amp;#8217; and 1-3 stars as &amp;#8216;critical&amp;#8217;. The reviews of Transformers 2 are a particularly good example:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://i.imgur.com/hapdc.png&quot; alt=&quot;Amazon ratings bringing the objectivity&quot; /&gt;&lt;/p&gt;

&lt;p&gt;For films, a yes/no ratings system, like that employed by &lt;a href=&quot;http://rottentomatoes.com&quot;&gt;Rotten Tomatoes&lt;/a&gt;, may be the best way to derive a single, aggregate score. But it relies on a huge number of anonymous ratings, and collects the least possible amount of information from each person.&lt;/p&gt;

&lt;p&gt;I think this is a step in the wrong direction.&lt;/p&gt;

&lt;p&gt;When considering your immediate friends, you &lt;em&gt;want&lt;/em&gt; to know the detail and variety of their opinions. There&amp;#8217;s a difference between your friend liking a movie and loving it, between not really caring for a film and actively warning you off it. Goodfilms &lt;a href=&quot;http://goodfil.ms/blog/blog/2011/09/30/why-goodfil-dot-ms&quot;&gt;focuses on these opinions&lt;/a&gt;, and so an aggregate score isn&amp;#8217;t good enough. Mind you, that doesn&amp;#8217;t mean we&amp;#8217;re happy with the usual 5-star rating system. Oh my word no.&lt;/p&gt;

&lt;h1&gt;Continuous ratings&lt;/h1&gt;

&lt;p&gt;There&amp;#8217;s another problem with the traditional star system - quantization. Considering the following graph of a staggering 10 million movie ratings from the &lt;a href=&quot;http://movielens.umn.edu/&quot;&gt;Movie Lens&lt;/a&gt; project:&lt;/p&gt;

&lt;div id=&quot;individual_ratings&quot; style=&quot;max-width: 500px; height: 300px; margin: 1em auto&quot;&gt;&lt;/div&gt;


&lt;p&gt;There&amp;#8217;s a clear bias toward integer ratings here - only 20% of all ratings are half-stars. And there&amp;#8217;s no clear reason why that should be the case - clearly the majority of ratings are 3 and 4 stars, why are there so few 3.5 star ratings?&lt;/p&gt;

&lt;p&gt;There&amp;#8217;s a great &lt;a href=&quot;http://blog.steepster.com/post/226679106/better-rating-system&quot;&gt;blog post&lt;/a&gt; by the guys at &lt;a href=&quot;http://steepster.com/&quot;&gt;Steepster&lt;/a&gt;, talking about how their ratings system developed over time, settling on the following design:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://i.imgur.com/j3KEH.png&quot; alt=&quot;Steepster rating dialog&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://goodfil.ms&quot;&gt;Goodfilms&lt;/a&gt; adopts a similar approach, and it completely avoids the integer-bias observed in the MovieLens dataset:&lt;/p&gt;

&lt;div id=&quot;ratings_comparison&quot; style=&quot;max-width: 650px; height: 400px; margin: 1em auto&quot;&gt;&lt;/div&gt;


&lt;p&gt;A continuous scale might be an improvement, but we haven&amp;#8217;t addressed the key problem - &lt;em&gt;there&amp;#8217;s more to rating films than objective quality&lt;/em&gt;. But what is it?&lt;/p&gt;

&lt;h1&gt;Going beyond &amp;#8216;quality&amp;#8217;&lt;/h1&gt;

&lt;p&gt;We&amp;#8217;ve come up with a concept that we think captures this neatly: &lt;em&gt;rewatchability&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Yes, I&amp;#8217;ll admit that that&amp;#8217;s not a real word. But it&amp;#8217;s a good short-hand for the question we ask when you rate a film on Goodfilms:&lt;/p&gt;

&lt;blockquote&gt;&lt;p&gt;If you were to watch it again, how much would you enjoy it?&lt;/p&gt;&lt;/blockquote&gt;

&lt;p&gt;Now, when grappling with what score to give a film like Transformers 2, you don&amp;#8217;t have to decide between &amp;#8216;Transformers Rock!&amp;#8217; and &amp;#8216;I wish I could have transformed into a gun to shoot myself&amp;#8217;, like the Amazon example above. Now you can freely admit that it&amp;#8217;s a long, loud, confusing mess of a film &lt;em&gt;and you like it anyway&lt;/em&gt;:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://i.imgur.com/o478G.png&quot; alt=&quot;Transformers 2 sucks and I don&#8217;t care&quot; /&gt;&lt;/p&gt;

&lt;p&gt;On the other end of the scale, I think &lt;a href=&quot;http://en.wikipedia.org/wiki/Black_Swan_(film)&quot;&gt;Black Swan&lt;/a&gt; is a terrific film, but I&amp;#8217;d sooner pull my nails out than watch it again.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://i.imgur.com/xtK9c.png&quot; alt=&quot;Black Swan is really great but please don&#8217;t make me watch it again&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Rewatchability turns out to be an excellent indicator of what films you&amp;#8217;d recommend. What better recommendation than a movie you&amp;#8217;d happily watch again and again? And would you really suggest a film to a friend if you yourself wouldn&amp;#8217;t enjoy a repeat viewing?&lt;/p&gt;

&lt;p&gt;We think this is a much better way to rate films, and from the first chunk of data we&amp;#8217;re starting to see some fascinating results. Here&amp;#8217;s a sneak peak at what we&amp;#8217;ve got so far, displaying &amp;#8216;quality&amp;#8217; along the x-axis and &amp;#8216;rewatchability&amp;#8217; along the y:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://i.imgur.com/ONXAa.png&quot; alt=&quot;Scatter plot&quot; /&gt;&lt;/p&gt;

&lt;p&gt;As you can see, both axes have their outliers, and overall there&amp;#8217;s a correlation between quality and rewatchability, which you&amp;#8217;d expect. Some films are showing themselves to be deeply contradictory, with others showing a surprising consensus. And there&amp;#8217;s a tendency toward rating higher-quality films in general, but rewatchability is more evenly distributed.&lt;/p&gt;

&lt;p&gt;There&amp;#8217;ll be much more detailed posts in the future, but for now - that&amp;#8217;s our ratings system, and how we came to it. We think it&amp;#8217;s the total win, and we hope you&amp;#8217;ll agree. So &lt;a href=&quot;http://goodfil.ms&quot;&gt;sign up&lt;/a&gt; if you haven&amp;#8217;t already, and get rating!&lt;/p&gt;

&lt;p&gt;-glen.&lt;/p&gt;

&lt;script src=&quot;http://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js&quot; type=&quot;text/javascript&quot;&gt;&lt;/script&gt;


&lt;script src=&quot;http://goodfil.ms/blog/javascripts/highcharts.js&quot; type=&quot;text/javascript&quot;&gt;&lt;/script&gt;


&lt;script type=&quot;text/javascript&quot;&gt;
  $(function() {
    Highcharts.setOptions({
      chart: {
        spacingTop: 20,
        style: { fontFamily: &#8216;Lato&#8217;, fontWeight: 400 }
      },
      title: {
        margin: 40,
        style: { fontWeight: 700, fontSize: 22 }
      },
      yAxis: {
        title: {
          margin: 20,
          style: { fontSize: 16, fontWeight: &#8216;normal&#8217; }
        }
      },
      xAxis: {
        title: {
          margin: 20,
          style: { fontSize: 16, fontWeight: &#8216;normal&#8217; }
        }
      },
      plotOptions: {
        series: {
          borderColor: &#8216;#999&#8217;,
          groupPadding: 0,
          marker: {enabled: false}
        }
      }
    });

    new Highcharts.Chart({

      chart: {
        renderTo: &#8216;individual_ratings&#8217;,
        type: &#8216;area&#8217;
      },

      legend: { enabled: false },

      title: {
        text: &#8216;Invidual Ratings&#8217;
      },

      xAxis: {
        min: 0,
        title: {
          text: &#8216;Stars&#8217;
        }
      },

      yAxis: {
        title: {
          text: &#8216;Num Ratings (millions)&#8217;
        }
      },

      series: [
        {
          data: [
            [0, 0],
            [0.5, 0.094988],
            [1, 0.38418],
            [1.5, 0.118278],
            [2, 0.790306],
            [2.5, 0.370178],
            [3, 2.356676],
            [3.5, 0.879764],
            [4, 2.87585],
            [4.5, 0.585022],
            [5, 1.544812]
          ]
        }
      ]
    });

    new Highcharts.Chart({

      chart: {
        renderTo: &#8216;ratings_comparison&#8217;,
        type: &#8216;area&#8217;
      },

      title: {
        text: &#8216;Ratings distribution&#8217;
      },

      xAxis: {
        title: {
          text: &#8216;Stars&#8217;
        }
      },

      yAxis: {
        max: 30,
        title: {
          text: &#8216;% of All Ratings&#8217;
        }
      },

      series: [
        {
          name: &#8216;MovieLens&#8217;,
          data: [
            [0, 0],
            [0.5, 0.95],
            [1, 3.842],
            [1.5, 1.183],
            [2, 7.903],
            [2.5, 3.702],
            [3, 23.565],
            [3.5, 8.797],
            [4, 28.757],
            [4.5, 5.85],
            [5, 15.447]
          ]
        },
        {
          name: &#8216;Goodfilms&#8217;,
          data: [
            [0.0, 0.859],
            [0.5, 1.327],
            [1.0, 1.327],
            [1.5, 2.108],
            [2.0, 3.005],
            [2.5, 6.167],
            [3.0, 10.07],
            [3.5, 20.141],
            [4.0, 21.819],
            [4.5, 20.258],
            [5.0, 12.92]
          ]
        }
      ]
    });

  });

&lt;/script&gt;

</content>
  </entry>
  
  <entry>
    <title>Why Goodfilms?</title>
    <link href="http://goodfil.ms/blog/posts/2011/09/30/why-goodfil-dot-ms/"/>
    <updated>2011-09-30T12:51:00+10:00</updated>
    <id>http://goodfil.ms/blog/posts/2011/09/30/why-goodfil-dot-ms</id>
    <content type="html">&lt;p&gt;You may be asking yourself, why does Goodfilms exist? Aren&amp;#8217;t there plenty of sites about movies already? Do we really need another one?&lt;/p&gt;

&lt;p&gt;Well yes, there are quite a few already. But they&amp;#8217;re all, at least a little bit, &lt;em&gt;balls&lt;/em&gt;.&lt;/p&gt;

&lt;h1&gt;The problem&lt;/h1&gt;

&lt;blockquote&gt;&lt;p&gt;How do I choose something &lt;em&gt;I&amp;#8217;ll&lt;/em&gt; enjoy?&lt;/p&gt;&lt;/blockquote&gt;

&lt;p&gt;We&amp;#8217;re already have ways of choosing what to watch. You might have a &lt;a href=&quot;http://www.abc.net.au/atthemovies/&quot;&gt;few&lt;/a&gt; &lt;a href=&quot;http://rogerebert.suntimes.com/&quot;&gt;critics&lt;/a&gt; or &lt;a href=&quot;http://www.avclub.com/&quot;&gt;blogs&lt;/a&gt; who you find you have an affinity with, and you watch whatever they recommend. You might trust an &lt;a href=&quot;http://rottentomatoes.com&quot;&gt;aggregate&lt;/a&gt; &lt;a href=&quot;http://imdb.com&quot;&gt;score&lt;/a&gt;, believing that what works for most people will work for you. Or, you might &lt;a href=&quot;http://trailers.apple.com&quot;&gt;watch&lt;/a&gt; &lt;a href=&quot;http://www.youtube.com/trailers&quot;&gt;trailers&lt;/a&gt; and base your decision on the number of explosions or how dramatic the &lt;a href=&quot;http://en.wikipedia.org/wiki/Don_LaFontaine&quot;&gt;voiceover guy&lt;/a&gt; is.&lt;/p&gt;

&lt;p&gt;And that might be working well for you already. Or you could be regularly watching duds. Either way, I think &lt;em&gt;Goodfilms can do better&lt;/em&gt;.&lt;/p&gt;

&lt;!&#8211;more&#8211;&gt;


&lt;h1&gt;A laughable claim, Mr Bond.&lt;/h1&gt;

&lt;p&gt;I wonder if you&amp;#8217;ve ever heard one of your friends say something like:&lt;/p&gt;

&lt;blockquote&gt;&lt;p&gt;Wait, you&amp;#8217;ve never seen The Usual Suspects? You have to. As in, take the afternoon off and watch it.&lt;/p&gt;

&lt;p&gt;Don&amp;#8217;t ever watch the Matrix sequels. In fact, they &lt;a href=&quot;http://xkcd.com/566/&quot;&gt;don&amp;#8217;t even exist&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Transformers 3 was, without a doubt, the worst example of filmmaking I&amp;#8217;ve ever witnessed. But, it&amp;#8217;s got giant robots punching each other, so&amp;#8230; you&amp;#8217;d probably like it.&lt;/p&gt;&lt;/blockquote&gt;

&lt;p&gt;Yes? No? Well, maybe you have different friends to me. But that&amp;#8217;s ok. In fact, that leads neatly into my point:&lt;/p&gt;

&lt;p&gt;Want to know what &lt;em&gt;you&amp;#8217;ll&lt;/em&gt; like? &lt;strong&gt;Ask your friends.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;And that&amp;#8217;s why Goodfilms exists:&lt;/p&gt;

&lt;p&gt;Goodfilms puts your friends&amp;#8217; opinions ahead of those of strangers. It lets you record which films were compelling and which were forgettable, lets you separate the inexplicably enjoyable from the intellectually insulting. And the result?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A better way to discover and remember films you&amp;#8217;ll enjoy.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;So &lt;a href=&quot;http://goodfil.ms&quot;&gt;sign up&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;-glen.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>My diary, by Arnold J. Rimmer</title>
    <link href="http://goodfil.ms/blog/posts/2011/09/29/my-diary/"/>
    <updated>2011-09-29T18:14:00+10:00</updated>
    <id>http://goodfil.ms/blog/posts/2011/09/29/my-diary</id>
    <content type="html">&lt;p&gt;There&amp;#8217;s something about writing the first post on a new blog that brings to mind &lt;a href=&quot;http://www.youtube.com/watch?feature=player_detailpage&amp;amp;v=e3cHCaCtz08#t=230s&quot;&gt;this bit from Red Dwarf&lt;/a&gt; ( I suppose I could have looked for a film reference rather than a TV one, but *shrug*). So, in order to not sound like a tool - I&amp;#8217;ll simply say that this is where I&amp;#8217;ll be explaining how Goodfilms is put together, and why.&lt;/p&gt;

&lt;p&gt;Anyway, why don&amp;#8217;t we skip over the &amp;#8216;first post&amp;#8217; nerves and get right to it, hey?&lt;/p&gt;

&lt;p&gt;Excellent.&lt;/p&gt;

&lt;p&gt;-glen.&lt;/p&gt;
</content>
  </entry>
  
</feed>
