<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/atom10full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><feed xmlns="http://www.w3.org/2005/Atom" xmlns:openSearch="http://a9.com/-/spec/opensearch/1.1/" xmlns:georss="http://www.georss.org/georss" xmlns:gd="http://schemas.google.com/g/2005" xmlns:thr="http://purl.org/syndication/thread/1.0" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" gd:etag="W/&quot;DUcDRX89eCp7ImA9WhRVE0U.&quot;"><id>tag:blogger.com,1999:blog-5274051535398117366</id><updated>2012-01-12T08:51:14.160-08:00</updated><category term="Python" /><category term="node.js" /><category term="streamie" /><category term="UniversalComet" /><category term="packaging" /><category term="Performance" /><category term="Trackback" /><category term="Attribute Meta Classes" /><category term="Release" /><category term="Cookbook" /><category term="Augment" /><category term="Ext" /><category term="VisualWorks" /><category term="JSCloud" /><category term="Method Modifiers" /><category term="jsonpickle" /><category term="PureMVC" /><category term="Roles" /><category term="Types" /><category term="Testing" /><category term="Joose" /><category term="jsconf" /><category term="Rhino" /><category term="Presentation" /><category term="Documentation" /><category term="Traits" /><category term="HttpRequest" /><category term="JSON" /><category term="Google Gears" /><category term="joosify" /><category term="Offtopic" /><category term="WebWorkers" /><category term="CPAN" /><category term="Logo" /><category term="HTML5" /><category term="database" /><category term="Builder" /><category term="because-you-can" /><category term="speed" /><category term="promotejs" /><category term="jscomp" /><category term="jQuery" /><category term="google gadget api" /><category term="personal" /><category term="JScript .NET" /><category term="WebSocket" /><category term="Profiling" /><category term="OR-Mapper" /><category term="#bchh08" /><category term="NoFlash" /><category term="permanize" /><category term="Joosification" /><category term="widgets" /><category term="Google App Engine" /><category term="PMWA" /><category term="CouchDB" /><category term="Prototypes" /><category term="Omniture" /><category term="Type Coercions" /><category term="recipe" /><category term="Example" /><category term="blok" /><category term="HTML" /><category term="Joose.Storage" /><category term="Trivia" /><category term="bespin" /><category term="JavaScript" /><category term="Exceptions" /><category term="JooseX" /><category term="Old-Style-Interface" /><category term="Meta Classes" /><category term="Block" /><category term="svn" /><category term="Moose" /><title>NonBlocking.io - Malte Ubl's Asynchronous Identity Disorder</title><subtitle type="html" /><link rel="http://schemas.google.com/g/2005#feed" type="application/atom+xml" href="http://www.nonblocking.io/feeds/posts/default" /><link rel="alternate" type="text/html" href="http://www.nonblocking.io/" /><link rel="next" type="application/atom+xml" href="http://www.blogger.com/feeds/5274051535398117366/posts/default?start-index=26&amp;max-results=25&amp;redirect=false&amp;v=2" /><author><name>Malte Ubl</name><uri>https://profiles.google.com/116910304844117268718</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-tQJAx2-8rqE/AAAAAAAAAAI/AAAAAAAAEhc/T74iWhwXXf0/s512-c/photo.jpg" /></author><generator version="7.00" uri="http://www.blogger.com">Blogger</generator><openSearch:totalResults>151</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/atom+xml" href="http://feeds.feedburner.com/joose" /><feedburner:info uri="joose" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><entry gd:etag="W/&quot;DUcDRXw8cSp7ImA9WhRVE0U.&quot;"><id>tag:blogger.com,1999:blog-5274051535398117366.post-4858162034415099988</id><published>2012-01-12T08:51:00.000-08:00</published><updated>2012-01-12T08:51:14.279-08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-01-12T08:51:14.279-08:00</app:edited><title>On a personal note</title><content type="html">Me and my wife &lt;a href="https://plus.google.com/u/1/101221677383028720528/posts"&gt;Svenja&lt;/a&gt; moved to San Francisco last Tuesday. Yaiih :)&lt;br /&gt;
So far we've been having a ton of fun and really enjoyed the weather compared to cold rainy Germany.&lt;br /&gt;
Svenja has been &lt;a href="http://cramforceswife.tumblr.com/"&gt;blogging about her experience coming to San Francisco&lt;/a&gt;. She called the blog &lt;a href="http://cramforceswife.tumblr.com/"&gt;cramforceswife.tumblr.com&lt;/a&gt;&amp;nbsp;after my Twitter name &lt;a href="http://twitter.com/cramforce"&gt;cramforce&lt;/a&gt;. Must be true love (more likely she is being ironic :)&lt;br /&gt;
&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5274051535398117366-4858162034415099988?l=www.nonblocking.io' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/joose/~4/UCd201oUVds" height="1" width="1"/&gt;</content><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/5274051535398117366/posts/default/4858162034415099988?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/5274051535398117366/posts/default/4858162034415099988?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/joose/~3/UCd201oUVds/on-personal-note.html" title="On a personal note" /><author><name>Malte</name><uri>http://www.blogger.com/profile/02896910385129311939</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><feedburner:origLink>http://www.nonblocking.io/2012/01/on-personal-note.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DEMDSHYzfyp7ImA9WhRQGUo.&quot;"><id>tag:blogger.com,1999:blog-5274051535398117366.post-7061486791281035746</id><published>2011-12-15T10:08:00.001-08:00</published><updated>2011-12-15T11:14:39.887-08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-12-15T11:14:39.887-08:00</app:edited><title>Experimental support for Common JS and AMD/require.js modules in Closure Compiler</title><content type="html">I recently stumbled over a thread on the jQuery mailing list about how to modularize jQuery which keeps getting bigger and bigger with every version with not everybody using every feature. Some argued to change jQuery to support "dead code elimination" via &lt;a href="http://code.google.com/closure/compiler/"&gt;Google Closure Compiler's&lt;/a&gt; advanced optimizations, which would eliminate unused code from people's projects; others wanted to use &lt;a href="https://github.com/amdjs/amdjs-api/wiki/AMD"&gt;AMD&lt;/a&gt;/&lt;a href="http://requirejs.org/"&gt;require.js&lt;/a&gt; modules instead, which enables only loading required dependencies.
Having just done a little project on closure compiler at &lt;a href="https://plus.google.com/"&gt;work&lt;/a&gt;, I figured it might be possible to support both of those ideas equally. And so &lt;a href="http://code.google.com/p/closure-compiler/source/detail?r=1684"&gt;I got coding…&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;h2&gt;


How it works&lt;/h2&gt;
With my change closure compiler (CC) gets experimental 1st class support for both Common JS and AMD modules. This means that CC knows about these types of modules and performs special transformations and optimizations for them.

The high level goals are:
&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;Concatenate all modules into a single large file.
&lt;/li&gt;
&lt;li&gt;Automatically order modules so that dependencies are fulfilled.
&lt;/li&gt;
&lt;li&gt;Make it really easy for CC to apply its built in optimizations.
&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;h3&gt;


Step 1: Transform AMD to Common JS modules&lt;/h3&gt;
Add &lt;code&gt;--transform_amd_modules&lt;/code&gt; to the command line options of CC to transform AMD modules to Common JS modules. In this first steps basically

&lt;br /&gt;
&lt;pre class="code"&gt;define([&lt;span style="color: pink;"&gt;'foo'&lt;/span&gt;], function(&lt;span style="color: red;"&gt;foo&lt;/span&gt;) { &lt;span style="color: blue;"&gt;return&lt;/span&gt; {
  test: function() {}
}});&lt;/pre&gt;
gets transformed to:

&lt;br /&gt;
&lt;pre class="code"&gt;var &lt;span style="color: red;"&gt;foo&lt;/span&gt; = &lt;span style="color: pink;"&gt;require('foo')&lt;/span&gt;;
&lt;span style="color: blue;"&gt;module.exports&lt;/span&gt; = { test: function() {} }
&lt;/pre&gt;
From now on we don't have to worry about the peculiarities of asynchronous AMD anymore. This step by itself, might be useful to some people. E.g. if you want to use AMD code in Node.js directly.


&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;


Step 2: Process Common JS modules&lt;/h3&gt;
Add &lt;code&gt;--process_common_js_modules&lt;/code&gt; to the command line options of CC to enable specific processing of Common JS modules.

Most Common JS implementations (like e.g. Node.js) implement it by wrapping all code of a module in a closure like this:
&lt;br /&gt;
&lt;pre class="code"&gt;(function(require, exports, modules) { /* your module code */ })(…)&lt;/pre&gt;
The problem with that is that the module pattern is really hard to optimize for closure compiler because with function calls and scopes involved, everything becomes really dynamic and hard to statically understand.
This is why I implemented a transformation for Common JS modules which allows them to be safely concatenated into a single JS file without the use of closures. This works by renaming all global symbols in a module so that they never conflict with a different module.
The following Common JS module named "example/baz":

&lt;br /&gt;
&lt;pre&gt;var foo = &lt;span style="color: red;"&gt;require('foo');&lt;/span&gt;
var &lt;span style="color: pink;"&gt;bar&lt;/span&gt; = &lt;span style="color: blue;"&gt;require('bar');&lt;/span&gt;
&lt;span style="color: orange;"&gt;exports&lt;/span&gt; = { test: 1 };&lt;/pre&gt;
becomes:

&lt;br /&gt;
&lt;pre class="code"&gt;var foo$$example$baz = &lt;span style="color: red;"&gt;module$foo;&lt;/span&gt;
var &lt;span style="color: pink;"&gt;bar$$example$baz&lt;/span&gt; = &lt;span style="color: blue;"&gt;module$bar;&lt;/span&gt;
&lt;span style="color: orange;"&gt;module$example$baz&lt;/span&gt; = { test: 1 };&lt;/pre&gt;
Notice how &lt;code&gt;exports&lt;/code&gt; just becomes &lt;code&gt;module$example$baz&lt;/code&gt; while &lt;code&gt;require('foo')&lt;/code&gt; gets turned into &lt;code&gt;module$foo&lt;/code&gt;. As you see both exports (and by proxy module.exports) as well as require get converted into direct references to the specific module. All global variables and function names get suffixed with the module name, so that they can no longer conflict with any other module.

Note that while these sources seem really verbose, closure compiler will, of course, make all variable names really short later in the compilation process.


&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;


Step 3: Managing dependencies&lt;/h3&gt;
Add &lt;code&gt;--common_js_entry_module=foo/bar.js&lt;/code&gt; to your command line options to specify your "base" or "main" module. Going from this, the system will figure out the dependencies and only include those in the final output. Also everything will be in the right order.

&lt;br /&gt;
&lt;pre&gt;find ./dojo -name '*.js' | xargs java -jar ../../build/compiler.jar --formatting=PRETTY_PRINT --process_common_js_modules --transform_amd_modules --common_js_entry_module=dojo/behavior.js&lt;/pre&gt;
This command shows how you can just use find to pipe all your modules into CC and then have it figure out what you need based on the common_js_entry_module.


&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;


Caveats&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;b&gt;I did this just for fun.&lt;/b&gt; However, I'm not actually using AMD or Common JS myself right now, so this isn't battle hardened (but it does compile Dojo). Please help me with feedback and patches.
&lt;/li&gt;
&lt;li&gt;There will be many bugs.
&lt;/li&gt;
&lt;li&gt;It does not support Require.js plugins because those can usually not be executed statically (that is their whole point).
&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;h3&gt;


How to use it&lt;/h3&gt;
Checkout the &lt;a href="http://code.google.com/p/closure-compiler/source/checkout"&gt;CC source&lt;/a&gt; and compile it yourself.


&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;


On Performance&lt;/h3&gt;
I'd argue that if you need to load some JS, doing it in a single requests usually always wins. Having *ALL* your JS in one file is, however, usually not a good idea. You want to incrementally load stuff. How to do that within the framework of what I described above is left as an exercise to the reader.&lt;br /&gt;
&lt;br /&gt;
&lt;a href="https://plus.google.com/u/1/116910304844117268718/posts/b6NEFxb438P"&gt;Comment here&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5274051535398117366-7061486791281035746?l=www.nonblocking.io' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/joose/~4/vdImLJkiw3Q" height="1" width="1"/&gt;</content><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/5274051535398117366/posts/default/7061486791281035746?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/5274051535398117366/posts/default/7061486791281035746?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/joose/~3/vdImLJkiw3Q/experimental-support-for-common-js-and.html" title="Experimental support for Common JS and AMD/require.js modules in Closure Compiler" /><author><name>Malte</name><uri>http://www.blogger.com/profile/02896910385129311939</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><feedburner:origLink>http://www.nonblocking.io/2011/12/experimental-support-for-common-js-and.html</feedburner:origLink></entry><entry gd:etag="W/&quot;A08NQXozeCp7ImA9WhRREk4.&quot;"><id>tag:blogger.com,1999:blog-5274051535398117366.post-183561450511911213</id><published>2011-11-25T08:10:00.000-08:00</published><updated>2011-11-25T08:51:30.480-08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-11-25T08:51:30.480-08:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="jscomp" /><title>ADVANCED_OPTIMIZATIONS in Google Closure Compiler</title><content type="html">&lt;a href="http://code.google.com/closure/compiler/"&gt;Google Closure Compiler&lt;/a&gt; supports a compilation mode called "ADVANCED_OPTIMIZATIONS". There are &lt;a href="http://code.google.com/closure/compiler/docs/api-tutorial3.html"&gt;official docs&lt;/a&gt; but I wanted to highlight a couple interesting aspects of what activating this mode means in practice. These features set closure compiler apart from other minifiers such as &lt;a href="https://github.com/mishoo/UglifyJS"&gt;UglifyJS&lt;/a&gt; and can potentially help to substantially decrease code size. They do, however, come as trade offs that limit your flexibility with using the JavaScript syntax. It is a common myth that advanced optimizations require using JSDoc types (which are then checked by closure compiler). This is not true.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Property renaming&lt;/h3&gt;Closure compiler can rename properties.&lt;br /&gt;&lt;code&gt;foo.longName&lt;/code&gt;&lt;br /&gt;becomes&lt;br /&gt;&lt;code&gt;foo.a&lt;/code&gt;&lt;br /&gt;Of course, this will break, once you call &lt;code&gt;foo['longName']&lt;/code&gt;, so be careful :)&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Property collapsing&lt;/h3&gt;Closure compiler can collapse property chains (which are often used to represent namespaces in JS)&lt;br /&gt;&lt;code&gt;foo.bar.baz&lt;/code&gt;&lt;br /&gt;becomes&lt;br /&gt;&lt;code&gt;foo$bar$baz&lt;/code&gt; (and will eventually be minified with standard variable renaming.&lt;br /&gt;There are various exit conditions when this optimization cannot be applied. Never assign to a global object twice.  Don't alias objects in the property chain as in: &lt;code&gt;a = {b: 0}; c = a; c.b = 5;&lt;/code&gt;&lt;br /&gt;This optimization reduces code size and can increase execution speed because fewer lookups have to be performed.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;The catch&lt;/h3&gt;&lt;b&gt;Don't use the module pattern.&lt;/b&gt; Code, that includes the module pattern usually will not minify well with closure compiler. Yeah, sorry about that :) This is not because the closure compiler engineers do not like the module pattern (and similar constructs) but rather because invariants that have to exist for the optimizations (and others) described above, can not be (easily) shown for code with very complex runtime semantics.&lt;br /&gt;We recently had an &lt;a href="https://plus.google.com/116910304844117268718/posts/1bdMB4n4sKZ"&gt;awesome conversation of G+&lt;/a&gt; about how to fix this. The idea is to transform special cases of idiomatic JavaScript (in this case using AMD) to a form that can be efficiently compiled by closure compiler. Lets do this!&lt;br /&gt;&lt;br /&gt;&lt;a href="https://plus.google.com/116910304844117268718/posts/HGtkDn1aVc3"&gt;Comments&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5274051535398117366-183561450511911213?l=www.nonblocking.io' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/joose/~4/hDSnIF-4Gs8" height="1" width="1"/&gt;</content><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/5274051535398117366/posts/default/183561450511911213?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/5274051535398117366/posts/default/183561450511911213?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/joose/~3/hDSnIF-4Gs8/advancedoptimizations-in-google-closure.html" title="ADVANCED_OPTIMIZATIONS in Google Closure Compiler" /><author><name>Malte</name><uri>http://www.blogger.com/profile/02896910385129311939</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><feedburner:origLink>http://www.nonblocking.io/2011/11/advancedoptimizations-in-google-closure.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DkMDRnkyeCp7ImA9WhdUGU4.&quot;"><id>tag:blogger.com,1999:blog-5274051535398117366.post-7914492060507627871</id><published>2011-10-06T12:40:00.000-07:00</published><updated>2011-10-06T13:54:37.790-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-10-06T13:54:37.790-07:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="jsconf" /><title>JSConf EU – The How-To Guide</title><content type="html">Last weekend was the third &lt;a href="http://jsconf.eu"&gt;JSConf EU&lt;/a&gt;. Again it was both an extremely challenging and rewarding experience as an organizer. We are honored to welcome such amazing attendees and speakers to create the ultimate nerd-heaven weekend. Thanks for coming from &lt;a href="https://twitter.com/janl"&gt;Jan&lt;/a&gt;, &lt;a href="https://twitter.com/hblank"&gt;Holger&lt;/a&gt; and &lt;a href="https://twitter.com/cramforce"&gt;me&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;People seem to &lt;a href="http://i.imgur.com/GGPWa.png"&gt;like our little conference&lt;/a&gt;. These are a couple paragraphs about how we put it on. The world still needs more JSConfs. Come to one of ours and then talk to us how to do one at your place of the world.&lt;br /&gt;&lt;br /&gt;&lt;img src="http://farm7.static.flickr.com/6226/6208267885_dd8057e0c8.jpg" align=left width=400&gt;&lt;b&gt;Done for love, not profit&lt;/b&gt;&lt;br /&gt;We spend every dime that we take it on the conference and don’t aim for profit. Often sponsors are signed at the last minute when all vendors are already paid for so it is actually hard to spend more money. In this case we’ll just increase the party and food budget.&lt;br /&gt;Not going for profit makes a whole different conference experience possible. While conferences usually optimize for creating the best possible value for the companies that pay for attendees’ tickets or the sponsors, we can make seemingly irrational decisions like having the best espresso maker in town at our venue that make a big difference for our attendees.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Parties&lt;/b&gt;&lt;br /&gt;We always do 3 parties: One before the conference, a big one after the first day (because everyone will have time and be in town) and another one after the conference.&lt;br /&gt;As we are in Berlin we try to bring the original Berlin party experience to our attendees. That means torn down venues in abandoned homes and lots of Techno.&lt;br /&gt;Drinks are always free for all which certainly helps in making the parties great.&lt;br /&gt;We also invite the local Berlin developer scene and friends to the parties. We want to show the people that travelled here how awesome this community is and we want to give the community a chance to meet all the great people who come. This usually works out great.&lt;br /&gt;&lt;br /&gt;&lt;img src="http://farm3.static.flickr.com/2724/4106809881_c105cc2e82.jpg" align=right width=400&gt;&lt;b&gt;Speakers dinner and other amenities&lt;/b&gt;&lt;br /&gt;Can’t have one because there is a big party for everyone :)&lt;br /&gt;10-20% of our overall attendees are actually speakers. Another large percentage of attendees have spoken at previous installations of the conference. This is another reason why e.g. having a special area on the venue that is exclusive for speakers does not make sense. In conjunction with the No Stage rule below, the ensures that speakers and attendees freely mix and speakers don't camp out hiding. And attendees don't get to feel inferior.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Stage&lt;/b&gt;&lt;br /&gt;We do not have a stage. We feel that part of what makes our conference special is that speakers and attendees are on one level and not having a stage to speak on emphasizes this point.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Speaker travel and hotel&lt;/b&gt;&lt;br /&gt;We pay for our speaker’s travel. On the other hand, we are happy if the speaker’s employer offers to pay for travel. As we are not-for-profit, we immediately turn around and use the budget to make something else more awesome. We also pay for their hotel, we add two nights around the conference at least (for all, not just international speakers) and we make it a fancy hotel, take care of the wifi and do everything else to make sure speakers are treated like rock stars. We don't quite pick them up from the airport individually, but we are thinking about it. If you treat speakers nicely, they are more likely to bring their A-game talks.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Technical depths of talks&lt;/b&gt;&lt;br /&gt;We encourage our speakers to aim their talks at an audience that is made up of experts in their particular field. Thus there won't likely be any “Introduction to X” talks. If one does not happen to be an expert in a given field, it is very likely that one will reach a point in a talk where one does not understand everything. At this point, if you are not a fan of mind blowing brain massages, JSConf is not for you. On the other hand, this does actually enable the conference to push attendees into new levels of understanding and interest. One can always read up on details after the conference and maybe eventually become an expert.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Talk length&lt;/b&gt;&lt;br /&gt;Most of our talks are 30 minutes long. The reason is that we want to invite the people who actually created. Not all of them are necessarily experienced public speakers. Doing a 30 minute talk is significantly easier than longer formats which helps keep the quality very high. And if you can't convey your awesome topic in 30 minutes, the attendees are likely to get bored.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Timing&lt;/b&gt;&lt;br /&gt;We do the conference on the weekend. While this might not be ideal for families, we make this tradeoff because we are looking for attendees, which are willing to sacrifice a weekend to come. This is also the reason &lt;a href="https://plus.google.com/116910304844117268718/posts/Cr7Tvy4Jget"&gt;why we start our ticket sales on Sundays.&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Size&lt;/b&gt;&lt;br /&gt;We did 350 people this year. My personal experience was that this was already a little too big, as such a number of people requires executing everything with great precision which is super unlikely for amateurs like us (Professionals can’t do this either, but they don’t care because lots of attendees means lots of money).&lt;br /&gt;250 people seems like a great number for a conference, smaller doesn’t hurt.&lt;br /&gt;&lt;br /&gt;&lt;img src="http://farm7.static.flickr.com/6048/6208909152_b64f14974a.jpg" align=left width=400&gt;&lt;b&gt;Round Tables&lt;/b&gt;&lt;br /&gt;Round tables at a conference make it look more like a wedding and they don't force everyone facing the same direction. Also they encourage conversations, which is our ultimate goal. See also: &lt;a href="http://www.randsinrepose.com/archives/2011/10/03/building_serendipity.html"&gt;Building Serendipity&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Power under every table&lt;/b&gt;&lt;br /&gt;We are geeks and we love our laptops so we make sure we can charge them whenever we want. Next year we should also have power in the lounges as well.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Food&lt;/b&gt;&lt;br /&gt;When a caterer says they can provide you with great food, we assume they are lying. Collecting references and testing the food is a must. It is important to have multiple food lines to make sure everybody gets their lunch quick - even if not everyone undstands this within milliseconds. In Berlin, make sure they stock up on Club Mate, and beer.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Venue&lt;/b&gt;&lt;br /&gt;The first year we did JSConf EU we got lucky by finding a venue that was perfect for our size, yet very much provided a unique Berlin experience that you can’t easily find anywhere else in the world. Ever since we are looking for raw, industrial venues that don’t even need electricity, let alone an internet connection and then bring in everything to create a truly unique experience. Compare that with a doing a conference in a business hotel that makes you bored even before the first talk has started.&lt;br /&gt;We learned that it is key to have great light artists who can turn every shithole into something very special.&lt;br /&gt;&lt;br /&gt;&lt;img src="https://lh4.googleusercontent.com/-Fks3Bd4ac68/TosjSK-u-uI/AAAAAAAADTQ/BJV5DixlUBg/s1024/IMAG0445.jpg" width="400" align=right&gt;&lt;b&gt;Wifi&lt;/b&gt;&lt;br /&gt;Impeccable Wifi is a must. Venues usually don't know/care or even lie about their capabilities. 16Mbit DSL over two routers is not enough for 200 people. Heck, it isn't enough for 20 people. See &lt;a href="http://www.nonblocking.io/2011/05/how-to-make-wifi-work-at-tech.html"&gt;my article about this topic&lt;/a&gt; for suggestions. Do not trust venues unless you tried their wifi with a crowd as big as yours.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Sound&lt;/b&gt;&lt;br /&gt;We always hire professional sound engineers for every track. They do things such as turning down the volume when a speaker sneezes which is a small thing but quite valuable when you sit in the audience. Make sure you have headset microphones as they work much better for inexperienced speakers and that you have a monitor box for the speaker.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Video&lt;/b&gt;&lt;br /&gt;We rent the most expensive and awesome professional projectors money can buy (even if we then have to save budget elsewhere).&lt;br /&gt;We’ll use DVI next year. Using VGA was a mistake (although not a bad one). Make sure you have VGA-to-DVI-I equipment and a pack of all possible converters on the speaker desk (You’ll also want a Apple power adapter there as usually 95% of speakers use Apple hardware).&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Selling tickets&lt;/b&gt;&lt;br /&gt;Conferences only sell tickets when they are sold out. This is a fact of life. Should you ever consider to run a conference, count your friends who will definitely buy a ticket. This will be the number of tickets you sell in early bird round one. Make sure your friends buy their tickets in the second sales open and then tweet about it immediately. The rest of the tickets will sell like a charm.&lt;br /&gt;Fortunately we don’t have to pull tricks like this anymore, as we usually sell out all tickets in seconds.&lt;br /&gt;We have three ticket categories: 1. a special rate for attendees of the previous US conference (if you come to both you are truly bad ass) 2. an early bird with a fixed percentage of total tickets, say 20-40% and 3. Regular tickets. There are no student discounts, no press tickets, no community tickets. Here is why: Since all money goes into making the conference, we can make the tickets as cheap as possible. And we feel it is fairer for everybody to pay a low price rather than having some pay more so few can pay less. This means some won't have the funds to attend, but that's how it is. We usually have a number of sponsors running contests around tickets which gives you a chance in.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Sponsors&lt;/b&gt;&lt;br /&gt;Sponsors appreciate sponsoring great conferences. Great conferences do not let their sponsors influence their decision-making. The way we make sure this happens, we generally follow this procedure while budgeting: the amount of money taken in from the attendees covers all base costs, venue, speakers, food, everything, but it might mean that we have 3-star food, and only a limited amount of free drinks at the parties. A sponsor then come in and upgrade the food and create a larger drink tab, or pay for another DJ set or something. But they can never interfere with the base conference setup. That said, we do not feel it is bad to have sponsors speak, in fact, we love it. But it is important to be very explicit about the fact, that product advertisements are not acceptable. If in doubt, we check the slides in advance and work with sponsors to make sure they don't miss a chance to totally delight the finest people in our industry.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;The unexpected&lt;/b&gt;&lt;br /&gt;We always try to do something noone in the audience expects. This might be a musical performance or a talk about nuclear physics. &lt;br /&gt;&lt;br /&gt;&lt;iframe src="http://player.vimeo.com/video/29873668?title=0&amp;amp;byline=0&amp;amp;portrait=0" width="400" height="225" frameborder="0" webkitAllowFullScreen allowFullScreen&gt;&lt;/iframe&gt;&lt;p&gt;&lt;a href="http://vimeo.com/29873668"&gt;JSConf.eu opening song - JavaScript Will Listen&lt;/a&gt; from &lt;a href="http://vimeo.com/langalex"&gt;Alexander Lang&lt;/a&gt; on &lt;a href="http://vimeo.com"&gt;Vimeo&lt;/a&gt;.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;&lt;a href="https://plus.google.com/u/1/116910304844117268718/posts/ZKBmWHP9BLK?hl=en"&gt;Please comment here.&lt;/a&gt;&lt;/b&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5274051535398117366-7914492060507627871?l=www.nonblocking.io' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/joose/~4/_iuAZ3R5u9Q" height="1" width="1"/&gt;</content><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/5274051535398117366/posts/default/7914492060507627871?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/5274051535398117366/posts/default/7914492060507627871?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/joose/~3/_iuAZ3R5u9Q/jsconf-eu-how-to-guide.html" title="JSConf EU – The How-To Guide" /><author><name>Malte</name><uri>http://www.blogger.com/profile/02896910385129311939</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://farm7.static.flickr.com/6226/6208267885_dd8057e0c8_t.jpg" height="72" width="72" /><feedburner:origLink>http://www.nonblocking.io/2011/10/jsconf-eu-how-to-guide.html</feedburner:origLink></entry><entry gd:etag="W/&quot;D08DQ3g7fCp7ImA9WhZbEk8.&quot;"><id>tag:blogger.com,1999:blog-5274051535398117366.post-1591579411792464471</id><published>2011-06-16T04:59:00.000-07:00</published><updated>2011-06-16T05:04:32.604-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-06-16T05:04:32.604-07:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Performance" /><title>window.nextTick</title><content type="html">Node.js has a special API called &lt;a href="http://nodejs.org/docs/v0.4.8/api/process.html#process.nextTick"&gt;process.nextTick&lt;/a&gt; which schedules a function to be executed immediately the next time the event loop is idle.&lt;div&gt;Web applications often try achieve a similar result using setTimeout. The problem is that setTimeout with a zero time parameter does not schedule for immediately but rather for &lt;a href="http://jsperf.com/smallest-timeout"&gt;some time a little later&lt;/a&gt;. This *can* lead to real performance problems.&lt;/div&gt;&lt;div&gt;I &lt;a href="http://jsperf.com/postmessage"&gt;benchmarked a couple ways to do postMessage&lt;/a&gt; between workers, iframes and on the current window itself. Turns out doing postMessage on your current window might be a really good alternative to implement process.nextTick in browsers.&lt;/div&gt;&lt;div&gt;Please tell me what I did wrong in my benchmark.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5274051535398117366-1591579411792464471?l=www.nonblocking.io' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/joose/~4/Us6GdL5__0A" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.nonblocking.io/feeds/1591579411792464471/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=5274051535398117366&amp;postID=1591579411792464471" title="3 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/5274051535398117366/posts/default/1591579411792464471?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/5274051535398117366/posts/default/1591579411792464471?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/joose/~3/Us6GdL5__0A/windownexttick.html" title="window.nextTick" /><author><name>Malte</name><uri>http://www.blogger.com/profile/02896910385129311939</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><thr:total>3</thr:total><feedburner:origLink>http://www.nonblocking.io/2011/06/windownexttick.html</feedburner:origLink></entry><entry gd:etag="W/&quot;Dk8HRng6cCp7ImA9WhZUGEU.&quot;"><id>tag:blogger.com,1999:blog-5274051535398117366.post-9073678194984857827</id><published>2011-06-12T05:34:00.000-07:00</published><updated>2011-06-12T06:20:37.618-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-06-12T06:20:37.618-07:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Performance" /><title>Triggering de-optimized execution in modern JavaScript engines</title><content type="html">Yesterday at &lt;a href="http://nodecamp.eu/"&gt;NodeCamp.eu&lt;/a&gt; I learned from &lt;a href="https://twitter.com/#!/mraleph"&gt;Mr. Aleph&lt;/a&gt; how one can easily get a good idea for when V8 has to run a function in un-optimized mode:&lt;div&gt;&lt;span class="Apple-style-span"&gt;&lt;blockquote&gt;curl -s http://v8.googlecode.com/svn/branches/bleeding_edge/src/hydrogen.cc | grep "Bailout("&lt;/blockquote&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;does the trick.&lt;/div&gt;&lt;div&gt;The result right now is:&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;blockquote&gt;&lt;div&gt;&lt;span class="Apple-style-span"&gt;owner()-&amp;gt;Bailout("bad value context for arguments value");&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"&gt;owner()-&amp;gt;Bailout("bad value context for arguments object value");&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"&gt;builder-&amp;gt;Bailout("arguments object value in a test context");&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"&gt;void HGraphBuilder::Bailout(const char* reason) {&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"&gt;Bailout("function with illegal redeclaration");&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"&gt;Bailout("Unsupported phi-use");&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"&gt;return Bailout("context-allocated arguments");&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"&gt;return Bailout("EnterWithContextStatement");&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"&gt;return Bailout("ExitContextStatement");&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"&gt;return Bailout("SwitchStatement: too many clauses");&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"&gt;return Bailout("SwitchStatement: non-literal switch label");&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"&gt;return Bailout("ForInStatement");&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"&gt;return Bailout("TryCatchStatement");&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"&gt;return Bailout("TryFinallyStatement");&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"&gt;return Bailout("DebuggerStatement");&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"&gt;return Bailout("SharedFunctionInfoLiteral");&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"&gt;return Bailout("reference to rewritten variable");&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"&gt;return Bailout("reference to uninitialized const variable");&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"&gt;return Bailout("reference to const context slot");&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"&gt;return Bailout("reference to a variable which requires dynamic lookup");&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"&gt;return Bailout("Object literal with complex property");&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"&gt;if (!Smi::IsValid(i)) return Bailout("Non-smi key in array literal");&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"&gt;return Bailout("unsupported const compound assignment");&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"&gt;return Bailout("compound assignment to lookup slot");&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"&gt;return Bailout("invalid lhs in compound assignment");&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"&gt;return Bailout("non-initializer assignment to const");&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"&gt;return Bailout("assignment to const context slot");&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"&gt;if (proxy-&amp;gt;IsArguments()) return Bailout("assignment to arguments");&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"&gt;return Bailout("assignment to LOOKUP or const CONTEXT variable");&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"&gt;return Bailout("invalid left-hand side in assignment");&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"&gt;Bailout("arguments access in inlined function");&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"&gt;Bailout("Function.prototype.apply optimization in inlined function");&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"&gt;return Bailout("call to a JavaScript runtime function");&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"&gt;Bailout("delete with global variable");&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"&gt;Bailout("delete with non-global variable");&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"&gt;return Bailout("invalid lhs in count operation");&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"&gt;return Bailout("unsupported count operation with const");&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"&gt;return Bailout("lookup variable in count operation");&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"&gt;return Bailout("Unsupported non-primitive compare");&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"&gt;return Bailout("unsupported declaration");&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"&gt;return Bailout("inlined runtime function: IsNonNegativeSmi");&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"&gt;return Bailout(&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"&gt;return Bailout("inlined runtime function: ClassOf");&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"&gt;return Bailout("inlined runtime function: SetValueOf");&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"&gt;return Bailout("inlined runtime function: RandomHeapNumber");&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"&gt;return Bailout("inlined runtime function: GetFromCache");&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"&gt;return Bailout("inlined runtime function: SwapElements");&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"&gt;return Bailout("inlined runtime function: MathSqrt");&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"&gt;return Bailout("inlined runtime function: IsRegExpEquivalent");&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"&gt;return Bailout("inlined runtime function: FastAsciiArrayJoin");&lt;/span&gt;&lt;/div&gt;&lt;/blockquote&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;I was also pleased to learn that my assumption that modern JS engines de-optimize when they see use of "arguments" is no longer true. E.g. uses like &lt;span class="Apple-style-span"&gt;&lt;span class="Apple-style-span"&gt;fn.apply(this, arguments)&lt;/span&gt; &lt;/span&gt;which is often used in AOP style function wrapping would be able to run in optimized code. I benchmarked various uses of arguments in &lt;a href="http://jsperf.com/arguments-de-opt"&gt;this JSPerf testcase&lt;/a&gt;. The idea is to have a loop in each test which should be easily optimized but which would take so long to run that the actual usage of "arguments" in the first statement should not influence the benchmark itself. Suggestions how to make this better, appreciated :)&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Does anyone know of similar ways to find out about which elements of JS will trigger running in non-optimized mode for other (open source) engines?&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5274051535398117366-9073678194984857827?l=www.nonblocking.io' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/joose/~4/zZlnScGTY-Q" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.nonblocking.io/feeds/9073678194984857827/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=5274051535398117366&amp;postID=9073678194984857827" title="4 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/5274051535398117366/posts/default/9073678194984857827?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/5274051535398117366/posts/default/9073678194984857827?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/joose/~3/zZlnScGTY-Q/when-javascript-jits-de-optimize.html" title="Triggering de-optimized execution in modern JavaScript engines" /><author><name>Malte</name><uri>http://www.blogger.com/profile/02896910385129311939</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><thr:total>4</thr:total><feedburner:origLink>http://www.nonblocking.io/2011/06/when-javascript-jits-de-optimize.html</feedburner:origLink></entry><entry gd:etag="W/&quot;C08HQ3g5cSp7ImA9WhZUEE4.&quot;"><id>tag:blogger.com,1999:blog-5274051535398117366.post-4732478621692490094</id><published>2011-06-02T09:18:00.000-07:00</published><updated>2011-06-02T09:23:52.629-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-06-02T09:23:52.629-07:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="personal" /><title>On a personal note</title><content type="html">Warning: If you do not know my mom you can quit reading now.&lt;br /&gt;&lt;br /&gt;Just wanted to say how proud I am of my mom to quit her job after like 40 years of working, going back to college, finishing that in no time and now open her own practice as a psychologist: &lt;a href="http://www.psychologische-gesundheit-elmshorn.de/"&gt;Psychologische Beratung Elmshorn&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5274051535398117366-4732478621692490094?l=www.nonblocking.io' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/joose/~4/4Gsq32nLt8o" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.nonblocking.io/feeds/4732478621692490094/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=5274051535398117366&amp;postID=4732478621692490094" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/5274051535398117366/posts/default/4732478621692490094?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/5274051535398117366/posts/default/4732478621692490094?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/joose/~3/4Gsq32nLt8o/on-personal-note.html" title="On a personal note" /><author><name>Malte Ubl</name><uri>https://profiles.google.com/116910304844117268718</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-tQJAx2-8rqE/AAAAAAAAAAI/AAAAAAAAEhc/T74iWhwXXf0/s512-c/photo.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://www.nonblocking.io/2011/06/on-personal-note.html</feedburner:origLink></entry><entry gd:etag="W/&quot;D0MNRXw4eSp7ImA9WhZWFEU.&quot;"><id>tag:blogger.com,1999:blog-5274051535398117366.post-6532090834360624561</id><published>2011-05-15T09:19:00.000-07:00</published><updated>2011-05-15T11:51:34.231-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-05-15T11:51:34.231-07:00</app:edited><title>How to make WIFI work at tech conferences (Part 1 of N)</title><content type="html">Good WIFI at tech conferences is hard. Very hard. Usually it doesn't work.&lt;div&gt;&lt;br /&gt;&lt;a href="http://1.bp.blogspot.com/-MdV8X3-POJ0/TdALEVXtHzI/AAAAAAAAAPM/Q0wywwJwdZA/s1600/Screen%2Bshot%2B2011-05-15%2Bat%2B4.48.35%2BPM.png" onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 278px;" src="http://1.bp.blogspot.com/-MdV8X3-POJ0/TdALEVXtHzI/AAAAAAAAAPM/Q0wywwJwdZA/s400/Screen%2Bshot%2B2011-05-15%2Bat%2B4.48.35%2BPM.png" border="0" alt="" id="BLOGGER_PHOTO_ID_5606993705160220466" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;a href="http://1.bp.blogspot.com/-4fUCrFqUvHM/TdALANvm7FI/AAAAAAAAAPE/eMPinMai1R0/s1600/Screen%2Bshot%2B2011-05-15%2Bat%2B4.48.23%2BPM.png" onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 380px; height: 400px;" src="http://1.bp.blogspot.com/-4fUCrFqUvHM/TdALANvm7FI/AAAAAAAAAPE/eMPinMai1R0/s400/Screen%2Bshot%2B2011-05-15%2Bat%2B4.48.23%2BPM.png" border="0" alt="" id="BLOGGER_PHOTO_ID_5606993634393517138" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;At JSConf 2011 &lt;a href="https://twitter.com/#!/fastandfearless"&gt;Meno Abels&lt;/a&gt; and &lt;a href="https://twitter.com/#!/cramforce"&gt;I&lt;/a&gt; tried to make it work. All the credit really goes to networking guru and all-things-software Meno. Also thanks to &lt;a href="http://www.stephouse.net/"&gt;Stephouse.net&lt;/a&gt; for awesome work on the connectivity and the access points. In the following paragraphs I will walk through the challenges one faces when it comes to WIFI at conferences. This article will stay quite shallow technically. If I ever have more time I will dig deeper.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;0. Basics&lt;/b&gt;&lt;br /&gt;Dear conference organizer, this is the part that can be easily fixed: You need to have a person that takes care of the WIFI as a core priority. The WIFI will break and you will need someone who is willing and capable to put on the wetsuit and jump into the shit. E.g. at least one of your access points will be overwhelmed at any given time. Many things can be done to fix this. Somebody will have to do it.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;1. Never trust anyone&lt;/b&gt;&lt;/div&gt;&lt;div&gt;Now you booked a venue and they say that they can handle the WIFI for you. Chances are, they are lying. In any case, if you want to go this route, ask for references and call the references. Purespace wanted to handle the Nodeconf traffic over a 2Mbit/s down, 300 kbit/s up DSL connection. That will not work. Chances are you have more than 300kbit/s in TCP ACK packages for your downstream traffic. At nodeconf Stephouse saved the day by coming in on 5 minute notice and installing a microwave link in about 45 minutes (Remember the wetsuit thing from above).&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;2. Number of attendees and number of devices&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;Calculate 2.3 devices per attendee.&lt;/i&gt;&lt;/div&gt;&lt;div&gt;The primary problem this creates is overwhelmed access points. What can you do to fix it:&lt;/div&gt;&lt;div&gt;&lt;ul&gt;&lt;li&gt;Monitor the number of people attached to each access point.&lt;/li&gt;&lt;li&gt;Add another access point in zones where more people come together than you expected.&lt;/li&gt;&lt;li&gt;Use as many access points as possible. Lower the antenna power as much as possible to decrease the range of each AP.&lt;/li&gt;&lt;li&gt;Never have two APs close to each other on adjacent channels.&lt;/li&gt;&lt;li&gt;Don't use encryption. This will be painful, but it really relaxes the CPU of your APs.&lt;/li&gt;&lt;li&gt;People sometimes move in groups and stay attached to an AP while others move in and go onto the AP because it is the closest one. This creates an uneven distribution of people over the APs.&lt;br /&gt;Easy fix: Throw everyone off the AP. The devices will then try to connect to the closest AP again.&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;&lt;b&gt;&lt;br /&gt;3. Unknown Building, Temporary Setup&lt;/b&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;Conferences are by definition temporary setups. You will have no time to tune your system under real world conditions. But still, you will need to update the configuration throughout the conference to make things work better under real load.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;4. Bandwidth&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;Calculate 100KBit/s per attendee in both directions.&lt;/i&gt;&lt;/div&gt;&lt;div&gt;You can live with a little less up-link traffic but don't go with consumer level DSL. In case you cannot get that kind of bandwidth from a single provider, take all you can get from multiple vendors and use the &lt;a href="https://github.com/mabels/Sashimi"&gt;software that Meno built for JSConf.eu&lt;/a&gt; to aggregate the links (Warning: Only an option if you have a black belt in networking kung fu.)&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;5.  Bandwidth in the Air&lt;/b&gt;&lt;/div&gt;&lt;div&gt;Effective bandwidth per WIFI channel is 20 MBit/s. WIFI has 11 channels.  That means that you will never get more that 220 MBit/s in the air, ever. This bandwidth has to be shared by everybody in the room. If you put 5000 people in a relatively small room, then your WIFI will be slow. There is no way around that. It is simply physics. (You may be able to add the 5GHz channels, but we recently experienced problems with some Apple devices.)&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;6. People&lt;/b&gt;&lt;/div&gt;&lt;div&gt;People are the primary problem for conference WIFI. Actually not people but the software running on their computers which they did not disable. This involves bittorrent clients and backup software. One person at JSConf uploaded 9.6 GB in the first 30 minutes of the conference. This means that one person used almost 80% of all bandwidth. Identifying these "powerusers" will get your conference a long way to good WIFI. Usually you will only have an IP address, so your only option is to either block the person entirely or to block the remote IP where the traffic is going. At JSConf we &lt;a href="http://social-traffic.streamie.org/preso/static/#slide17"&gt;introduced social traffic&lt;/a&gt; which links all traffic to Twitter identities. This way we can used Twitter @-messages to ask people to disable rogue services.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;7. JSConf learnings&lt;/b&gt;&lt;/div&gt;&lt;div&gt;All of the above we learned at previous confs. This is what we learned at JSConf:&lt;/div&gt;&lt;div&gt;With a few 100 people at a conferences there will be a couple stupid persons in the audience. Redirecting all traffic from http://twitter.com to https://twitter.com goes a long way in fixing this problem.&lt;/div&gt;&lt;div&gt;Using an auth service such a social traffic requires white listing of IP addresses. Make sure you highjack all DNS traffic (all on port 53 regardless of DNS server used) so that you are able to control e.g. the IP address you white listed for twitter.com.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;a href="http://social-traffic.streamie.org/preso/static/"&gt;More info in our JSConf presentation.&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Now go back and read point 0. The most important thing is to have somebody who cares and gets their hands dirty when it is most needed.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5274051535398117366-6532090834360624561?l=www.nonblocking.io' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/joose/~4/0Gt7r0YRoWY" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.nonblocking.io/feeds/6532090834360624561/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=5274051535398117366&amp;postID=6532090834360624561" title="28 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/5274051535398117366/posts/default/6532090834360624561?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/5274051535398117366/posts/default/6532090834360624561?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/joose/~3/0Gt7r0YRoWY/how-to-make-wifi-work-at-tech.html" title="How to make WIFI work at tech conferences (Part 1 of N)" /><author><name>Malte</name><uri>http://www.blogger.com/profile/02896910385129311939</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://1.bp.blogspot.com/-MdV8X3-POJ0/TdALEVXtHzI/AAAAAAAAAPM/Q0wywwJwdZA/s72-c/Screen%2Bshot%2B2011-05-15%2Bat%2B4.48.35%2BPM.png" height="72" width="72" /><thr:total>28</thr:total><feedburner:origLink>http://www.nonblocking.io/2011/05/how-to-make-wifi-work-at-tech.html</feedburner:origLink></entry><entry gd:etag="W/&quot;A0cMQXsyfip7ImA9WhZWEU8.&quot;"><id>tag:blogger.com,1999:blog-5274051535398117366.post-8046385165715175620</id><published>2011-05-11T07:44:00.000-07:00</published><updated>2011-05-11T08:51:20.596-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-05-11T08:51:20.596-07:00</app:edited><title>Unsolvable Problems – Why micro libs suck.</title><content type="html">OK, actually I like micro libs. The title of this post is only part of my Hacker News Optimization (HNO) strategy, developed by my PR agency.&lt;div&gt;There has been some heated discussion in my Twitter feed over the last days and following &lt;a href="http://jsconf.us/"&gt;#jsconf&lt;/a&gt; about the benefit and usability of micro libs.&lt;/div&gt;&lt;div&gt;Now one can make many good points pro and contra using micro libs but one stood out to me the most&lt;/div&gt;&lt;div&gt;&lt;blockquote&gt;Micro libs suck because they have weak cross browser compatibility.&lt;/blockquote&gt;&lt;/div&gt;&lt;div&gt;As should be obvious, this is a classic bullshit argument. I can prove it wrong easily: The ultimate micro lib &lt;a href="http://vaporjs.com/"&gt;Vapor.js&lt;/a&gt; has the best cross browser compatibility of all JavaScript code that has ever been written.&lt;/div&gt;&lt;div&gt;Coming back to the original argument, even joking aside, it is still wrong. There might be individual micro libs with certain bugs but that is true for all environments. Roughly 99% of all jQuery modules are badly written and contain massive bugs. The situation might be better in big toolkits such as quooxdoo, dojo and Ext but why is that really?&lt;/div&gt;&lt;div&gt;Right, these guys actually test their stuff.&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"&gt;&lt;b&gt;Testing&lt;/b&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"&gt;Cross browser or interoperability problems between micro libs are actually a problem of insufficient testing. Now the good thing is that we, as the JavaScript community have one pretty unique feature that will help us solve the testing dilemma:&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"&gt;&lt;/span&gt;&lt;blockquote&gt;&lt;span class="Apple-style-span"&gt;The&lt;a href="http://jsconf.eu/2010/communityjs_by_chris_williams_1.html"&gt; founding myth&lt;/a&gt; of the JavaScript community &lt;/span&gt;&lt;span class="Apple-style-span"&gt;&lt;a href="http://blog.rebeccamurphey.com/modern-javascript"&gt;is not based on Perl-Angst.&lt;/a&gt;&lt;/span&gt;&lt;/blockquote&gt;&lt;span class="Apple-style-span"&gt;&lt;a href="http://blog.rebeccamurphey.com/modern-javascript"&gt;&lt;/a&gt;Thus we may copy everything that made CPAN successful without falling prey to not-invented-here-syndrom that made gems, pips and all the others only sub-par competitors.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"&gt;&lt;a href="https://twitter.com/#!/izs"&gt;Izaac&lt;/a&gt; showed great vision at &lt;a href="http://www.nodeconf.com/"&gt;#nodeconf &lt;/a&gt;when he described the future of npm using &lt;a href="http://testanything.org/"&gt;TAP&lt;/a&gt; in the testing layer. TAP, which comes from the Perl community but really hasn't anything to do with it, is a protocol for test-runner output. It is really easy to produce, human readable and fairly easy to consume as a machine. With TAP everyone can use their favorite testing framework, even cucumberish natural language comprehension using regexes, if only it supports TAP output. On the other side we are able to build awesome tools that process the TAP output of your tests to do awesome things with it.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"&gt;See for example &lt;a href="http://static.cpantesters.org/distro/A/Archive-Zip.html"&gt;this page&lt;/a&gt; that shows automated test results gathered from people installing &lt;a href="http://search.cpan.org/~adamk/Archive-Zip-1.30/lib/Archive/Zip.pm"&gt;Archive::Zip&lt;/a&gt;. So it mostly passes everywhere but there are some weird outliers that go wrong. Does that sound familiar? Yes, this is just like things happening in browsers.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"&gt;Imagine if the awesome &lt;a href="http://microjs.com/"&gt;microjs.com&lt;/a&gt; by Mr. &lt;a href="https://twitter.com/#!/thomasfuchs"&gt;Thomas Fuchs&lt;/a&gt; had an extra select box like this:&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;a href="http://3.bp.blogspot.com/-koTkCAv9e_M/Tcqn5xYXKgI/AAAAAAAAAO8/-mN2ULgITIE/s1600/Screen%2Bshot%2B2011-05-11%2Bat%2B5.13.29%2BPM.png" onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 241px;" src="http://3.bp.blogspot.com/-koTkCAv9e_M/Tcqn5xYXKgI/AAAAAAAAAO8/-mN2ULgITIE/s400/Screen%2Bshot%2B2011-05-11%2Bat%2B5.13.29%2BPM.png" border="0" alt="" id="BLOGGER_PHOTO_ID_5605477297165904386" /&gt;&lt;/a&gt;&lt;br /&gt;Using our unified testing infrastructure each library would be tested in all possible browser environments. The select-box now allows you to select which environment is important for you and only modules compatible with that environment will be shown to you. Remember, it is perfectly valid that &lt;a href="http://zeptojs.com/"&gt;zepto.js&lt;/a&gt; chooses not to run in IE. If that is important to you, update your selection and it will no longer be recommended.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;Closing words&lt;/b&gt;&lt;/div&gt;&lt;div&gt;Lets build this infrastructure. I for one donate my free time starting now.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;PS: Everytime I say "micro lib" you may read "module" instead :)&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5274051535398117366-8046385165715175620?l=www.nonblocking.io' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/joose/~4/13dhdhNCsXY" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.nonblocking.io/feeds/8046385165715175620/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=5274051535398117366&amp;postID=8046385165715175620" title="6 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/5274051535398117366/posts/default/8046385165715175620?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/5274051535398117366/posts/default/8046385165715175620?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/joose/~3/13dhdhNCsXY/unsolvable-problems-why-micro-libs-suck.html" title="Unsolvable Problems – Why micro libs suck." /><author><name>Malte</name><uri>http://www.blogger.com/profile/02896910385129311939</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://3.bp.blogspot.com/-koTkCAv9e_M/Tcqn5xYXKgI/AAAAAAAAAO8/-mN2ULgITIE/s72-c/Screen%2Bshot%2B2011-05-11%2Bat%2B5.13.29%2BPM.png" height="72" width="72" /><thr:total>6</thr:total><feedburner:origLink>http://www.nonblocking.io/2011/05/unsolvable-problems-why-micro-libs-suck.html</feedburner:origLink></entry><entry gd:etag="W/&quot;D0QCSHc8eyp7ImA9WhZRFEs.&quot;"><id>tag:blogger.com,1999:blog-5274051535398117366.post-7582283290830477144</id><published>2011-04-10T12:27:00.000-07:00</published><updated>2011-04-10T12:56:09.973-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-04-10T12:56:09.973-07:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="jQuery" /><title>The jQuery module anti-pattern</title><content type="html">I don't really know why I'm writing about this now, but it is on my mind and I want to let it go :)&lt;div&gt;I like jQuery. Boom. I'm sorry if that hurts your feelings. I like it as a neat DOM access layer and basic building block of desktop websites and applications. I'm saying building block, because only jQuery will not get you far, but that is another story.&lt;/div&gt;&lt;div&gt;There is one rule that you have to keep in mind when you are using jQuery and you care about your code to scale beyond a few lines:&lt;/div&gt;&lt;div&gt;&lt;b&gt;You can never use jQuery modules.&lt;/b&gt;&lt;/div&gt;&lt;div&gt;jQuery modules (as in methods and properties hanging off the jQuery's prototype object or, to a lesser extent, things hanging off the jQuery function itself) are practically always the wrong design.&lt;/div&gt;&lt;div&gt;&lt;ul&gt;&lt;li&gt;They all live in a single namespace, so there is risk of collision.&lt;/li&gt;&lt;li&gt;They force a DOM centric application design.&lt;/li&gt;&lt;li&gt;It almost never looks nice as an API if you are not implementing actions like "show" and "hide".&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;Instead for almost all use cases where people use jQuery modules it would be better to use a "stratified" API which is this case just means that you pass the jQuery object you want to work on to a function, rather then calling the function on the jQuery object. While jQuery's beauty derives from chaining its basic methods, this usually doen't make sense for more complex modules.&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;a href="http://api.jquery.com/jQuery.sub/"&gt;jQuery.sub()&lt;/a&gt; has been introduced to somewhat reduce the risk of namespace collision. I think it will go down in history as the most blatant example of a &lt;a href="http://www.joelonsoftware.com/articles/LeakyAbstractions.html"&gt;leaky abstraction&lt;/a&gt; because you end up never knowing the exact type of a given jQuery object unless you instantiated it yourself.&lt;/div&gt;&lt;div&gt;What about all those nice jQuery modules, you can copy and paste into your application to make fancy things? My personal experience is that most are of very bad quality, so it makes sense to rewrite them to a stratified API style (usually this only take a few minutes) and then assume ownership of the code and fix bugs as they appear.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5274051535398117366-7582283290830477144?l=www.nonblocking.io' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/joose/~4/6TYMEgsdm64" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.nonblocking.io/feeds/7582283290830477144/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=5274051535398117366&amp;postID=7582283290830477144" title="7 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/5274051535398117366/posts/default/7582283290830477144?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/5274051535398117366/posts/default/7582283290830477144?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/joose/~3/6TYMEgsdm64/jquery-module-anti-pattern.html" title="The jQuery module anti-pattern" /><author><name>Malte</name><uri>http://www.blogger.com/profile/02896910385129311939</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><thr:total>7</thr:total><feedburner:origLink>http://www.nonblocking.io/2011/04/jquery-module-anti-pattern.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DkQHRHw_fSp7ImA9Wx9aFUw.&quot;"><id>tag:blogger.com,1999:blog-5274051535398117366.post-6371124607646149204</id><published>2011-03-07T08:01:00.000-08:00</published><updated>2011-03-07T08:12:15.245-08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-03-07T08:12:15.245-08:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Performance" /><title>Optimizing HTML(5) web apps</title><content type="html">Last month I wrote about why &lt;a href="http://www.nonblocking.io/2011/02/on-premature-optimization.html"&gt;premature optimization is indeed the root of all evil&lt;/a&gt;.&lt;div&gt;Speaking against premature optimization is often misinterpreted as an argument against optimizations. Which it isn't. That's why the word &lt;i&gt;premature&lt;/i&gt; is included in the term.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;In reality building web apps with what is called HTML5 technology can be quite hard when it comes to have everything perform really well. I tried to put &lt;a href="http://www.html5rocks.com/tutorials/speed/html5/"&gt;everything I know about optimizing the performance of web apps into one article on html5rocks.com&lt;/a&gt; (+ some of what &lt;a href="http://twitter.com/paul_irish"&gt;@paul_irish&lt;/a&gt; knows about the subject). I hope you enjoy reading it and don't skip over the chapter about profiling :)&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5274051535398117366-6371124607646149204?l=www.nonblocking.io' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/joose/~4/MSyKXemYCpo" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.nonblocking.io/feeds/6371124607646149204/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=5274051535398117366&amp;postID=6371124607646149204" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/5274051535398117366/posts/default/6371124607646149204?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/5274051535398117366/posts/default/6371124607646149204?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/joose/~3/MSyKXemYCpo/optimizing-html5-web-apps.html" title="Optimizing HTML(5) web apps" /><author><name>Malte</name><uri>http://www.blogger.com/profile/02896910385129311939</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><thr:total>0</thr:total><feedburner:origLink>http://www.nonblocking.io/2011/03/optimizing-html5-web-apps.html</feedburner:origLink></entry><entry gd:etag="W/&quot;D0MGQHg9eyp7ImA9Wx9UGEU.&quot;"><id>tag:blogger.com,1999:blog-5274051535398117366.post-5626535186280373947</id><published>2011-02-16T10:51:00.000-08:00</published><updated>2011-02-16T11:43:41.663-08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-02-16T11:43:41.663-08:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Performance" /><title>On Premature Optimization</title><content type="html">There has been a lot of discussion in my &lt;a href="http://twitter.com/#!/cramforce/following"&gt;Twitter peer group&lt;/a&gt; lately about what's the right level of optimizing website performance and how to go about it.&lt;div&gt;&lt;blockquote&gt;Premature optimization is the root of all evil.&lt;/blockquote&gt;&lt;/div&gt;&lt;div&gt;This sentence is true but it might also seem like an excuse to never optimize, so lets elaborate a little on the condition when and how to optimize your code.&lt;/div&gt;&lt;div&gt;Lets start by defining optimization for the purpose of this blog entry:&lt;/div&gt;&lt;div&gt;&lt;blockquote&gt;When optimizing for performance one chooses between two or more implementation strategies and picks the fastest one based on the reason that it is faster then the other strategies.&lt;/blockquote&gt;&lt;/div&gt;&lt;div&gt;Now that we have defined performance optimization as a choice between multiple implementation strategies is becomes clear that it is just a subset of the more general concept of software design. When picking between implementation strategies in software design one has to regularly pick the worst of all evils to achieve the best possible outcome. The trade offs are:&lt;/div&gt;&lt;div&gt;&lt;ul&gt;&lt;li&gt;Maintainability&lt;/li&gt;&lt;li&gt;Scope&lt;/li&gt;&lt;li&gt;Quality&lt;/li&gt;&lt;li&gt;Performance&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;You never get more than 3 of those right with limited ressources. This also means that if we want great performance we will have to be cool with worse maintainability, reduced scope and/or worse quality. As a software designer we need to be aware of this and should make a very conscious decision when we opt for great performance.&lt;/div&gt;&lt;/div&gt;&lt;div&gt;Now &lt;a href="http://stevesouders.com/"&gt;Steve Souders&lt;/a&gt; and friends have proven to great length that in the special case of website and web application load time performance can have a great positive impact on the overall success of a project, thus is will often be the right thing to opt for performance and e.g. decide to reduce scope.&lt;/div&gt;&lt;div&gt;Now if performance optimization can be a good thing, then why is premature optimization always evil: Because the &lt;i&gt;prematur&lt;/i&gt;e refers to the fact that the performance optimization was not done because of a conscious design decision but for other reasons: E.g. because we can or because when writing that ray tracer the other day something proved to be faster.&lt;/div&gt;&lt;div&gt;To differentiate mature from premature optimization I already late down that conscious decisions are the key factor. What is needed for a conscious decision:&lt;br /&gt;&lt;b&gt;Data&lt;/b&gt;&lt;/div&gt;&lt;div&gt;When thinking about data and performance profiling comes to mind. Yes, profiling is very, very important, but I was also referring to data in terms of how the optimization will impact the scope, quality and maintainability of your application.&lt;/div&gt;&lt;div&gt;Unfortunately profiling is not the solution to all performance problems. Often you have to make decision early in the software design process when little code is written and you cannot really profile anything (you could micro profile but you don't know if that code is relevant to your overall performance yet). Thus besides profiling you also want to use experience to get some things right in the first place. If you don't have that experience, buy &lt;a href="http://www.amazon.de/Even-Faster-Web-Sites-Performance/dp/0596522304/ref=sr_1_1?ie=UTF8&amp;amp;qid=1297885043&amp;amp;sr=8-1"&gt;some&lt;/a&gt; &lt;a href="http://www.amazon.de/Speed-Matters-Psychology-Technology-High-Performance/dp/0321715209/ref=ntt_at_ep_dpi_2"&gt;books&lt;/a&gt;. Experience often also is the only way to assess how an optimization will impact things like quality or maintainability. I guess, you need a lot of experience to write good software :)&lt;/div&gt;&lt;div&gt;The good thing is: Computers are fast. This leads to an important rule:&lt;/div&gt;&lt;div&gt;&lt;blockquote&gt;When things aren't too slow, optimizing them will always be a bad idea because the the trade offs in quality, maintainability and scope will result in a net loss.&lt;/blockquote&gt;&lt;/div&gt;&lt;div&gt;When designing your software, you should &lt;b&gt;never&lt;/b&gt; pick an implementation strategy because it is faster unless you have data (through profiling or experience) to support your decision.&lt;/div&gt;&lt;div&gt;In concrete terms for JavaScript when picking between&lt;/div&gt;&lt;div&gt;&lt;ol&gt;&lt;li&gt;C-style loop and forEach -&gt;  use forEach.&lt;/li&gt;&lt;li&gt;String builder and concatenation -&gt; use concatenation&lt;/li&gt;&lt;li&gt;Script tag VS. script loader -&gt; use a script tag&lt;/li&gt;&lt;li&gt;etc.&lt;/li&gt;&lt;/ol&gt;&lt;div&gt;If you ever pick the faster alternative that is cool as well, just make sure that when you work on my team that you are able to back up your decision with data or I'll make you change it to the slower code with a really embarrassing commit statement :)&lt;/div&gt;&lt;/div&gt;&lt;div&gt;PS: Point 1+2 in the list above are examples where you will only ever opt to the faster version after profiling. Point 3 is a good example where your experience might tell you that the tradeoff of using a script loader is well worth the effort.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5274051535398117366-5626535186280373947?l=www.nonblocking.io' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/joose/~4/jDJga4NJfJo" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.nonblocking.io/feeds/5626535186280373947/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=5274051535398117366&amp;postID=5626535186280373947" title="18 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/5274051535398117366/posts/default/5626535186280373947?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/5274051535398117366/posts/default/5626535186280373947?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/joose/~3/jDJga4NJfJo/on-premature-optimization.html" title="On Premature Optimization" /><author><name>Malte</name><uri>http://www.blogger.com/profile/02896910385129311939</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><thr:total>18</thr:total><feedburner:origLink>http://www.nonblocking.io/2011/02/on-premature-optimization.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DUMDRn85eyp7ImA9Wx9UGE0.&quot;"><id>tag:blogger.com,1999:blog-5274051535398117366.post-7213438300140964698</id><published>2011-02-15T11:54:00.000-08:00</published><updated>2011-02-15T14:04:37.123-08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-02-15T14:04:37.123-08:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="streamie" /><title>Streamie and the Chrome webstore</title><content type="html">Full Disclosure: I work for Google but &lt;a href="http://streamie.org/"&gt;Streamie&lt;/a&gt; is my private just-for-fun project. Your results may vary.&lt;div&gt;&lt;br /&gt;I will do a post on Streamie's overall Analytics soon, but lets start off with the numbers related to the Chrome webstore.&lt;div&gt;This data is from the first week of 2011. &lt;a href="http://streamie.org/"&gt;Streamie&lt;/a&gt; was used by a hardcore scene of enthusiastic users and the numbers weren't big. At this time Streamie was already in the &lt;a href="https://chrome.google.com/webstore/detail/jgfbjkfnffkhpnbnnkgaafipalhhblam"&gt;webstore&lt;/a&gt; but it wasn't actually available to users of the stable version of Chrome.&lt;/div&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/-wfVmMyypLyE/TVrbkjclR9I/AAAAAAAAADk/PnBpfsMshwU/s1600/Screen%2Bshot%2B2011-02-15%2Bat%2B8.57.31%2BPM.png"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 400px; height: 103px;" src="http://4.bp.blogspot.com/-wfVmMyypLyE/TVrbkjclR9I/AAAAAAAAADk/PnBpfsMshwU/s400/Screen%2Bshot%2B2011-02-15%2Bat%2B8.57.31%2BPM.png" border="0" alt="" id="BLOGGER_PHOTO_ID_5574008909861308370" /&gt;&lt;/a&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The Chrome webstore launched to all users on January 7th and on January 12th Streamie became a &lt;a href="https://chrome.google.com/webstore/detail/jgfbjkfnffkhpnbnnkgaafipalhhblam"&gt;featured application&lt;/a&gt;. This is what happened:&lt;/div&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/-KqglYq6LUWM/TVrcji6SgiI/AAAAAAAAADs/xpU0xDWZvp8/s1600/Screen%2Bshot%2B2011-02-15%2Bat%2B9.02.31%2BPM.png"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 400px; height: 104px;" src="http://1.bp.blogspot.com/-KqglYq6LUWM/TVrcji6SgiI/AAAAAAAAADs/xpU0xDWZvp8/s400/Screen%2Bshot%2B2011-02-15%2Bat%2B9.02.31%2BPM.png" border="0" alt="" id="BLOGGER_PHOTO_ID_5574009992049230370" /&gt;&lt;/a&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;I will put more data on what happened after that in a later post, but as far as the Chrome webstore is concerned it continued to send a significant amount of traffic to the site (both as direct referrers and from the newtab page).&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;Some final numbers:&lt;/b&gt;&lt;/div&gt;&lt;div&gt;Users who have Streamie installed as an app in Chrome have&lt;/div&gt;&lt;div&gt;- spend 300,029 minutes using Streamie&lt;/div&gt;&lt;div&gt;- send about 10,000 tweets with Streamie&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;I'm personally somewhat surprised with the overall traffic that Streamie is receiving. It is nice that people like it. The Chrome webstore definitely helped pushing the traffic to a new level, though, which, of course, triggered a lot of blog posts and tweets and then more traffic :)&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5274051535398117366-7213438300140964698?l=www.nonblocking.io' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/joose/~4/9pylcghrnng" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.nonblocking.io/feeds/7213438300140964698/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=5274051535398117366&amp;postID=7213438300140964698" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/5274051535398117366/posts/default/7213438300140964698?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/5274051535398117366/posts/default/7213438300140964698?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/joose/~3/9pylcghrnng/streamie-and-chrome-webstore.html" title="Streamie and the Chrome webstore" /><author><name>Malte</name><uri>http://www.blogger.com/profile/02896910385129311939</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://4.bp.blogspot.com/-wfVmMyypLyE/TVrbkjclR9I/AAAAAAAAADk/PnBpfsMshwU/s72-c/Screen%2Bshot%2B2011-02-15%2Bat%2B8.57.31%2BPM.png" height="72" width="72" /><thr:total>0</thr:total><feedburner:origLink>http://www.nonblocking.io/2011/02/streamie-and-chrome-webstore.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DEMCRXs4fSp7ImA9Wx9UFk0.&quot;"><id>tag:blogger.com,1999:blog-5274051535398117366.post-416821433978067559</id><published>2011-02-13T03:59:00.000-08:00</published><updated>2011-02-13T06:14:24.535-08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-02-13T06:14:24.535-08:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="streamie" /><title>Taking away information</title><content type="html">&lt;div&gt;&lt;a href="http://streamie.org/"&gt;Streamie&lt;/a&gt; was always a very selfish project. I wanted to build a Twitter client that is optimized for my personal usage. That is consuming tweets from about 350 people on my main account.&lt;/div&gt;&lt;div&gt;I have to be brutally honest: I have always used a second Twitter client (Tweetie aka Twitter for Mac) in parallel. That is because Streamie does not support multiple accounts which is a feature that I need. Why did I not implement this? Because it is not important for my consumption habits. On all the other accounts (@jsconfeu, @hhjs, @streamie and some others) I never actually read the tweets but only look out for mentions and DMs.&lt;/div&gt;&lt;div&gt;That said Streamie is moving forward as a get-out-of-your-way tweet-consumption application: All information that is not completely necessary to read a tweet has been removed from the default view. You can't see the name of the author (but his avatar (This works for me even though I suck at remembering faces)), the age of the tweets or any buttons at all. Just the avatar and the text.&lt;/div&gt;&lt;div&gt;I was recently experimenting with integrating embedly and a two-pane view to view content without navigating away. People hated it and it was against the simplicity of Streamie. People actually like opening background tabs for later reading.&lt;/div&gt;&lt;div&gt;In adition to this several small refinements have recently landed in Streamie&lt;/div&gt;&lt;div&gt;&lt;ul&gt;&lt;li&gt;The ugly t.co links shortened by Twitter are replaced with a short version of the actual URL. Clicking the link does not go through t.co. This is good for your privacy and also much faster.&lt;/li&gt;&lt;li&gt;You can now choose for which type of tweet (all, mentions, DM) you want to be notified. Differently colored favicons are used to for notification through the tab-bar.&lt;/li&gt;&lt;li&gt;When somebody deletes a tweet, the tweet content is displayed with a &lt;strike&gt;strike&lt;/strike&gt; and you can actually delete your own tweets.&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;I'm very open for more ideas about information that can be thrown away or hidden behind a hover or click (please comment). I'm not completely happy with the current hover behavior. At least for touch devices this needs to be changed to a click and maybe it should be a click everywhere (Opinions?).&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Update: I changed the rollover info to never resize the tweet. The layout jumping was too much.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5274051535398117366-416821433978067559?l=www.nonblocking.io' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/joose/~4/z5xfOa4urTg" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.nonblocking.io/feeds/416821433978067559/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=5274051535398117366&amp;postID=416821433978067559" title="2 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/5274051535398117366/posts/default/416821433978067559?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/5274051535398117366/posts/default/416821433978067559?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/joose/~3/z5xfOa4urTg/taking-away-information.html" title="Taking away information" /><author><name>Malte</name><uri>http://www.blogger.com/profile/02896910385129311939</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><thr:total>2</thr:total><feedburner:origLink>http://www.nonblocking.io/2011/02/taking-away-information.html</feedburner:origLink></entry><entry gd:etag="W/&quot;C0EMQ3c4cCp7ImA9Wx9WFUw.&quot;"><id>tag:blogger.com,1999:blog-5274051535398117366.post-7083353155560650527</id><published>2011-01-19T08:59:00.000-08:00</published><updated>2011-01-20T00:21:22.938-08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-01-20T00:21:22.938-08:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="WebSocket" /><title>Streaming ASCII Art Demo on html5rocks.com</title><content type="html">I recently thought about building a WebSocket demo for &lt;a href="http://www.html5rocks.com/"&gt;html5rocks.com &lt;/a&gt;and I was thinking about building something which could not easily be replicated with any of the work arounds such as long-polling, etc but would require a real streaming socket connection.&lt;div&gt;So I built a &lt;a href="http://studio.html5rocks.com/#Art"&gt;collaborative video streaming server and client for ASCII art videos&lt;/a&gt;.&lt;/div&gt;&lt;div&gt;The server is using node.js and it has two modes: it can either talk to a local QuickTime installation to stream the video or load a file into memory that contains all the text that makes up the frames of the video. For the production setup that runs on &lt;a href="https://no.de/"&gt;Joyent's no.de&lt;/a&gt; infrastructure the in-memory solution is used to avoid the dependency on QuickTime and to allow simulatenous access by multiple users.&lt;/div&gt;&lt;div&gt;The collaborative part comes into play with the scrub bar that scrubs the video for all users that are currently watching. This also means that the demo will not really be enjoyable with many concurrent watchers :)&lt;/div&gt;&lt;div&gt;I really love the simplicity of the client. It receives every frame as a single WebSocket messages and just puts that into a &amp;lt;pre&amp;gt; tag as soon as it is received. That's it.&lt;/div&gt;&lt;div&gt;2 Warnings:&lt;div&gt;&lt;ol&gt;&lt;li&gt;Most browsers recently disabled their WebSocket implementation, so it might not work for you.&lt;br /&gt;Updates:&lt;br /&gt;Chrome should work&lt;br /&gt;To view on Firefox 4, go to about:config and set network.websocket.override-security-block to true.&lt;br /&gt;For Opera:  opera:config#UserPrefs|EnableWebSockets&lt;/li&gt;&lt;li&gt;Lets see how much node.js can take :)&lt;/li&gt;&lt;/ol&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5274051535398117366-7083353155560650527?l=www.nonblocking.io' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/joose/~4/ApootkiDsbs" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.nonblocking.io/feeds/7083353155560650527/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=5274051535398117366&amp;postID=7083353155560650527" title="1 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/5274051535398117366/posts/default/7083353155560650527?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/5274051535398117366/posts/default/7083353155560650527?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/joose/~3/ApootkiDsbs/streaming-ascii-art-demo-on.html" title="Streaming ASCII Art Demo on html5rocks.com" /><author><name>Malte</name><uri>http://www.blogger.com/profile/02896910385129311939</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><thr:total>1</thr:total><feedburner:origLink>http://www.nonblocking.io/2011/01/streaming-ascii-art-demo-on.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CU8CSXc4eSp7ImA9Wx9WEE4.&quot;"><id>tag:blogger.com,1999:blog-5274051535398117366.post-8852569230187373941</id><published>2011-01-14T11:31:00.000-08:00</published><updated>2011-01-14T11:37:48.931-08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-01-14T11:37:48.931-08:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="promotejs" /><title>W3Fools</title><content type="html">&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://a2.twimg.com/profile_images/1210451533/6ee71d17eb50217780974fb5c8f48cbf.jpg"&gt;&lt;img style="float:left; margin:0 10px 10px 0;cursor:pointer; cursor:hand;width: 250px; height: 250px;" src="http://a2.twimg.com/profile_images/1210451533/6ee71d17eb50217780974fb5c8f48cbf.jpg" border="0" alt="" /&gt;&lt;/a&gt;&lt;br /&gt;Wrong education about web technologies has seriously hurt web development as a whole. W3Schools is the worst offender in this area – and because they are along so long they tend to dominate search results. The &lt;a href="http://promotejs.com/"&gt;PromoteJS&lt;/a&gt; initiative tries to fix this and now we have a new site in the ring. Help spread the word about &lt;a href="http://w3fools.com/"&gt;W3Fools&lt;/a&gt;, an intervention for W3Schools.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5274051535398117366-8852569230187373941?l=www.nonblocking.io' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/joose/~4/0snOry4ZBGo" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.nonblocking.io/feeds/8852569230187373941/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=5274051535398117366&amp;postID=8852569230187373941" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/5274051535398117366/posts/default/8852569230187373941?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/5274051535398117366/posts/default/8852569230187373941?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/joose/~3/0snOry4ZBGo/w3fools.html" title="W3Fools" /><author><name>Malte</name><uri>http://www.blogger.com/profile/02896910385129311939</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><thr:total>0</thr:total><feedburner:origLink>http://www.nonblocking.io/2011/01/w3fools.html</feedburner:origLink></entry><entry gd:etag="W/&quot;A04DSXs8fCp7ImA9Wx9REEQ.&quot;"><id>tag:blogger.com,1999:blog-5274051535398117366.post-4068038850161555941</id><published>2010-12-11T05:50:00.000-08:00</published><updated>2010-12-11T12:39:38.574-08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-12-11T12:39:38.574-08:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="speed" /><title>The Slowest JavaScript Application on Earth</title><content type="html">While writing an article about profiling browser-based JavaScript applications I thought it might be fun to have the worst possible example to make it better step-by-step.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Thus I created this &lt;a href="http://vaporjs.com/"&gt;VaporJS&lt;/a&gt; based &lt;a href="https://github.com/cramforce/The-Slowest-JavaScript-Application-on-Earth"&gt;Github repo&lt;/a&gt; which will accept only the worst offenders in JavaScript performance best practices. Pull requests that make it slower or add cool new bad performing features are very much appreciated.&lt;/div&gt;&lt;div&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="https://github.com/cramforce/The-Slowest-JavaScript-Application-on-Earth"&gt;Github Repo&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://cramforce.github.com/The-Slowest-JavaScript-Application-on-Earth/slow.html"&gt;Try the current application&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;Ideas&lt;/div&gt;&lt;div&gt;&lt;ul&gt;&lt;li&gt;Do some blocking Ajax calls.&lt;/li&gt;&lt;li&gt;I think we need jQuery selectors that won't make it through querySelectorAll.&lt;/li&gt;&lt;li&gt;Some legacy Prototype.js code to slow down Array operations can't be bad.&lt;/li&gt;&lt;li&gt;Add some CSS that will make rendering really hard (The * selector can help).&lt;/li&gt;&lt;li&gt;Hint: Writing to localStorage blocks the UI thread in Chrome and Firefox.&lt;/li&gt;&lt;li&gt;When you load ressources, make sure you always bust the cache so it doesn't get in your way.&lt;/li&gt;&lt;li&gt;A little flash movie with the right wmode can have devastating effects.&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;div&gt;If you are scared to contribute because future employers might find the code and not hire you, we provide a wikileaks-style-highly-anonymous way to send in patches: @-messages a link to &lt;a href="http://www.blogger.com/twitter.com/cramforce"&gt;@cramforce&lt;/a&gt;.&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;Important:&lt;/b&gt; Thanks for all the awesome contributions. Please make sure the changes are realistic (as in: some people would actually put them in because they don't know better) and add features.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5274051535398117366-4068038850161555941?l=www.nonblocking.io' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/joose/~4/shn804dB_Y4" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.nonblocking.io/feeds/4068038850161555941/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=5274051535398117366&amp;postID=4068038850161555941" title="4 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/5274051535398117366/posts/default/4068038850161555941?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/5274051535398117366/posts/default/4068038850161555941?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/joose/~3/shn804dB_Y4/slowest-javascript-application-on-earth.html" title="The Slowest JavaScript Application on Earth" /><author><name>Malte</name><uri>http://www.blogger.com/profile/02896910385129311939</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><thr:total>4</thr:total><feedburner:origLink>http://www.nonblocking.io/2010/12/slowest-javascript-application-on-earth.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DkUHQnszcSp7ImA9Wx5bEE4.&quot;"><id>tag:blogger.com,1999:blog-5274051535398117366.post-5973849546191110022</id><published>2010-10-25T11:53:00.000-07:00</published><updated>2010-10-25T12:17:13.589-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-10-25T12:17:13.589-07:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="NoFlash" /><title>Eliminating the Need for Flash</title><content type="html">HTML5 and related technologies are getting more powerful every day and for me as a web developer it feels good that it becomes possible to build event more things that would have been the domain of "Flash Developers" a couple years ago (if not event cooler stuff). And, actually, I was using Flash myself to make things work back in those days.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Still there are two things that would still make me pick Flash as the technology of choice and I don't like that, so we should fix it!&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;1. Fullscreen&lt;/b&gt;&lt;/div&gt;&lt;div&gt;Fullscreen is awesome. &lt;a href="http://www.apple.com/macosx/lion/#apps"&gt;Even Steve Jobs thinks so.&lt;/a&gt; I personally think we should do something really simple to fix this: &lt;code&gt;window.open('/my/path', '_fullscreen')&lt;/code&gt;&lt;/div&gt;&lt;div&gt;&lt;code&gt;window.open&lt;/code&gt; already has all the security restrictions in place that we expect from a modern browser with a built in popup blocker. There might need to be extra restrictions with respect to keyboard events (so that pressing ESC always takes you back) but even not having keyboard events at all would be fine for a start.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;2. Ease of Deployment&lt;/b&gt;&lt;/div&gt;&lt;div&gt;The deployment story for Flash is awesome. Send someone a SWF file, he can play it. Yes, for more advanced use cases the file might reference a couple XML files and other asset and might need some parameters to get going but overall that's it.&lt;/div&gt;&lt;div&gt;If you haven't worked in big multi-Agency projects (which would be a good thing :) you might not appreciate this point as important but it really makes sense to have an exchange format for content which one can just "drop" into what is already there.&lt;/div&gt;&lt;div&gt;I don't personally have the perfect solution for SWF like deployment of HTML content but at least I have an idea: Putting HTML files and related assets in a ZIP file and then delivering the ZIP file to the browser which would be smart enough to unzip the file, stick it into an iframe and display everything. I played around with doing all of that with just a little bit of JavaScript. It kinda worked but is nowhere near a fully transparent abstraction.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5274051535398117366-5973849546191110022?l=www.nonblocking.io' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/joose/~4/llReiBIJjdg" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.nonblocking.io/feeds/5973849546191110022/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=5274051535398117366&amp;postID=5973849546191110022" title="2 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/5274051535398117366/posts/default/5973849546191110022?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/5274051535398117366/posts/default/5973849546191110022?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/joose/~3/llReiBIJjdg/eliminating-need-for-flash.html" title="Eliminating the Need for Flash" /><author><name>Malte</name><uri>http://www.blogger.com/profile/02896910385129311939</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><thr:total>2</thr:total><feedburner:origLink>http://www.nonblocking.io/2010/10/eliminating-need-for-flash.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CkYCQXYyeCp7ImA9Wx5UEk4.&quot;"><id>tag:blogger.com,1999:blog-5274051535398117366.post-8608772009337236359</id><published>2010-10-12T12:17:00.001-07:00</published><updated>2010-10-16T04:56:00.890-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-10-16T04:56:00.890-07:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="streamie" /><title>Access to WebSockets on a Sub-Domain though a Proxy iFrame</title><content type="html">One of the current design mistakes of &lt;a href="http://streamie.org/"&gt;Streamie&lt;/a&gt; is that the WebSocket server is hosted on the same domain as the main web server. At first this seems like a good idea and the HTTP-upgrade-style implementation of web sockets seem to be made for this approach. Here is why this is still a bad idea:&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Scaling web servers is a different task from scaling web socket servers (e.g. because HTTP is inherently stateless while web sockets are inherently stateful and many other reasons). Thus my recommendation is to always host your web socket service on a different domain to enable hosting it on a different infrastructure.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;Problem:&lt;/b&gt;&lt;/div&gt;&lt;div&gt;While native web sockets make cross domain access easy this is no longer true when you start using all the work arounds (e.g. by using &lt;a href="http://socket.io/"&gt;socket.io&lt;/a&gt;) like XHR long-polling, multi-part XHR and html-files.&lt;/div&gt;&lt;div&gt;It is important to note that even for clients that do support web sockets often you cannot use them because popular web proxies such as squid do not support web sockets.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;Solution:&lt;/b&gt;&lt;/div&gt;&lt;div&gt;Access your web socket service through a proxy iframe. This invisible iframe is hosted on the domain of your web socket server and is embedded in your main page. Then access the iframe though the use of document.domain or postMessage. A similar technique (albeit not actually for accessing web sockets is being implemented by #newtwitter. See the source and look for an iframe to api.twitter.com)&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5274051535398117366-8608772009337236359?l=www.nonblocking.io' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/joose/~4/xKwDAEgi4nE" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.nonblocking.io/feeds/8608772009337236359/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=5274051535398117366&amp;postID=8608772009337236359" title="6 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/5274051535398117366/posts/default/8608772009337236359?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/5274051535398117366/posts/default/8608772009337236359?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/joose/~3/xKwDAEgi4nE/access-to-websockets-on-sub-domain.html" title="Access to WebSockets on a Sub-Domain though a Proxy iFrame" /><author><name>Malte Ubl</name><uri>https://profiles.google.com/116910304844117268718</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-tQJAx2-8rqE/AAAAAAAAAAI/AAAAAAAAEhc/T74iWhwXXf0/s512-c/photo.jpg" /></author><thr:total>6</thr:total><feedburner:origLink>http://www.nonblocking.io/2010/10/access-to-websockets-on-sub-domain.html</feedburner:origLink></entry><entry gd:etag="W/&quot;AkYHQXo9fyp7ImA9Wx5VFUw.&quot;"><id>tag:blogger.com,1999:blog-5274051535398117366.post-4872714526227890931</id><published>2010-10-07T13:22:00.000-07:00</published><updated>2010-10-07T23:08:50.467-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-10-07T23:08:50.467-07:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="streamie" /><title>Streamie now runs on Twitter Site Streams</title><content type="html">The production environment of &lt;a href="http://streamie.org/"&gt;Streamie&lt;/a&gt; was just switched over to use the &lt;a href="http://dev.twitter.com/pages/site_streams"&gt;Twitter Site Stream AP&lt;/a&gt;I for backend connectivity. (The code for this currently lives in a &lt;a href="http://github.com/cramforce/streamie/tree/site"&gt;branch&lt;/a&gt;).&lt;div&gt;With site streams the server only opens a single connection for every 1000 users. Twitter then sends the events of all users over this single connection. The node.js app uses a simple single process &lt;a href="http://github.com/cramforce/streamie/blob/site/lib/message-queue.js"&gt;message queue&lt;/a&gt; to distribute the messages to the actual clients.&lt;/div&gt;&lt;div&gt;If you are running your own streamie server there should be no changes. Site streams are only available to privileged users. If you do not have access, streamie will transparently fall back to user streams. However, the site branch also introduces the usage of a JSON based config file for the server (instead of the ugly command line parameters). &lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5274051535398117366-4872714526227890931?l=www.nonblocking.io' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/joose/~4/8vo84_ps6ZU" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.nonblocking.io/feeds/4872714526227890931/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=5274051535398117366&amp;postID=4872714526227890931" title="1 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/5274051535398117366/posts/default/4872714526227890931?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/5274051535398117366/posts/default/4872714526227890931?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/joose/~3/8vo84_ps6ZU/streamie-now-runs-twitter-site-streams.html" title="Streamie now runs on Twitter Site Streams" /><author><name>Malte Ubl</name><uri>https://profiles.google.com/116910304844117268718</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-tQJAx2-8rqE/AAAAAAAAAAI/AAAAAAAAEhc/T74iWhwXXf0/s512-c/photo.jpg" /></author><thr:total>1</thr:total><feedburner:origLink>http://www.nonblocking.io/2010/10/streamie-now-runs-twitter-site-streams.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DkIFQHo9eip7ImA9Wx5VEU4.&quot;"><id>tag:blogger.com,1999:blog-5274051535398117366.post-604488876428482421</id><published>2010-10-03T12:34:00.001-07:00</published><updated>2010-10-03T12:35:11.462-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-10-03T12:35:11.462-07:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Joose" /><title>Joose Blog</title><content type="html">I haven't been blogging on Joose here for a while. Check out &lt;a href="http://joose.it/blog/"&gt;the official Joose blog&lt;/a&gt; for that.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5274051535398117366-604488876428482421?l=www.nonblocking.io' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/joose/~4/edNDFpPvGBE" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.nonblocking.io/feeds/604488876428482421/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=5274051535398117366&amp;postID=604488876428482421" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/5274051535398117366/posts/default/604488876428482421?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/5274051535398117366/posts/default/604488876428482421?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/joose/~3/edNDFpPvGBE/joose-blog.html" title="Joose Blog" /><author><name>Malte Ubl</name><uri>https://profiles.google.com/116910304844117268718</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-tQJAx2-8rqE/AAAAAAAAAAI/AAAAAAAAEhc/T74iWhwXXf0/s512-c/photo.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://www.nonblocking.io/2010/10/joose-blog.html</feedburner:origLink></entry><entry gd:etag="W/&quot;Ck8EQ3g4eSp7ImA9Wx5XE08.&quot;"><id>tag:blogger.com,1999:blog-5274051535398117366.post-3995369320814583346</id><published>2010-09-12T11:59:00.000-07:00</published><updated>2010-09-12T12:46:42.631-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-09-12T12:46:42.631-07:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="streamie" /><title>-O fun || Designing for Hackability</title><content type="html">My little side project &lt;a href="http://streamie.org/"&gt;Streamie&lt;/a&gt; was designed to be hackable, as in easy for somebody else to make changes on, from the ground up.&lt;div&gt;Besides the ability to run Streamie against &lt;a href="http://www.nonblocking.io/2010/08/future-is-here-i-just-forked-running.html"&gt;your own github repository&lt;/a&gt; without running your own backend server, there are several other design decisions in the code that are supposed to make the source as approachable as possible.&lt;/div&gt;&lt;div&gt;I'm joining Google next month, so the obvious choice for a client framework would have been to use &lt;a href="http://code.google.com/closure/library/"&gt;closure&lt;/a&gt;. I decided to go with jQuery instead because everybody already knows that one already.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;app.js&lt;/b&gt;&lt;/div&gt;&lt;div&gt;When you want to start digging into Streamie, start by reading &lt;a href="http://github.com/cramforce/streamie/blob/kosta/public/lib/stream/app.js"&gt;app.js&lt;/a&gt;. In this file you see two big static arrays:&lt;/div&gt;&lt;div&gt;&lt;a href="http://github.com/cramforce/streamie/blob/538d31c38bc08535987d4cf45b610c8187df3469/public/lib/stream/app.js#L22"&gt;streamPlugins&lt;/a&gt;: This is a list of "plugins" which are run on every single tweet and which can transform the tweet in any way. Currently all stream plugins are defined in &lt;a href="http://github.com/cramforce/streamie/blob/538d31c38bc08535987d4cf45b610c8187df3469/public/lib/stream/streamplugins.js"&gt;streamplugins.js&lt;/a&gt;. If you want to extend Streamie's tweet view, like e.g. &lt;a href="http://jeromeetienne.streamie.org/"&gt;translating all tweets&lt;/a&gt;, this is where you want to add your own plugin.&lt;/div&gt;&lt;div&gt;&lt;a href="http://github.com/cramforce/streamie/blob/538d31c38bc08535987d4cf45b610c8187df3469/public/lib/stream/app.js#L44"&gt;initPlugins&lt;/a&gt;: This is a list of plugins which are run as soon as the connection to the backend system has been established and Streamie is basically "running".&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;The REST API&lt;/b&gt;&lt;/div&gt;&lt;div&gt;Twitter exposes an extensive &lt;a href="http://apiwiki.twitter.com/Twitter-API-Documentation"&gt;REST API&lt;/a&gt; to interact with it's service. Using these APIs requires doing oAuth. Doing anything with oAuth sucks. The good thing is that Streamie provides a proxy for the REST API that does all the oAuth stuff for you. This means you can use the API just as it is documented. E.G. this is how you &lt;a href="http://github.com/cramforce/streamie/blob/538d31c38bc08535987d4cf45b610c8187df3469/public/lib/stream/status.js#L192"&gt;favorite something&lt;/a&gt; in Streamie. Because the main channel from Twitter to Streamie is the &lt;a href="http://dev.twitter.com/pages/user_streams"&gt;user stream API,&lt;/a&gt; you don't really have to worry about rate limits.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;b&gt;Tweets, Direct Messages, Retweets&lt;/b&gt;&lt;/div&gt;&lt;div&gt;Coming from the Twitter API Tweets, Direct Messages, Retweets look quite different. Streamie normalizes them to be all specialized Tweets. This makes working with them quite easy. Streamie is using the "Stream" metaphor extensively. That is why your direct messages appear in the main stream. The buttons in the navigation only activate CSS classes which filter out certain tweets.&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;Start up time&lt;/b&gt;&lt;/div&gt;&lt;div&gt;Streamie's startup time can be quite long (as in a couple seconds). This is mostly due to the time it takes to load initial data from twitter. (&lt;a href="http://github.com/cramforce/streamie/blob/538d31c38bc08535987d4cf45b610c8187df3469/public/lib/stream/initplugins.js#L243"&gt;See here&lt;/a&gt; if you are curious how that works). In development you can append ?cache to the URL and these API calls will use sessionStorage caching which makes loading almost instant.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;Learnings&lt;/b&gt;&lt;/div&gt;&lt;div&gt;Overall Streamie's software design is quite simplistic. Right now it feels like there might be a need to abstract some stuff a little more. There will likely be classes for "status inputs", custom events and maybe even tweets :)&lt;/div&gt;&lt;div&gt;Do you have questions or suggestions about how Streamie works or is supposed to work?&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5274051535398117366-3995369320814583346?l=www.nonblocking.io' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/joose/~4/mHT3GEqQ3BU" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.nonblocking.io/feeds/3995369320814583346/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=5274051535398117366&amp;postID=3995369320814583346" title="8 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/5274051535398117366/posts/default/3995369320814583346?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/5274051535398117366/posts/default/3995369320814583346?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/joose/~3/mHT3GEqQ3BU/o-fun-designing-for-hackability.html" title="-O fun || Designing for Hackability" /><author><name>Malte Ubl</name><uri>https://profiles.google.com/116910304844117268718</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-tQJAx2-8rqE/AAAAAAAAAAI/AAAAAAAAEhc/T74iWhwXXf0/s512-c/photo.jpg" /></author><thr:total>8</thr:total><feedburner:origLink>http://www.nonblocking.io/2010/09/o-fun-designing-for-hackability.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DU8HQ3s-fip7ImA9Wx5XE0w.&quot;"><id>tag:blogger.com,1999:blog-5274051535398117366.post-4078606311301278526</id><published>2010-09-12T11:41:00.000-07:00</published><updated>2010-09-12T11:57:12.556-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-09-12T11:57:12.556-07:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="streamie" /><title>Another week in Streamie</title><content type="html">Just a quick recap what we did in &lt;a href="http://streamie.org/"&gt;Streamie&lt;/a&gt; this week:&lt;div&gt;&lt;ul&gt;&lt;li&gt;Your own tweets now show up with opposite orientation (thanks to &lt;a href="http://twitter.com/helgri"&gt;@helgri&lt;/a&gt;)&lt;/li&gt;&lt;li&gt;You can now upload images to be included in statuses&lt;/li&gt;&lt;li&gt;We now display your direct messages and you can write them using the standard "d username" twitter syntax&lt;/li&gt;&lt;li&gt;We use fancy utf8 characters as icons. This didn't really work on Windows. &lt;a href="http://twitter.com/helgri"&gt;@helgri&lt;/a&gt; added images as a fallback.&lt;/li&gt;&lt;li&gt;&lt;a href="http://twitter.com/joemccann"&gt;@joemccann&lt;/a&gt; made the "run your own streamie" experience a little smoother&lt;/li&gt;&lt;li&gt;If a proxy gets in the way of web sockets we now fall back to more conservative transport methods automatically. If Streamie didn't work for you before, maybe it does now.&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;Last but not least, &lt;a href="http://twitter.com/alicelikesbob"&gt;@AliceLikesBob&lt;/a&gt; provided a patch to integrate chrome notifications into Streamie. This will be integrated shortly.&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Next to the direct contributions, people continue to have fun &lt;a href="www.nonblocking.io/2010/08/future-is-here-i-just-forked-running.html"&gt;with their own versions&lt;/a&gt; of streamie. There are now 27 forks on &lt;a href="http://github.com/cramforce/streamie"&gt;Github&lt;/a&gt;.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5274051535398117366-4078606311301278526?l=www.nonblocking.io' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/joose/~4/eVQ6cWm0WSw" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.nonblocking.io/feeds/4078606311301278526/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=5274051535398117366&amp;postID=4078606311301278526" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/5274051535398117366/posts/default/4078606311301278526?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/5274051535398117366/posts/default/4078606311301278526?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/joose/~3/eVQ6cWm0WSw/another-week-in-streamie.html" title="Another week in Streamie" /><author><name>Malte Ubl</name><uri>https://profiles.google.com/116910304844117268718</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-tQJAx2-8rqE/AAAAAAAAAAI/AAAAAAAAEhc/T74iWhwXXf0/s512-c/photo.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://www.nonblocking.io/2010/09/another-week-in-streamie.html</feedburner:origLink></entry><entry gd:etag="W/&quot;C0UHRXc_eyp7ImA9Wx5QGE0.&quot;"><id>tag:blogger.com,1999:blog-5274051535398117366.post-1089761691396541599</id><published>2010-09-05T13:46:00.000-07:00</published><updated>2010-09-06T12:27:14.943-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-09-06T12:27:14.943-07:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="streamie" /><title>Streaming along</title><content type="html">In it's second week of development &lt;a href="http://streamie.org/"&gt;Streamie&lt;/a&gt; is starting to gain quite a few convenience features beyong the simple one like "Yes, we can actually post a status".&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;The new features are&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;ul&gt;&lt;li&gt;Previews of image links&lt;/li&gt;&lt;li&gt;Click on "in reply to" shows ALL tweets of that conversation. &lt;b&gt;This is awesome. Try it!&lt;/b&gt;&lt;/li&gt;&lt;li&gt;Auto completion of twitter names&lt;/li&gt;&lt;li&gt;A page for new users that describes what streamie is&lt;/li&gt;&lt;li&gt;Settings&lt;/li&gt;&lt;li&gt;... and many, many small improvements&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;&lt;b&gt;Hackability++&lt;/b&gt;&lt;/div&gt;&lt;div&gt;Streamie is aiming to be the most customizable and developer friendly Twitter client in the world (&lt;a href="http://www.nonblocking.io/2010/08/future-is-here-i-just-forked-running.html"&gt;See how you can make your own version&lt;/a&gt;). It is starting to be an active open source project. Streamie now includes contributions from these awesome people: &lt;a href="http://twitter.com/mathias"&gt;@helgri&lt;/a&gt;, &lt;a href="http://www.blogger.com/antimatter15"&gt;@antimatter15&lt;/a&gt;, &lt;a href="http://twitter.com/mathias"&gt;@mathias&lt;/a&gt;. Overall there are now &lt;a href="http://github.com/cramforce/streamie/network/members"&gt;17 forks&lt;/a&gt; on github.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5274051535398117366-1089761691396541599?l=www.nonblocking.io' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/joose/~4/q_jEO02VcgU" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.nonblocking.io/feeds/1089761691396541599/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=5274051535398117366&amp;postID=1089761691396541599" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/5274051535398117366/posts/default/1089761691396541599?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/5274051535398117366/posts/default/1089761691396541599?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/joose/~3/q_jEO02VcgU/streaming-along.html" title="Streaming along" /><author><name>Malte Ubl</name><uri>https://profiles.google.com/116910304844117268718</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-tQJAx2-8rqE/AAAAAAAAAAI/AAAAAAAAEhc/T74iWhwXXf0/s512-c/photo.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://www.nonblocking.io/2010/09/streaming-along.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DE4HSXs6fyp7ImA9Wx5QE0o.&quot;"><id>tag:blogger.com,1999:blog-5274051535398117366.post-7340884562321006646</id><published>2010-09-01T14:11:00.000-07:00</published><updated>2010-09-01T14:35:38.517-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-09-01T14:35:38.517-07:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="streamie" /><title>The first week of Streamie.org</title><content type="html">&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_31o4cLQyFS4/TH7FqAevNQI/AAAAAAAAAdg/DsxXih4KayI/s1600/streamie_screen.png"&gt;&lt;img style="float:right; margin:0 0 10px 10px;cursor:pointer; cursor:hand;width: 400px; height: 385px;" src="http://4.bp.blogspot.com/_31o4cLQyFS4/TH7FqAevNQI/AAAAAAAAAdg/DsxXih4KayI/s400/streamie_screen.png" border="0" alt="" id="BLOGGER_PHOTO_ID_5512060319422166274" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;a href="http://streamie.org/"&gt;Streamie.org&lt;/a&gt; is now one week old. It is already quite usable as a Twitter client and I personally dogfood it 100%. People also seem to &lt;a href="http://search.twitter.com/search?q=streamie+awesome"&gt;like it&lt;/a&gt;.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;People are also starting to make their &lt;a href="http://github.com/cramforce/streamie/network"&gt;own versions&lt;/a&gt; of the client &lt;a href="http://www.nonblocking.io/2010/08/future-is-here-i-just-forked-running.html"&gt;(Learn how to do it here)&lt;/a&gt;. E.g., we will soon get a version of Streamie that actually works well on Windows. The current version uses UTF8 characters which are not included in Windows-Default-Arial.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;So what's next&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;ul&gt;&lt;li&gt;Maybe a nice homepage&lt;/li&gt;&lt;li&gt;Nicer oAuth dialogs&lt;/li&gt;&lt;li&gt;Maybe a command line&lt;/li&gt;&lt;li&gt;jQuery style filtering of tweets e.g. $(".status.retweet contains('@phiggins')").hide()&lt;/li&gt;&lt;li&gt;Options&lt;/li&gt;&lt;li&gt;Direct Messages&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;div&gt;If you want to do fun things with &lt;a href="http://streamie.org/"&gt;Streamie&lt;/a&gt;, I suggest you start in &lt;a href="http://github.com/cramforce/streamie/blob/master/public/lib/stream/app.js"&gt;app.js&lt;/a&gt; and start making your own streamPlugin. E.G. you could color all tweets red that include the word Dojo.&lt;/div&gt;&lt;div&gt;Because Streamie was supposed to be hackable I did actually write source level docs. Let me know what extra info you need.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5274051535398117366-7340884562321006646?l=www.nonblocking.io' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/joose/~4/lE1OcCaLAIE" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.nonblocking.io/feeds/7340884562321006646/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=5274051535398117366&amp;postID=7340884562321006646" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/5274051535398117366/posts/default/7340884562321006646?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/5274051535398117366/posts/default/7340884562321006646?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/joose/~3/lE1OcCaLAIE/first-week-of-streamieorg.html" title="The first week of Streamie.org" /><author><name>Malte Ubl</name><uri>https://profiles.google.com/116910304844117268718</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-tQJAx2-8rqE/AAAAAAAAAAI/AAAAAAAAEhc/T74iWhwXXf0/s512-c/photo.jpg" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://4.bp.blogspot.com/_31o4cLQyFS4/TH7FqAevNQI/AAAAAAAAAdg/DsxXih4KayI/s72-c/streamie_screen.png" height="72" width="72" /><thr:total>0</thr:total><feedburner:origLink>http://www.nonblocking.io/2010/09/first-week-of-streamieorg.html</feedburner:origLink></entry></feed>

