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

  <title><![CDATA[Acko.net]]></title>
  <link href="http://acko.net/atom.xml" rel="self"/>
  <link href="http://acko.net/"/>
  <updated>2016-05-12T20:06:53+02:00</updated>
  <id>http://acko.net/</id>
  <author>
    <name><![CDATA[Steven Wittens]]></name>
    
  </author>

  
  <entry>
    <title type="html"><![CDATA[Occupy WWW Street (NL)]]></title>
    <link href="http://acko.net/blog/occupy-www-street-nl/"/>
    <updated>2015-11-30T00:00:00+01:00</updated>
    <id>http://acko.net/blog/occupy-www-street-nl</id>
    <content type="html"><![CDATA[<div class="g8 i2 first serif"><div class="pad">

<h2 class="sub">Internetactivisme en Media in het Socialerechtvaardigheidstijdperk</h2>

</div></div>

<div class="c"></div>

<div class="g8 i2 serif"><div>

<p class="outlined lang mt1 mb1 tc"><a href="/blog/occupy-www-street-en/">English</a> - <a href="/blog/occupy-www-street-fr/">Français</a>  - <a href="/blog/occupy-www-street-nl/"><strong>Nederlands</strong></a></p>

</div></div>

<div class="g12 serif"><div class="pad">

<blockquote class="mt1 mb2 lift outlined">
  <em class="big">"Ik heb geen toegang tot ongewone onderzoeksmethodes en de speciale kennis die ik heb van taal is niet van toepassing op sociale en politieke kwesties. […] Om ideologie te analyseren […] heb je algemeen gezien enkel een beetje open wereldsopvatting, gewone intelligentie en gezond skepticisme nodig." -&nbsp;NC</em>
</blockquote>

</div></div>

<div class="g10 i1 mt0 serif"><div class="pad">

<p class="outlined">'t Is weer tijd voor mijn <a href="/blog/storms-and-teacups/">serieus lettertype</a>. Helaas is het moeilijk om over courante zaken te spreken zonder meteen een kilo context erbij te kappen. In plaats daarvan ga ik weer in onze recente geschiedenis duiken.</p>

<p class="outlined"><em>Denk er aan: de boodschapper neerschieten is onbeleefd, en ketterij is middeleeuws.</em></p>


<h2 class="mt5">The Only Working Class Man In The Village</h2>

<p>Het is september 2011, Amerikaans links staat in vlam. De boodschap is kordaat en mikt op grote doelen: het 99% publiek wilt dat die 1% gierige vrekken er nu eens mee ophouden met hun giga-ondernemingen als knuppel te gebruiken. Zuccotti Park in New York vult zich met kamperende activisten, daar om hun boodschap mondig te verspreiden.</p>

</div></div>

<div class="c m1"></div>

<div class="wide mt2">
  <a target="_blank" href="https://www.youtube.com/watch?v=6yrT-0Xbrn4"><img src="https://acko.net/files/occupy-www-street/lagreca.jpg" alt="Jesse LaGreca schools Fox News"></a>
</div>

<div class="g10 i1 mt1 serif"><div class="pad">

<p>Zo ook Jesse LaGreca. Met zijn typisch Amerikaanse "Joe Average" look verrast hij velen, vooral de Fox News verslaggever die hem aanspreekt. In het <a target="_blank" href="https://www.youtube.com/watch?v=6yrT-0Xbrn4">impromptu interview</a>, nooit op Fox uitgezonden, deelt LaGreca forse kletsen uit, met vonken erbij. De suggestieve vragen worden meteen aangeduid, en hij merkt op dat als er nu iets is waarover het nieuws—<em>"de propagandamachine in de media"</em>—niet wilt spreken, dan is het wel zichzelf. Dat het misschien tijd is voor wat participatieve democratie die <em>"niet met nieuwscamera's gefinancierd wordt."</em></p>

<p>Een journalist duikt op om de wereld te verklaren maar ontdekt dat dit verhaal deels over hemzelf gaat. Hij ontkent het meteen: <em>"We zijn hier om je een kans te geven […] om eender wat je wilt uit te zenden, met eerlijke berichtgeving. […] Dus is er een uitzondering in dit geval, want je zou je boodschap niet zonder ons kunnen verspreiden."</em></p>


<p>Hij kaatst terug: <em>"Zeker. Neem bijvoorbeeld toen Glenn Beck […] de president beschreef als iemand die blanke mensen en blanke cultuur haat. Dat was een laag moment in de Amerikaanse geschiedenis en jullie hebben daar eigelijk wel een grote rol in gespeeld. Dus 't is tof te zien dat jullie mening aan't veranderen is en dat je wat meer aandacht aan de [rest van het land] besteedt, in plaats van een extreem rechtse marge die de middenstand liever compleet zou vernielen. […] De mensen zouden liever wat economische rechtvaardigheid of sociale rechtvaardigheid zien, je weet wel, Jesusdingen, zoals de armen voeden of voor de zieken zorgen, maar ze kunnen maar niet vatten dat een systeem van winstjagende gezondheidsdiensten niet werkt. […] Ik vind het echt grappig dat de mensen met de grondwet zitten wapperen terwijl ze naar homo-soldaten schreeuwen. […] Als we willen dat de president meer doet, laat ons op een functioneel niveau spreken in plaats van om zijn geboortecertificaat te vragen, […]"</em></p>
 
<p><em>Shots fired, critical hit, no chance of recovery.</em> 't Is begrijpelijk dat de clip "vergeten" werd, en begrijpelijk dat het tot groot faam <a target="_blank" href="https://archive.is/aUFoA" title="http://observer.com/2011/10/exclusive-occupy-wall-street-activist-slams-fox-news-anchor-in-un-aired-interview-video/">zou uitlekken</a> als zijnde <em>too extreme for Fox news</em>. Voorspelbare koppen vroegen ons of hij de <em>Slimste Mens op Wall Street</em> was, en legden uit hoe <em>Jesse LaGreca de Vooroordelen van de Media Over Occupy Wall Street Blijft Vernielen</em>. Allemaal verslaggevers die gretig wilden tonen hoe graag <em>zij</em> wel niet iedereen een stem wilden geven.</p>

<p>LaGreca raakte hier zelf aan <a target="_blank" href="https://archive.is/tEjrc#selection-495.650-495.893" title="http://observer.com/2011/10/jesse-lagreca-on-abcs-this-week/">op TV</a>: <em>"De realiteit is dat ik de enige mens van de arbeidersklasse ben die je op het journaal op zondag gaat zien… of politiek nieuws… misschien ooit. En ik denk dat dat zeer indicatief is voor de mislukkingen van onze media om over de belangrijkste zaken bericht te geven…"</em> De gastheer onderbreekt meteen: <em>"We doen ons best toch, Jesse."</em></p>

<p>Komt de vraag echter of er een politieke eis is die Occupy zou kunnen samenvatten, iets om <em>"het over te doen gaan,"</em> neemt hij een vreemde bocht:</p>

<p><em>"Ik denk dat de hele beweging over economische rechtvaardigheid gaat. Ik bedoel, voor mij—en ik spreek niet voor Occupy Wall Street, ik geef enkel mijn mening—ik denk dat het een kwestie is van economische rechten en een kwestie van sociale rechten, en sociale rechtvaardigheid. En voor de mensen die het kwalijk nemen dat het woord “sociale” voor “rechtvaardigheid” staat, ik nodig u uit om de grondwet te herlezen."</em></p>

<p>Ok. Ik heb effe het <a target="_blank" href="https://archive.is/fQr94" title="https://melissafong.wordpress.com/2014/04/04/why-suey_park-wont-enact-the-labour-women-of-colour-dont-owe-you-an-education/">laborieuze werk volbracht om mijzelf te onderwijzen</a>. De grondwet van de Verenigde Staten verwijst nergens naar <em>sociale</em> zaken als het op rechtvaardigheid aankomt. Het is alsof de schrijvers dachten dat het <em>"oprichten van rechtvaardigheid"</em> genoeg was. Burgers worden niet ingedeeld volgens privilege, macht wordt niet bepaald volgens benadeling en onderdrukking, en het maakt geen allusie naar <em>punching up</em>. Dat is hedendaagse sociale rechtvaardigheid, niet? Of bedoelde hij die <em>"Jesusdingen"</em>? Ik ben geen grondwettelijk advocaat, corrigeer mij gerust.</p>

<p>Een beetje genanter, als het op het inspiratie van andere protestbewegingen komt, in Griekenland en Europa en elders, beweert LaGreca dat <em>"we eigelijk niemand echt volgden."</em></p>

</div></div>

<aside class="g5 mt1"><div class="pad">
  <a target="_blank" href="http://www.webcitation.org/63DZ1nIDl" title="http://www.adbusters.org/blogs/adbusters-blog/occupywallstreet.html"><img src="https://acko.net/files/occupy-www-street/adbusters-occupy.jpg" class="fit" /></a>
</div></aside>

<div class="g7 serif"><div class="pad">

<p>Occupy is misschien wel spontaan van bestaande groeperingen gegroeid, maar het startschot was een vroege <a target="_blank" href="https://archive.is/55OJB" title="https://www.adbusters.org/blogs/adbusters-blog/million-man-march-wall-street.html">blogpost</a> en een <a target="_blank" href="http://www.webcitation.org/63DZ1nIDl" title="http://www.adbusters.org/blogs/adbusters-blog/occupywallstreet.html">nieuwsblast naar Adbusters' 90 000 abbonnees</a> van <em>"jullie verlossers, rebellen en radicalen,"</em> die beweerde dat er een <em>"wereldwijde shift in revolutionaire tactieken is,"</em> en vroeg of ze klaar stonden voor hun eigen <em>"Tahrirmoment."</em> De 267 reacties hebben dit bericht met enthousiasme en inzet ontvangen.</p>

<p>LaGreca was ook geen onbekende, hij was al drie jaar lang bijdrager bij Daily Kos. De leden van de site <a target="_blank" href="https://archive.is/6isVY" title="http://www.dailykos.com/story/2011/10/05/1023035/--UPDATE-Jesse-LaGreca-taking-on-Hannity-NY-Observer-MoT-Smartest-Man-on-Wall-Street">juichten hem toe</a> dat hij <em>"niet in het mediastereotype van die stomme rechteloze ne0-hippies viel"</em> en verspreidden het goede woord terwijl ze op het spektakel toekeken: <em>"Ik vermoed dat Hannity schrik heeft. Zoals het moet!"</em></p>

</div></div>

<div class="g10 i1 mt1 serif"><div class="pad">

<p>Dit is geen aanklacht aan het hof van Mr. LaGreca, hij heeft veel zinvols gezegd en met verve. Maar we zijn nu vier jaar later. Ondanks de gulle zendtijd die zijn nieuwe <a target="_blank" href="http://knowyourmeme.com/memes/streisand-effect">fame-by-Streisand</a> uitkeerde, en de belofte van verslaggevers om het beter te doen, is de situatie niet verbeterd, niet bij Fox News en niet bij de andere pers die zichzelf definiëert via oppositie.</p>

<p>Als er nu iets is waar de media dol op is, dan is het wel de uitzondering die de regel bevestigt: je kan het publiek niet galvanizeren zonder toegang tot bestaande netwerken en platformen. Als je het opneemt tegen de media, word je <a target="_blank" href="https://archive.is/IyrOb" title="http://www.tv.com/shows/black-mirror/community/post/black-mirror-15-million-merits-review-138498157166/">omgetoverd tot vermaaksproduct</a>, en de massa zal het geniaal vinden. Jij ook misschien.</p>

<h2 class="mt3">Activists Are Dead</h2>

<p>Wat is er gebeurd? In juni 2012 verklaart gemeenschappelijk forum en vzw OccupyWallSt.org dat <a target="_blank" href="https://archive.is/KhRmS" title="http://occupywallst.org/article/reports-occupys-death-have-been-greatly-exaggerate/">Berichten over de Dood van Occupy Mateloos Overdreven zijn</a>.</p>

<p class="tc"><a class="block" target="_blank" href="https://archive.is/KhRmS" title="http://occupywallst.org/article/reports-occupys-death-have-been-greatly-exaggerate/"><img src="https://acko.net/files/occupy-www-street/occupy-logo.png" alt="OccupyWallSt.org logo" class="natural" /></a></p>

<blockquote>

<p><em>"Sinds de buitenzetting van de manifestanten op Freedom Plaza vorige november, is het een media-cliché geworden om over de "Dood van Occupy" bericht te geven. Artikels zijn over heel het web opgesprongen die vrolijk over de mislukte tweede wind van Occupy spreken, deze tamme "Amerikaanse Lente," en de algemene staking in mei die het systeem niet bepaald platlegde.</em></p>
  
<p><em>Het mag geen verrassing zijn dat de algemene pers graag vertelt dat Occupy vermoedelijk gesneuveld is. Zelfs als we negeren dat media onder zakelijk bestuur een sterke wens heeft om sociale bewegingen als Occupy nooit te zien slagen, heeft de media als algemene regel voor ieder verslag een dramatische vertelling nodig. Voor hen moet ieder verhaal een boeiende boog hebben met een begin, een midden en een einde."</em></p>

</blockquote>

</div></div>

<div class="g12 mt0 serif"><div class="pad">

<p class="tc mt0 mb0"><a target="_blank" href="https://www.reddit.com/comments/mkuww"><img src="https://acko.net/files/occupy-www-street/casually-pepperspray.jpg" alt="Casually pepper spraying cop" class="fit" /></a></p>

</div></div>

<div class="g10 i1 mt0 serif"><div class="pad">

<blockquote>

<p><em>"Volgens de media […] weerklonk de oproep bij velen, [maar] kreeg de beweging haar dramatische impuls pas toen de politie op foto stond terwijl ze geweldloze manifestanten aanvielen en pepper-sprayden. Het bereikte haar vroege einde toen de politie de verschillende kampen met geweld opruimde. Het enige dat er nog van over blijft, buiten enkele norse paters die maar niet kunnen begrijpen dat Occupy gedaan is, is de eigen populistische retoriek van de 99%—die moedwillig opgepikt is door Democratische front-groeperingen zoals MoveOn.org om de Obama-verkiezingsmachine te voeden."</em></p>

</blockquote>

<p>Allez soit. Is er iets op TV vanavond? Een presidentieel debat zegt ge?</p>

<blockquote>
<p><em>"Deze populaire vertelling […] is zo succesvol geweest dat zelfs zij die nog steeds in de Occupybeweging actief zijn het deels absorberen. […Vergaderingen] zijn vaak borderline publiekstherapie. Op het internet worden ietwat zelfophemelende Paul Krugman-y artikels gepost en gerepost dat Occupy “tenminste de publieke dialoog verschoven heeft”  […] om elkaar te herinneren dat Occupy tenminste een klein effect had."</em></p>
</blockquote>

<p>Plezant zeg. De post legt verder uit hoe Amerikanen geschiedenis zien als <em>"iets dat andere mensen (vaak blanke, welstandige mannen) lang geleden deden"</em> en vergeten dat er <em>"honderdduizenden mensen nodig waren—vooral jonge mensen, vrouwen, en laagstandige mannen–om de ideeën van democratie te verspreiden. […] [We] zien Occupy als een spektakel […] van mensen die anders zijn dan ons. […] Occupy-vriendelijke celebrities en artiesten, zwarte-blok-stijl anarchisten en de despotische burgemeesters van onze steden zijn de personages in dit drama, [die het uitstrijden] voor nieuwskoppen op het podium van onze verloederde steden."</em></p>

<blockquote>
<p><em>"De echte magie van Occupy was dat het dit allemaal afwees. Niemand had meer macht dan iemand anders, […] het kon niemand in Occupy echt schelen dat we door de algemene pers genegeerd werden. We hebben geen groep idioten van Time Magazine nodig om ons te prijzen voor onze protestvaardigheid. […] Het doel van Occupy was om samen te komen als een gemeenschap van gelijken, om een andere toekomst te vorderen dan we gegeven werden, en om een traditie van democratische vooruitgang te hernieuwen […] Het was een manier om met elkaar nieuwe omgangsvormen te onderzoeken."</em></p>
</blockquote>

</div></div>

<div class="c m1"></div>

<div class="wide mt3">
  <img src="https://acko.net/files/occupy-www-street/occupy-vancouver.jpg" alt="Occupy Vancouver" />
</div>

<div class="g8 i2 mb1">
<p class="credit"><a target="_blank" href="http://www.vancouverobserver.com/blogs/thescene/2011/10/15/occupy-vancouver-protesters-photos">1</a> <a target="_blank" href="http://weheartit.com/entry/group/210433">2</a> <a target="_blank" href="http://www.vancouverobserver.com/politics/news/2011/11/06/what-are-occupy-vancouvers-demands">3</a> <a target="_blank" href="https://skepticedge.wordpress.com/">4</a> <a target="_blank" href="http://jodieponto.com/photoblog/2011/10/16/occupy-vancouver/">5</a></p>
</div>

<div class="g10 i1 mt0 serif"><div class="pad">

<p>De auteur benadrukt dat we <em>"Occupy niet als tijdelijke rage of linkse koortsdroom mogen afwijzen."</em> Ok. Buiten een "<em>feeling in the air</em>", zijn de beschreven effecten dat <em>"meer dan 7200 Occupyprotesteerders gearresteerd zijn in de Verenigde Staten"</em> met vele <em>"geslagen en gemarteld."</em> Occupy is gedemoniseerd omdat <em>"de media onder druk gezet werd om […] in een ongunstig licht bericht te geven."</em> Wat nog?</p>

<blockquote><p><em>"We moeten herinneren dat het woord “occupy” een […] oproep tot actie is, niet de actie zelf. [Het] was nuttig om individuen en organisaties buiten te brengen die vroeger geïsoleerd waren of zich slechts op één kwestie richtten. […] Het belangrijkste is dat er een brede gemeenschap van oppositie aan het vormen is die vele sociale barrières brugt, en zij die de macht hebben zijn nu heel bang."</em></p></blockquote>

<p>Negeer de dikkenekkerij op 't einde, er is iets veel markanter en verhelderend.</p>

<p>Het resultaat van Occupy, zo zegt een bewijs van zijn blijvende waarde, is dat 7200 mensen zichzelf tot <em>slachtoffers en martelaars</em> gemaakt hebben, ondersteund door passief idealistisch verzet van buitenaf. Ze zijn langzaam depressief en irrelevant geworden, nadat de pers die in de publieke interesse moest spreken hen verkeerd afgeschilderd heeft. Ze vonden dit ok.</p>

<p>De beste leugens zijn half-waar. Dit collectieve absoluut-niet-rouwritueel was een kern van steenharde realiteit in een schil van narcistisch zelfbedrog. Men zou het zelfs Stockholm Syndroom kunnen noemen. Dit zijn de geëvolueerde Pokémongenera van de cultuur van het slachtoffer, waar slachtofferschap gevierd wordt, omdat het je immense macht kan bieden.</p>


<h2 class="mt3">Burgers and Fries with Ketchup</h2>

<p>Ik geloof het officiële Occupyverhaal niet, noch dat van de media noch van de beweging zelf, omdat ik zelf ben gaan kijken in Vancouver. Ik bedoel niet de <em>9/11 Truthers</em> of de wietactivisten: die horen er nu eenmaal bij, waar moeten zij anders naartoe? Nee, ik merkte twee specifieke groepen op.</p>

<p>Eerst waren er de glanzende latté kids die <em>human microphone</em> speelden—net zoals in Biscotti Park!—met vriendjes assorti en te modieus om arm te zijn. Het leek mij creepy: omdat geluidsversterking verboden was zoals bij vele Occupy's, moest iedereen elkaar naroepen, alsof ze mantras zongen. Verdere sabotage was een algemeen gebrek aan focus of moderatie. Voeg een mix mensen toe met meer gevoelens dan ze mee weg kunnen, en het gaat oertraag nergens naar toe, net als Twitter in The&nbsp;Matrix.</p>

</div></div>

<div class="c"></div>

<div class="wide mt1">
  <a target="_blank" href="http://www.matthewjohnburgess.com/essays-on-writing/" class="block"><img src="https://acko.net/files/occupy-www-street/human-mic.jpg" alt="The human microphone" /></a>
</div>

<div class="c"></div>

<div class="g10 i1 mt2 serif"><div class="pad">

<p>Dit zicht was geen publiek forum voor verlicht debat, geen stadsvergadering, het was een irritante zeepkist waar geen ordinaire mens meer dan 5 minuten naar zou luisteren. Ze hadden van activisme gehoord, ze wisten zelfs hoe het eruit zag en hoe het klonk, maar ze dansten enkel de pasjes. Net zoals op vele livestreams die ik elders checkte.</p>

<p>Ten tweede, de eigelijke daklozen van de stad, werkloos en onwerkbaar, mentaal gestoord, chronisch verslaafd of op andere wijze verstoten door de maatschappij, die net in die ruimte rondhingen die ze iedere dag al occuppeerden. Gooi deze twee groepen samen, quelle surprise, de verlichtte idealen over gelijkwaardigheid, privilege, diversiteit, discriminatie, vrije meningsuiting en gemeenschappelijk bezit worden getest. Jeugd wier idee van kamperen niveau Werchter is, met proper sanitair en waterkraantjes, vonden een openbare plaats in putje winter minder aangenaam. Meer nog, als het onvermijdelijke conflict en de misdaad opduiken, is het waarschijnlijk geen goed idee om rechtvaardigheid over te laten aan zij die met woorden als <em>"triggered"</em> en <em>"rape culture"</em> omgaan als ware het snoep.</p>

</div></div>

<div class="c"></div>

<div class="wide mt2">
  <a target="_blank" href="https://www.saybrook.edu/newexistentialists/posts/10-31-11"><img src="https://acko.net/files/occupy-www-street/occupy-empathy.jpg" alt="Scene from Occupy" /></a>
</div>

<div class="c"></div>

<div class="g10 i1 mt2 serif"><div class="pad">

<p>In plaats van deze werkelijkheid te confronteren, adopteerden ze de <a target="_blank" href="http://www.webcitation.org/69wFEoszj" title="http://www2.timesdispatch.com/news/rtd-opinion/2011/nov/04/tdopin02-hinkle-ows-protesters-have-strange-ideas--ar-1433590/">progressive stack</a>, die <em>"vrouwen en traditioneel gemarginaliseerde groepen aanspoort om vóór mannen te spreken, vooral blanke mannen."</em> Als je wat rondgraaft <a target="_blank" href="https://archive.is/N9as3" title="https://sli.mg/Vkzcmj">vind je klachten</a> over <em>"vergaderingen ontspoort door vrouwen en minderheden die geobsedeerd waren met identiteitspolitiek. […] [Plannen] werden praktisch altijd beantwoord met tegenstand van feministen, queeractivisten, zwarten, latino's, enz. Vaak hadden deze mensen zelfs geen specifieke zorgen. […] Ze stonden gewoon recht en begonnen te klagen over een gebrek aan diversiteit, over de victimisatie van hun specifieke identiteitsgroep, etc. […] Slimme, vaardige, georganiseerde blanke kerels vertrokken. Ze trapten het af in frustratie."</em></p>

<p>Er zijn veel verhalen van ruzie en misbruik, het was vruchtige grond voor de media, sociaal of niet. Wat er nu juist wel of niet gebeurd is bij specifieke incidenten was zelden relevant, en zulke zorgen werden belangrijker dan het financieel en economisch systeem of de instituten die het onderleggen. We moesten blijkbaar <a target="_blank" href="https://archive.is/irNvT#selection-823.91-823.139" title="http://www.dominionpaper.ca/articles/4268">het gendergericht geweld in de Occupybeweging confronteren</a>: <em>"Wat er nu juist gebeurd is, is onduidelijk. Sommigen beweren dat er een poging tot verkrachting was. Anderen schilderen heel de affaire af als niets meer dan een jonge vrouw wier persoonlijke ruimte werd binnengevallen door een dronken man."</em> Denk je dat dat onderscheid misschien belangrijk is?</p>

<p>Sociaalvaardige media <a target="_blank" href="https://archive.is/SFZiU" titke="http://www.xojane.com/issues/occupy-wall-street-rape">overdreef gretig</a>, mannen werden herinnerd om <em>"niemand te verkrachten, ooit"</em>—thx xojane—en dat <em>"ruimtes voor vrouwen gebouwd worden om geavanceerde gesprekken te expediteren over kwesties waar vrouwen mee omgaan (en, idealiter, iedereen die zich niet als man identificeert) zonder dat die discussies regelmatig onderbroken worden omdat andere mensen in de groep onderwezen moeten worden."</em></p>

<p>Met andere woorden… Mannen, buiten en zwijgen. Vrouwen, geen vragen stellen. Schitterend gedaan social justice, je hebt alles wat je zogezegd haat naar je idealistische utopie gebracht, en al de anderen weggejaagd.</p>

<p>Het is gemakkelijk om de schuld af te schuiven op whacky marginalen met vreemd haar en een brilleke. Als je Stephen Colbert heet, dan kan je ze zelfs <a target="_blank" href="https://archive.is/EymTY" title="http://newsbusters.org/blogs/pj-gladnick/2011/11/05/colbert-ows-interviewee-ketchup-reveals-self-wacko">genadeloos uitlachen voor miljoenen</a> en grote punten scoren. Het echte probleem is de activistenmassa zelf en de media waar ze in geloven. Ze geraken te graag vast in hun eigen problemen, dingen waarvan ze zelf toegeven dat het eigelijk puur een microcosmos is van een menselijke gemeenschap. De fout is om geïsoleerde incidenten als universeel voor te stellen, terwijl de olifant in de kamer genegeerd wordt. Dat is een ziekte geconcentreerd in de media, maar niet alleen bij hen.</p>
  
<p>Het is moeilijk om een <em>Belangrijk Gesprek over het <a target="_blank" href="https://archive.is/XN8Vh" title="http://www.fudco.com/chip/deconstr.html">Deconstrueren van Zaken</a></em> serieus te nemen, als social justice niet weet hoe te socializen.</p>

</div></div>

<div class="c"></div>

<div class="wide mt1">
  <a target="_blank" href="https://archive.is/EymTY" title="http://newsbusters.org/blogs/pj-gladnick/2011/11/05/colbert-ows-interviewee-ketchup-reveals-self-wacko"><img src="https://acko.net/files/occupy-www-street/ketchup.jpg" alt="Burgers and Fries and Ketchup" /></a>
</div>

<div class="c"></div>

<div class="g10 i1 mt2 serif"><div class="pad">
  
<h2>/Chomsky/ was Right</h2>

<p>In <em>Language and Responsibility</em> (1979) legt Noam Chomsky echter welsprekend uit. Hij beschrijft vooral zijn theorieën van linguïstiek, te niche voor de meesten. Maar het eerste hoofdstuk "Politiek" begint heel anders, met ideeën die later zouden terugkomen in <em>Manufacturing Consent: The Political Economy of the Mass Media</em> (1988). Gevraagd wat de relatie is tussen zijn methodes en zijn bekendere politieke schrijfsels:</p>

</div></div>

<div class="c m1"></div>

<aside class="g8"><div class="pad">
  <a target="_blank" href="http://www.vulgartrader.com/post/32392205367/hipster-chomsky"><img src="https://acko.net/files/occupy-www-street/chomsky.jpg" alt="Noam Chomsky" /></a>
</div></aside>

<div class="g4 serif"><div class="pad">

<blockquote class="flush">
<p><em>"Ik heb geen toegang tot ongewone onderzoeksmethodes en de speciale kennis die ik heb van taal is niet van toepassing op sociale en politieke kwesties. […] Om ideologie te analyseren […] heb je algemeen gezien enkel een beetje open wereldsopvatting, gewone intelligentie en gezond skepticisme nodig."</em></p>
</blockquote>

</div></div>

<div class="g10 i1 mt1 serif"><div class="pad">

<blockquote>
<p><em>"Neem [de] intelligentsia in een maatschappij als de onze. Deze sociale klasse, met historici en andere geleerden, journalisten, politieke commentators en zo voort, neemt het op zich om een beeld van sociale werkelijkheid te brengen via analyse. […] Zij dienen als bemiddelaars tussen de sociale feiten en de bevolkingsmassa: zij scheppen de ideologische onderbouw voor de sociale praktijk. Lees het werk van de specialisten in hedendaagse zaken en vergelijk hun interpretatie met wat er gebeurd is, vergelijk wat ze zeggen met de feiten. Je vindt vaak grote en redelijk systematische afwijkingen […]."</em></p>

<p><em>"Met een beetje moeite en toepassing [ziet] eender wie van het systeem wilt vrijkomen […] dwars door de methodes van vervorming. […] Iedereen kan dat. […] Vaak slecht uitgewerkt, […] produceert men sociale en politieke analyse om speciale interesses te verdedigen in plaats van een feitelijke verklaring te brengen."</em> <em>(p3-4)</em></p> 
</blockquote>

<p>De specifieke situatie in de Verenigde Staten wordt toegelicht. Hij vergelijkt de opgenomen feiten met de aanvaardde verklaring van bijvoorbeeld de McCarthy-paniek, de studentenrevolutie, de burgerrechtenbeweging, de Vietnamoorlog, en meer. Hij praat over interventies van de VS in het thuisland en elders, legitiem of illegitiem. Het overheersende thema is duidelijk:</p>

<blockquote>
<p><em>"Hier in de VS is er een verbazingwekkende graad van ideologische uniformiteit voor zulk een complex land. Hier zijn algemeen twee redenen voor. Eerst is er de verbazende ideologische gelijkheid van de Amerikaanse intelligentsia in groep, die zelden afwijken van één van de varianten van staat-kapitalistische ideologie (liberaal of conservatief), een vaststelling die voor verklaring vraagt. Tweede is dat de massa-media kapitalistische instituten zijn. […] Het feit dat deze instituten de ideologie van overheersende economische interesses weerspiegelen kan geen verrassing zijn."</em> <em>(p9)</em></p>
</blockquote>

<p>Een hoofdartikel in de <em>New York Times</em> van 1975, een terugblik op de Vietnamoorlog, legt uit dat er mensen zijn die denken dat <em>"de oorlog […] anders gevoerd had kunnen worden"</em> en zij die denken dat <em>"een haalbaar niet-Communistisch Zuid-Vietnam altijd een mythe was."</em> Chomsky speelt vlot kaart:</p>

<blockquote>

<p><em>"Ze zeggen niets over de logische mogelijkheid van een derde positie: namelijk, dat de VS […] het legaal of moreel recht niet had, om zich met macht in de interne zaken van Vietnam te moeien. […] Merk op dat wanneer de <em>Times</em> het spectrum van ideologisch debat opzet, de positie van een groot deel van de vredesbeweging eenvoudigweg uitgesloten wordt. Niet dat deze fout is, maar eerder ondenkbaar, onuitdrukbaar. […]</em></p>

<p><em>Hier hebben we een fantastische illustratie van hoe propaganda werkt in een democratie. Een totalitaire staat legt officiële doctrine gewoon op—duidelijk, expliciet. […] oppositie spreekt op eigen risico. In een democratisch systeem van propaganda wordt niemand gestraft (in theorie) voor bezwaren tegen officieel dogma. Meer nog, dissidentie wordt aangemoedigd.</em></p>

<p><em>Wat dit systeem probeert te doen is om de limieten van mogelijke denkgang vast te zetten: supporters van officiële doctrine aan de ene kant, en de critici—levendig, moedig, en veel bewonderd voor hun onafhankelijkheid van oordeel—aan de andere. De haviken en de duiven. Maar we ontdekken dat ze allemaal bepaalde stille veronderstellingen hebben, en dat het net deze zijn die cruciaal zijn.</em></p>

<p><em>Ongetwijfeld is een propagandasysteem effectiever wanneer de doctrines geïnsinueerd worden in plaats van geasserteerd. […] Hoe levendiger het debat, hoe beter de algemene doctrines van het systeem ingeprent worden, zwijgend verondersteld aan alle kanten. Vandaar de uitgebreide pretentie dat de pers een kritische oppositionele kracht is—misschien zelfs té kritisch voor een gezonde democratie—terwijl zij praktisch compleet onderdanig is aan de algemene principes van het ideologisch systeem."</em> <em>(p36-39)</em></p>
</blockquote>

<p>Dit was in de jaren 70. Hoe zou dat er in het internettijdperk uitzien?</p>


<h2 class="mt3">Don't Stalk Me Bro</h2>

<p>In 2006 lanceerde Gawker Media de <a target="_blank" href="https://archive.is/JM9mj" title="http://gawker.com/160338/introducing-gawker-stalker-maps">Gawker Stalker Maps</a>, een site waar iedereen gevraagd werd om publieke meldingen van bekende gezichten in te sturen. Hollywoodsterren zagen de typische bende papparazzi gevolgd door nu nog vreemdere mensen van op het internet, en konden er niet bepaald mee lachen.</p>

<p class="tc"><a target="_blank" href="https://www.youtube.com/watch?v=2-avakrRUaU" class="block"><img src="https://acko.net/files/occupy-www-street/gawker-stalker-kimmel.jpg" class="natural" /></a></p>

<p>Wanneer Jimmy Kimmel hen een jaar later op het matje roept, gaat redactrice Emily Gould <a target="_blank" href="https://www.youtube.com/watch?v=2-avakrRUaU">voor het goud in de mentale gymnastiek</a>. Ondanks de oorspronkelijke vraag voor info <em>"zo snel als je het kan insturen,"</em> beweert ze dat de kaart meestal traag updatet. Ze lacht bewijs van gepubliceerde leugens weg als zijnde <em>"burgerjournalistiek"</em> waarvan <em>"iedereen weet dat dat niet nagekeken wordt,"</em> hoewel veel van de inhoud redactioneel is. Ze struikelt en mist haar recovery, met <em>"een stalking…heh…het is geen <em class="normal">echte</em> stalking,"</em> omdat er nu eenmaal geen verwachting is om niet in het openbaar opgemerkt te worden, en het bevestigt alleen maar dat bekende gezichten gewone mensen zijn. Ze vraagt dan meteen of celebrities niet <em>"door bergen geld beschermd worden als men stenen naar hen gooit,"</em> wordt afgefloten en moonwalkt in respons dat <em>"het niet ok is om leugens over de mensen te vertellen,"</em> in een complete invalidering van al het vorige.</p>

<p>Kimmel gooit er een leuke anachronistische zinger bij: <em>"Ik snap nu eenmaal niet waarom iemand advertenties op een website zou willen kopen."</em></p>

<p>Het is zeldzaam. Verre celebrities worden gehumaniseerd, nieuwe media krijgt lik op stuk van de oude, en de vrolijke poppenkast stelt zich bloot als de schaamteloze opportuniste die ze is. Een kans om te tonen dat de media beter weet, niet? Aaron Sorkin zag het ook en nam dit <em>teachable moment</em> op in een aflevering van The Newsroom, <a target="_blank" href="https://www.youtube.com/watch?v=SWjREXKdXbM">met dialoog deels letterlijk overgenomen</a>:</p>

</div></div>

<div class="c"></div>

<div class="wide mt1">
  <a target="_blank" href="https://www.youtube.com/watch?v=SWjREXKdXbM"><img src="https://acko.net/files/occupy-www-street/the-newsroom.jpg" alt="The Newsroom" /></a>
</div>

<div class="c"></div>

<div class="g10 i1 mt1 serif"><div class="pad">

<p>Helaas natuurlijk, al de scherpe kantjes zijn weggevijld. In plaats van een bubbly redactrice van een internetroddelblad, is er een mollig Zuckerberg-type die zijn tech startup en app zit te verkopen. Zijn bezwaar lijkt oprechter, en de plotse beschuldigingen zijn onverwacht, niet het geplande onderwerp. De celebrities worden niet voorgesteld, zij zijn elders, druk bezig achtervolgd en vermoord te worden. Hij is een onwetende technocraat die de maatschappij schaadt met zijn egoïstisch autisme, zij is de charmant-maar-genadeloze journaliste die hem op de feiten drukt.</p>

<p>Zie je hoe een compleet media-on-media verhaal herschreven werd tot iets helemaal anders, zonder zelfs de meeste woorden aan te raken? Het zit 'em volledig in de afwerking en presentatie. Het excuus dat het niet helemaal realistisch of letterlijk mag zijn is niet van toepassing. Ik vind de hele zaak nogal <em>handig</em> om bepaalde visies in een goed licht te stellen. Het schept verwarring tussen de sociopaten die bepaalde zaken draaien, en de mensen die de technologie onderhouden, met een subtiel laagje <em>nerd bashing</em> er bovenop.</p>

<p>Het zou best wel eens een goed voorbeeld kunnen zijn van wat Chomsky zei: wanneer ze een interpretatie van de feiten brengen, schuiven Amerikaanse retorici een lens in die enkel op een heel smal en voorspelbaar kader schijnt, terwijl ze net het omgekeerde beweren. Zoals tonen <em>hoe het nieuws nu echt gemaakt wordt</em>, een <em>uitgebreide pretentie dat de pers een kritische oppositionele kracht is</em>.</p>

</div></div>

<div class="c"></div>

<div class="wide mt1">
  <a target="_blank" href="https://www.youtube.com/watch?v=SWjREXKdXbM"><img src="https://acko.net/files/occupy-www-street/the-newsroom-2.jpg" alt="The Newsroom" /></a>
</div>

<div class="c"></div>

<div class="g10 i1 mt1 serif"><div class="pad">

<p>Of misschien is het gewoon een middelmatige aflevering van een uitsputterend TV-programma. Alleen, ik ben zeker dat er al ontelbare artikels en reacties zijn die dit in pijnlijk diep detail nagaan.</p>

<p>In ieder geval, terwijl ze net realiteit in hun fantasie wilden brengen, hebben ze het in hun eigen beeld herschapen. Ze doen dat wel vaker. Niet alleen degene die <a target="_blank" href="https://archive.is/t4EVf" title="http://newsdiffs.org/diff/934341/934454/www.nytimes.com/2015/07/11/technology/ellen-pao-reddit-chief-executive-resignation.html">fictie schrijven</a>.</p>

<h2 class="mt3">Celebrate Diversity</h2>

<p>Waar zouden we dit onlangs weer allemaal gezien hebben? Laat ik het effe allemaal uitleggen. Wat doe je als je een bekende verslaggever bent in entertainment, bijvoorbeeld video games, en ontdekt dat een handelspers grotendeels overbodig is wanneer YouTube, Twitch, Twitter, Reddit en anderen bestaan?</p>

<p>Wel, misschien heb je dan een existentiële crisis, wanneer je beseft dat je eigelijk gewoon een <a target="_blank" href="https://archive.is/cjyTj">zatte megafoon</a> was voor zakelijke marketinginteresses, en beslis je daarom een <a target="_blank" href="http://deepfreeze.it/article.php?a=unfair#alexander">antenne voor je eigen interesses op te zetten</a>. Spring op Twitter en post <em>"Fuck ethics get money,"</em> geef talks waar je <em>"geen pretentie hebt van onpartijdig te zijn."</em> Geef aandacht aan mensen en projecten die je leuk vindt, verspreid politieke en sociale ideeën die je belangrijk vindt, en kies de exclusieve hits zelf in plaats van te wachten tot ze in je schoot landen. Terwijl je meedoet aan de globale neerwaartse race zijnde <a target="_blank" href="http://deepfreeze.it/article.php?a=quickdirty">modern clickbait</a>.</p>

<p>Als genoeg mensen in een clique dit doen, dan zou dat best wel eens de schijn van een indie renaissance kunnen opwekken. Maar het zou enkel aandacht voeden aan een heel smalle context, enkel toegankelijk voor hen met de juiste connecties en die de juiste dingen zeggen. Als dit allemaal gebeurt in een multi-miljarden-dollars-industrie, dan zou dat best wel eens op wat tenen kunnen trappen. Vooral als er bijvoorbeeld <a target="_blank" href="http://www.tiki-toki.com/timeline/entry/343871/Corruption-consumer-hate-and-bad-journalism-in-games-journalism/">jarenlange problem waren met tendentieuze journalistiek, belangenverstrengeling, en politieke agenda's</a>, met meerdere carrières permanent besmeurd met pek en veren, via onverantwoordelijke roddel en leugens. <a target="_blank" href="https://archive.is/U0pBQ" title="https://paxdickinson.wordpress.com/2014/10/22/moral-panics-and-the-death-of-fun/">Puur</a> <a target="_blank" href="https://archive.is/68A2y" title="http://www.escapistmagazine.com/articles/view/video-games/gamergate-interviews/12397-Brad-Wardell-GamerGate-Interview.2">hypothetisch</a>, natuurlijk.</p>

</div></div>

<div class="c"></div>

<div class="wide mt1">
  <a target="_blank" href="http://deepfreeze.it"><img src="https://acko.net/files/occupy-www-street/ramiel.jpg" alt="Ramiel" /></a>
</div>

<div class="c"></div>

<div class="g10 i1 mt1 serif"><div class="pad">

<p>In zulke omgeving, stel je voor dat bewijsmateriaal opduikt dat één van de nieuwe medialievelingen eigelijk een redelijk gestoord individu is. Terwijl ze zich profileert als Moeder Theresa en een getalenteerde progressieve activiste en game-ontwikkelaar, manipuleert en liegt ze zich dwars door haar carrière en relaties. Dit alles is in de vorm van <a target="_blank" href="https://thezoepost.wordpress.com">een waarschuwing en gedetailleerde beschrijving van emotioneel misbruik</a>, bewezen met, bijvoorbeeld, uitgebreide Facebookcitaten. De details zijn vrij pikant, namelijk ontrouw met een handvol zogenaamde <em>Five Guys</em> in de game industrie, inclusief haar baas en een Kotaku-verslaggever. Bovendien heeft deze keizerin geen kleren aan, want haar "werk" bestaat grotendeels uit één kies-je-eigen-verhaal Twine boek dat amper zou voldoen als een eerstejaarsstudente's half-jaarproject. Het werd vooral geloofd voor het onderwerp—depressie—niet de uitwerking.</p>

<p>Stel je bovendien voor dat in respons, andere kennissen uit de kast komen om <a target="_blank" href="http://gamergate.wikia.com/wiki/Zoe_Quinn">andere verhalen te vertellen</a>. Zoals valse beschuldigingen <a target="_blank" href="https://archive.is/zSifp" title="http://i.imgur.com/moFXvoa.jpg">dat een forum van depressieve mensen</a> haar lastig viel, <a target="_blank" href="http://knowyourmeme.com/photos/813691-quinnspiracy">persoonlijke gegevens lekken</a> van een goed doel gezien als rivaal, hen <a target="_blank" href="https://www.reddit.com/r/KotakuInAction/comments/2gtqoq/the_story_of_the_fine_young_capitalists_and_the/">zwartschilderen tegen vrienden in media</a>, een <a target="_blank" href="http://knowyourmeme.com/photos/815901-quinnspiracy">reality TV game show</a> waar ze in meespeelt saboteren, geld vragen voor een ander evenement dat <a target="_blank" href="https://archive.is/HYm63" title="http://apgnation.com/articles/2014/09/09/6977/truth-gaming-interview-fine-young-capitalists">nooit plaats had</a>,  dit alles door vorige contacten redelijk kwalijk genomen.</p>

<p>Maar belangrijker, stel je voor dat dit schandaal een druppel is, en dat <a target="_blank" href="https://archive.is/3z33G" title="https://www.reddit.com/r/Games/comments/2ejs7v/gaming_journalists_patricia_hernandez_of_kotaku/">vorige belangensverstrengeling</a> herbezocht of ontdekt wordt, <a target="_blank" href="https://archive.is/fEfRB" title="http://kotaku.com/a-different-way-to-respond-to-a-rape-accusation-update-1605542083">vorige lynchpartijen in de media</a> besproken worden, en verantwoording gevraagd wordt, omdat het over <a target="_blank" href="https://www.youtube.com/watch?v=0QLz0CqtMVc#t=121">meerdere invloedrijke sites gaat</a> en een hoop mensen <a target="_blank" href="https://archive.is/DIpqE#selection-4917.0-4928.0" title="http://www.gamespot.com/articles/who-wins-when-mass-effect-3s-ending-changes/1100-6367380/">het beu zijn</a>.</p>

</div></div>

<aside class="g7"><div class="pad">
  <p><a class="block" target="_blank" href="https://archive.is/nWG2P" title="http://www.gamasutra.com/view/news/224400/Gamers_dont_have_to_be_your_audience_Gamers_are_over.php"><img src="https://acko.net/files/occupy-www-street/gamers-are-over.jpg" alt="Gamers are over" /></a></p>
</div></aside>

<div class="g5 serif"><div class="pad">

<p>In plaats van toe te geven dat het allemaal één grote klucht geworden is, en iedereen misleid werd, zouden verslaggevers best wel eens de rangen kunnen sluiten. Ze zouden een verhaal kunnen bedenken dat al de kritiek die zij ontving onaanvaardbare intimidatie was, dat gamers een stelletje <a target="_blank" href="https://archive.is/Awcw9" title="http://www.gamasutra.com/view/news/224400/Gamers_dont_have_to_be_your_audience_Gamers_are_over.php">stompe strontgooiers en tierende hyper&shy;consumenten</a> zijn die vrouwen haten, in een declaratie dat de gameridentiteit dood is, in <a target="_blank" href="https://pixietalksgamergate.wordpress.com/gamers-are-dead-article-analysis/">tien artikels gepubliceerd in 24 uur</a>. Hoewel dit hun bestaande publiek zou wegjagen, zou dit met plezier ontvangen worden door het nieuwere, meer politieke segment, die al hun voorbestaande foute ideeën over video games en gamers bevestigd zouden zien, compleet onwetend dat het medium al een lange geschiedenis had van <a target="_blank" href="http://torment.wikia.com/wiki/Planescape:_Torment">ingewikkelde thema's</a> op <a target="_blank" href="http://silenthill.wikia.com/wiki/Silent_Hill_2">volwassen wijze gebracht</a>.</p>

</div></div>

<div class="g12 mt0 serif"><div class="pad">
  
<p class="mt0 mb2"><a class="block" target="_blank" href="https://archive.is/Awcw9" title="http://www.gamasutra.com/view/news/224400/Gamers_dont_have_to_be_your_audience_Gamers_are_over.php"><img src="https://acko.net/files/occupy-www-street/gamers-are-over-2.png" alt="Gamers are over" /></a></p>

</div></div>

<div class="g10 i1 mt0 serif"><div class="pad">

<p class="mt0">Nieuwe lezers zouden dit misschien kunnen zien als startschot om het plebs van Rome te onderwijzen en de vrome maagden te verdedigen van de zogezegde barbaarse horde, terwijl iedereen op social media kan meekijken, <a target="_blank" href="http://knowyourmeme.com/memes/nods-respectfully-towards-you">nodding respectfully</a>. Ze zouden heel de affaire best wel eens kunnen herschrijven als een "afgewezen ex"—hoewel hij <em>haar</em> dumpte—die een haatbende bijeen riep op 4chan—hoewel hij eerst op <a target="_blank" href="https://archive.is/h5LTs" title="https://medium.com/@srachel_m/gamergate-launched-in-my-apartment-and-internet-im-sorry-not-that-sorry-13e5650fd172">Something Awful en Penny Arcade</a> postte en de reactie gemengd was. Ze zouden misschien spreken over "wraakporno" hoewel het eigelijk haar eigen <a target="_blank" href="https://archive.is/rbmsg" title="http://www.skepticink.com/incredulous/2014/10/04/zoe-quinns-lying-cheating-claim-stabbing-killing-man-alleged-former-photographer/">betaalde erotica</a> is die circuleert. Dit zijn allemaal ofwel directe leugens ofwel een bekentenis dat het te veel moeite was om <a target="_blank" href="http://www.gamergatewiki.com/index.php/Timeline/Full">bronmateriaal te lezen</a>.</p>



<p>Het zou cruciaal zijn dat berichten nooit spreken over de gecoördineerde censuur die op Reddit, 4chan en andere gamingfora zou kunnen plaatsnemen, en het resulterende Streisand-effect dat er enkel meer aandacht naar zou brengen. Bijvoorbeeld <a target="_blank" href="https://archive.is/rp54n" title="http://greggreenwell.tumblr.com/post/95136135894/zoe-quinn-is-using-false-dmca-claims-to-take-down">een valse copyrightclaim</a> op een prominente YouTuber, een <a target="_blank" href="https://archive.is/i9Du4" title="http://www.reddit.com/r/gaming/comments/2dz0gs/totalbiscuit_discusses_the_state_of_games">kerkhof van 25 000 gewiste reacties</a>, mensen bannen gewoon omdat ze bepaalde namen durven vermelden, en heel het onderwerp interdit maken op bepaalde <em>geallieerde</em> subreddits. Hiervoor zouden gunsten moeten ingeruild worden achter de schermen bij de moderators, en dit zou best wel eens de schijn van <a target="_blank" href="https://archive.is/7VKAE" title="http://blogjob.com/oneangrygamer/2015/03/reddit-mods-admit-to-censoring-gamergate-hiding-corruption/">nog meer collusie</a> en zelfs regelrechte samenzwering kunnen opwekken, zodat sommigen in degout zouden verhuizen van 4chan naar een voordien piepklein forum, en daarna naar Voat van Reddit.</p>

<p>Zelfs als iemand volledig voorspeld had wat er ging gebeuren, <a target="_blank" href="https://archive.is/AemDy#selection-7417.0-7421.310" title="https://archive.4plebs.org/pol/thread/34317761/">een week voordien</a>, dan zou de gamingpers niet, onder welke omstandigheden ook, de realiteit confronteren en fouten toegeven. In plaats daarvan zouden ze een lange en uitgerekte mediaoorlog beginnen tegen consumenten die vooral verantwoording willen, en zo de grootste magneet voor trolls, drama, e-celebrities en valse vlaggen worden die het internet tot nu toe gezien had. Vooral diegene <a target="_blank" href="https://archive.is/wbbOI#selection-2943.0-2950.0" title="http://gamergate.wikia.com/wiki/Reddit">die het oude internet al lang kende</a>.</p>

<p>Net zoals het slachtoffer van misbruik Eron Gjoni gewaarschuwd had, voor hij monddood gemaakt werd door omstreden en <a target="_blank" href="https://archive.is/pzSeP" title="https://www.reddit.com/r/KotakuInAction/comments/3hgq3b/zoe_quinn_moves_to_vacate_restraining_order_eron/">mogelijk ongrondwettelijke juristiek</a>, zou een heel segment van een industrie wel eens <a target="_blank" href="https://archive.is/RNzCI" title="http://gawker.com/how-we-got-rolled-by-the-dishonest-fascists-of-gamergat-1649496579">compleet geschift kunnen gaan</a> de maanden nadien, wegens de egoïstische machinaties van een misbruikster. Games en feministische media zouden eindeloos materiaal hebben met hun nieuwe zondebok. Omdat de globale pers eigelijk vooral telefoontje speelt, zouden ze herhalen wat deze <em>experts</em> hen gezegd hadden. Ze zouden met de knuppel van collectieve schuld zwieren zonder verder te kijken dan hun neus lang is, en onwetend zulk een massaal <a target="_blank" href="https://duckduckgo.com/?q=gamergate+harassment">grote leugen</a> vertellen dat Goebbels er kloppend priapisme van zou krijgen. Bijvoorbeeld als één <a target="_blank" href="https://archive.is/Hy6AO" title="https://sli.mg/NUIXim">anonieme internet comment</a> waar niemand iets van weet ogenblikkelijk de <a target="_blank" href="https://archive.is/vAJ69" title="https://duckduckgo.com/?q=felicia+day+doxxed">dood van Princess Diana in the Mushroom Kingdom</a> werd. Als willekeurige IRC quotes als <a target="_blank" href="https://archive.is/MWvXC" title="http://www.theguardian.com/technology/2014/sep/12/zoe-quinn-gamergate-online-hate-mobs-depression-quest">spion-thriller feiten</a> aanzien werden over de meningen en motivaties van duizenden, zonder onderzoek of recht op antwoord. Misschien zouden ze het woord nemen van een seriële leugenaar en zelf-beschreven <a target="_blank" href="https://archive.is/Qb96g" title="https://twitter.com/TheQuinnspiracy/status/166659770248474624">helldump-veteraan</a>, die zowel als aartshertog Ferdinand en <a target="_blank" href="https://archive.is/h8rlT">Jeanne d'Arc</a> in haar eigen <a target="_blank" href="http://knowyourmeme.com/memes/events/donglegate-adria-richards">DongleGate</a> figureert.</p>

<p>Dit allemaal om <em>vidya</em>, video games, allemaal omdat een hoop verslaggevers <a target="_blank" href="https://archive.is/6yr0p" title="http://deepfreeze.it/journo.php">koningen en koninginnen</a> werden in een kasteel waar amper iemand nog naar omkeek. Ze vonden een publiek dat overtuigd was dat je activisme via Twitter en Tumblr deed en dat <a target="_blank" href="https://archive.is/iNbS5" title="http://www.thecrimson.com/column/words-words-words/article/2015/3/12/simplistic-social-justice-warrior/">een campus</a> of <a target="_blank" href="https://archive.is/c2RWs" title="http://www.examiner.com/article/calgary-expo-censors-the-honey-badger-brigade-s-anti-censorship-message">een congres</a> een plaats was waar <a target="_blank" href="https://www.youtube.com/watch?v=iARHCxAMAO0">onderzoek ter goeder trouw</a> sterft.</p>

<p>Maar dat zou zot zijn.</p>

</div></div>

<div class="g12 mt0 serif"><div class="pad">

<p class="mt0 mb0"><a target="_blank" href="http://knowyourmeme.com/memes/events/shirtstorm"><img src="https://acko.net/files/occupy-www-street/matt-taylor-shirt.jpg" alt="Matt Taylor and his shirt" class="fit block"></a></p>

</div></div>

<div class="g10 i1 mt0 serif"><div class="pad">

<p>Nog hallucinanter zou zijn om te zien dat, in tegenstelling tot al de vorige keren, dit soort entryisme-via-social-justice tegen een muur van moedwillige oppositie zou knallen. Dat op één of andere manier het onmogelijke gebeurd was, en dat mensen van <a target="_blank" href="https://www.reddit.com/r/KotakuInAction/">Reddit</a> en <a target="_blank" href="https://8ch.net/">4chan</a> hun verschillen lang genoeg konden overkomen om effectief <a target="_blank" href="https://gitgud.io/gamergate/gamergateop/tree/master/Operations/Operation-Disrespectful-Nod">werk gedaan te krijgen</a>. Dat al de <a target="_blank" href="https://www.reddit.com/r/tumblrinaction">oppression olympics</a>, de <a target="_blank" href="http://knowyourmeme.com/memes/events/shirtstorm">lynchpartijen</a>, <a target="_blank" href="https://duckduckgo.com/?q=tim+hunt">de karikaturen</a>, het <a target="_blank" href="http://slatestarcodex.com/2014/07/07/social-justice-and-words-words-words/">woordenspel</a>, de <a target="_blank" href="http://esr.ibiblio.org/?p=2122">denkfouten</a> en <a target="_blank" href="https://www.youtube.com/watch?v=IAF2UmyXe-4">morele aanstellerij</a> eindelijk genoeg mensen over de schreef had geduwd om te zeggen: nee deze keer, neem <em>jij</em> eens een stap terug. Dat ze zouden proberen om Twitter om te vormen naar het rationeelste discussieforum dat het ooit zou zijn, namelijk, helemaal niet. Dat de mensen daar zo verslaafd zouden zijn aan digitale bevestiging en goedkeuring, dat ze de kordate afbraak van hun slordige ideeën zouden verwarren voor misbruik. Ze zouden het misschien zelfs <a target="_blank" href="https://duckduckgo.com/?q=cyberviolence+at+the+UN">cybergeweld kunnen noemen</a> bij de VN, met enkele idioten geselecteerd als toonbeeld voor effect, en <a target="_blank" href="https://archive.is/ITMjR" title="https://github.com/freebsdgirl/ggautoblocker">de rest geautoblokkeerd</a> in een zelfverwekkende profetie.</p>

<p class="mt2 mb2"><iframe style="width: 100%" height="400" src="https://www.youtube.com/embed/8NhIBhbqWo4" frameborder="0" allowfullscreen="allowfullscreen"></iframe></p>

<p>Het zou best kunnen dat je, een jaar in, ontdekt dat het nogal moeilijk is om de <a target="_blank" href="http://spjairplay.com">Society of Professional Journalists</a> te doen erkennen dat er probleemniveaus van partijdigheid, copypasting, luiheid en strooplikkerij zijn. Veel moeilijker dan in 2011 met Fox News: <a target="_blank" href="https://archive.is/mj8wM" title="http://risemiaminews.com/2015/08/breaking-gamer-gate-controversy-prompts-evacuation-of-koubek-center-in-miami/">deze keer</a> zijn er <a target="_blank" href="https://archive.is/NmVco" title="http://nichegamer.com/2015/05/peaceful-gamergate-in-d-c-meetup-receives-false-bomb-threats/">bommeldingen</a> en niemand lijkt bepaald geïnteresseerd wie de daders zijn of zelfs <a target="_blank" href="https://archive.is/fFzuD" title="http://gamepolitics.com/2015/08/12/challenge-accepted-interviewing-internet-hashtag/">hoe hierover bericht te geven</a>. Dat Wikipedia's infotocratie <a target="_blank" href="https://archive.is/jehPF">gewoonweg</a> omschakelt naar <a target="_blank" href="https://archive.is/rUVo3">voluit propaganda</a> voor <a target="_blank" href="https://archive.is/Nf6Wv">een tijd</a>, vastgerot in de <a target="_blank" href="https://archive.is/P2UdO" title="http://i.imgur.com/RnQJ3Hl.png">citogenesis</a> die ze <a target="_blank" href="https://archive.is/G4ppN" title="https://www.reddit.com/r/KotakuInAction/comments/2yfrfb/why_did_ryulong_get_banned_from_wikipedia/">zelf schepten</a>.</p>

<p>Omdat het nu eenmaal veel <a target="_blank" href="https://archive.is/ouEJX" title="https://www.patreon.com/zoe">lucratiever</a> is om <a target="_blank" href="https://archive.is/14BFg" title="https://archive.is/14BFg">dom te doen</a> nadat je <a target="_blank" href="https://archive.is/X7DJB" title="https://images.encyclopediadramatica.se/7/71/Brianna_Wu_Meme_original.png">autistische kinderen</a> voor <a target="_blank" href="https://archive.is/UhZYI" title="http://www.cjr.org/the_kicker/gawker_bullying.php">de lol</a> gebruikt, vanuit een positie van <a target="_blank" href="https://archive.is/WhVcQ" title="https://pbs.twimg.com/media/B1Pg0IgIYAEKXX_.png:large">onverdiend voordeel</a>.</p>

<h2 class="mt3">Brave New World</h2>

<p>Laten we de toer afmaken op dit hoefijzer van waanzin.</p>

<p>Wat als het nu bleek dat Occupy's langgezochte <em>"nieuwe manier om met elkaar om te gaan"</em> al bestond sinds 2001, in de vorm van het Anonieme Image Board? Misschien niet toevallig een uitvinding van Japan, een cultuur die repressie tot een kunstvorm bracht. Een forum waar identiteit en status geen standaardwaarde hebben, en bijgevolg <a target="_blank" href="http://twentytwo.fibreculturejournal.org/fcj-158-tits-or-gtfo-the-logics-of-misogyny-on-4chans-random-b/">compleet andere spelregels gehanteerd worden</a>, als evolutie van de praktijk van vroege cyberspace.</p>

<p>Het leidt niet tot een utopisch forum waar intellectuelen pontificeren alsof ze op de set van Star Trek staan. Integendeel, dit gaat over homo sapiens. Het leidt tot plaatsen met een mix van diep skepticisme, willekeurige beledigingen en grafische schendingen, waar iedereen en alles even waardeloos en nep is tot het tegendeel bewezen is. Het is niet alleen onwelkom om identiteit als algemeen argument op te brengen, het wordt afgeschreeuwd met net die beledigingen die de meeste schade doen. Relevantie is het resultaat van weerstand, wie zich beledigd voelt verliest. Creativiteit, overdrijving en humor zijn de controlemechanismen om het allemaal uit te balanceren, als non-stop bullshit test tegen de <a target="_blank" href="https://www.youtube.com/watch?v=DlSx7m09R60">ongeïnspireerde mens</a> en zijn <a target="_blank" href="https://www.youtube.com/watch?v=WcebgKvAoh0">intellectueel bankroet</a>. Ideeën moeten concurreren om relevant te blijven, maar wel met een belangrijke nota: geen enkel onderwerp is verboden. Vooral taboe. 't Is niet bepaald voor boterbloemekes.</p>

<p>Het is de functioneelste, meest universele <em>safe space</em> die het internet heeft, namelijk, helemaal niet, niet volgens enige offline definitie. Eens je het gewoon bent… de porno, de trolls, de marchanten, de jodengrappen, de <em>jeanetterij</em>, het wordt allemaal zo banaal en transparant dat het de moeite niet is om je er druk in te maken. En dat is nu nét hoe het zou moeten.</p>

<p><iframe style="width: 100%" height="410" src="https://www.youtube.com/embed/3hPFPjQ4pjo?cc_load_policy=1" frameborder="0" allowfullscreen="allowfullscreen"></iframe></p>

<p class="mt2 mb2">Kijk, hoewel het even zwak is voor al het gevit en al het tribalisme dat Twitter zo successvol maakte, is het <a target="_blank" href="https://archive.is/vWYnS" title="https://sli.mg/BVK1RH">veel capabeler</a> zowel als medium en cultuur om er <a target="_blank" href="https://archive.is/kCa3S" title="https://sli.mg/x18eNq">duurzaam mee overweg te gaan</a>. Misschien zouden ze zelfs hun <a target="_blank" href="https://archive.is/Oxe8d" title="https://sli.mg/Tw3tn4">eigen dorp platbranden</a> en <a target="_blank" href="https://archive.is/oViUh" title="https://sli.mg/5z0jV6">elders opnieuw opbouwen</a> in grote nood. Als het allemaal bestaat uit triviale eentjes en nullekes van de conciërge, is dat veel meer dan een adreswijziging? Dit is niet zo vreemd: onze voorouders waren nomaden, totdat de belofte van een volle buik en een permanent dak boven ons hoofd ons overtuigde om een beetje vrijheid op te geven voor een hele hoop veiligheid.</p>

<p><iframe style="width: 100%" height="410" src="https://www.youtube.com/embed/Vx5BNDPA5hE" frameborder="0" allowfullscreen="allowfullscreen"></iframe></p>

</div></div>

<div class="g9 mt0 serif"><div class="pad">
  
<p>Er zijn velen die dit weten, <a target="_blank" href="http://knowyourmeme.com/memes/events/project-chanology">er is niks nieuw aan</a>, en een generatie is er nu mee aan't opgroeien, even normaal als Facebook en Snapchat. Sommige anderen echter kijken toe op de chans als ware het een hellepoort naar eindeloze kwezels, criminelen en lastposten. Net zoals 9/11 Truth bij Occupy, als je bepaalde ideeën in de tegencultuur drukt, verbaas je niet dat zij die openlijk willen spreken ruimte moeten delen met twijfelachtige buren, met een heel lange staart van fringe en echte marginalizatie. Je vindt dit misschien niet fijn, maar je kan er niet naast kijken.</p>

</div></div>

<aside class="g3"><div class="pad">
  <p class="mt0"><a class="block" target="_blank" href="http://knowyourmeme.com/memes/mark-bunkerwise-beard-man"><img src="https://acko.net/files/occupy-www-street/wise-beard-man-alt.jpg" alt="Mark Bunker, Wise Beard Man" /></a></p>
</div></aside>

<div class="g10 i1 mt0 serif"><div class="pad">

<p>Voor oudere lezers: als je denkt dat <em>Dungeons and Dragons</em> tieners tot heksen en tovenaars omdoctrineert, of dat creches kinderen doodden in satanische rituelen, verwacht dat mensen je uitlachen als onwetende paniekerige lomperik. Misschien hebben ze wel <a target="_blank" href="https://archive.is/IgTMn" title="http://www.torychristman.com/2008/02/01/youth-speaks-to-tory-anonymous-vs-scientologys-abuses/">jarenlang</a> geleden <a target="_blank" href="https://archive.is/AFT86" title="http://i.imgur.com/jQ2VBPA.png">bruggen leren bouwen</a> zonder dat je het wist, en was het niet nodig om zich aan de <em>"juiste kant"</em> van de geschiedenis te attacheren.</p>

<p>Het is nog hilarischer wanneer ze dit zeggen over <em>dear old Reddit</em>, bijvoorbeeld in <a target="_blank" href="https://archive.is/dj7Jy" title="http://idibon.com/toxicity-in-reddit-communities-a-journey-to-the-darkest-depths-of-the-interwebs/">belachelijk slecht internetonderzoek</a>. Ze doen hun best om hun eigen conclusies en de troepschoppers aan hun kant te negeren, met een willekeurige <em>"but Women in Tech!"</em> ertussen. Ze zien wat ze willen zien, zonder te beseffen dat alles letterlijk nemen niet alleen de voorspelbaarste fout is, het is vaak een mechanisme dat dient om <em>ongemotiveerde</em> buitenstaanders buiten te houden. De trolls die je meteen opmerkt zijn de <a target="_blank" href="https://archive.is/P3Qqu" title="http://www.breitbart.com/london/2014/12/10/the-madness-of-queen-shanley/">amateurs</a>, en bepaalde mensen zijn zo nutteloos dat ze een netto kost zijn voor iedere online&shy;gemeenschap waar ze aanmeren, vaak zonder wedergevolg.</p>

<p>Het internet verliest zijn unieke waarde van zodra je wilt dat het net als het echte leven werkt. Onze primaire instincten en activiteiten zijn niet zo geschikt voor meer dan één kleine stam, dat lijkt zowel <a target="_blank" href="https://archive.is/s0GhA" title="http://beautifultrouble.org/theory/dunbars-number/">empirisch feit</a> als <a target="_blank" href="http://knowyourmeme.com/memes/greater-internet-fuckwad-theory">persoonlijke ervaring</a>. Onze standaardimpuls is om op gedeelde identiteit terug te vallen voor sociale validatie. Onzekerheid weerhoudt mensen van kritisch te denken, te bang om tegen de stroom in te zwemmen. Ze schrikken weg van ideeën in plaats van ze te confronteren, en gaan onder aan groupthink en echokamers. Het tegengif is nog steeds hetzelfde: goedwillige humor en reflectie, ook van zichzelf, niet morele paniek.</p>

<p>Er is maar één vraag die je moet beantwoorden om dit plaatje te vervolledigen. Waarom geloven ze zó graag dat iedereen in gaming—of in heel de maatschappij—vrouwen en minderheden haat, er niet wilt mee omgaan, ze als inferieur behandelt, zodat speciale programma's en ondersteuning nodig zijn, zelfs als er <a target="_blank" href="http://knowyourmeme.com/memes/notyourshield">tegenbewijs en getuigen zijn</a> die anders beweren? Hier is een hint: <em>Social Justice Warriors</em> hebben altijd enorm veel moeite om een verschillende mening te beschouwen. Maar deze, daar kunnen ze niet over zwijgen.</p>

<p><abbr title="De weg naar de hel bestaat niet alleen uit goeie bedoelingen, de Duivel deelt gratis popcorn uit aan iedere halte.">The road to hell is not just paved with good intentions, the Devil provides free popcorn at every station</abbr>.</p>

<p><span class="spoiler" onclick="this.classList.toggle('active')">En als ge een konijnenpijp binnenrijdt, zorgt dat ge genoeg brandstof meepakt om terug te vallen naar nen <strong><a target="_blank" href="https://www.youtube.com/watch?v=F92l2s_bO-k">hogeren</a> <a target="_blank" href="https://www.youtube.com/watch?v=VQtnqU-mEDc">apoapsis</a></strong>.</span></p>

</div></div>

<div class="c m1"></div>

<div class="wide mt2">
  <a target="_blank" href="https://kerbalspaceprogram.com/en/"><img src="https://acko.net/files/occupy-www-street/kerbal.jpg" alt="Kerbal Space Program"></a>
</div>

<div class="g10 i1 mt1 serif"><div class="pad">

<p>De <a target="_blank" href="https://gitgud.io/gamergate/gamergateop/tree/master/Current-Happenings">media heeft gelogen</a>, en veel mensen die beter zouden moeten weten trapten er in. Het was complete hersenneukerij om opgeleide breinen zo hard te zien falen en hun favoriete bruine hemd voor de vooruitgang uit de kast te halen. 't Is alsof ze nooit het middelbaar ontgroeiden, nog steeds bij de toffe groep proberen te horen terwijl ze het hen kwalijk nemen. Nooit beseffend dat moreel theater een truk is die enkel op de zwakwilligen werkt, en slechts voor een tijd. <a target="_blank" href="https://www.youtube.com/watch?v=LHPqUddXCu8">Anita pls stop</a>.</p>
  
<p><a target="_blank" href="https://archive.is/sxkgY" title="https://pbs.twimg.com/media/CTJXaFIWIAEEP72.jpg:large">Listen</a> en <a target="_blank" href="https://archive.is/Cd0rz" title="https://pbs.twimg.com/media/CTJXY8cWsAA-zQC.jpg:large">Believe</a>, neem u met-ons-of-tegen-ons ideologie en ga eens goed nadenken over wat ge nu juist gedaan hebt. Het internet wordt volledig onderschept, globale handelsverdragen staan op punt om multinationals tot het niveau van staten te eleveren, en men is nog nooit zo bereid geweest om het net te censureren. Deze keer is het niet alleen de bedreiging van terrorisme, pedofilie of kinderporno die gebruikt worden om de massa af te schrikken en critici de mond te snoeren. Het is <em>"cybergeweld tegen vrouwen"</em> en marginale <em>"white supremacy."</em> Weet ge wat een <em>"kapot systeem in stand houdt"</em>? Als ge een populaire misbruikster over haar ondergeknoet mannelijk slachtoffer laat bulldozeren, en dan schaamteloos <a target="_blank" href="https://archive.is/AGZie" title="http://shrink4men.com/2011/01/19/presto-change-o-darvo-deny-attack-and-reverse-victim-and-offender/">de etiketjes omwisselt</a> terwijl ge mondtape opplakt.</p>

<p>Kijk eens rond. Één van de prominentste activisten voor de persvrijheid zit in huisarrest op een ambassade, beschuldigd van de internationaal-monsterlijke misdaad van <a target="_blank" href="https://archive.is/a8br" title="http://georgewashington2.blogspot.de/2010/12/sex-charges-and-arrest-warrant-against.html">consensuele seks maar zonder condoom</a>, namelijk <em>"verkrachting."</em> Seksisme en racisme worden gebruikt als perverse wapens voor omgekeerde slachtoffers door mensen die niks substantieel te bieden hebben maar enorme winst kunnen jagen. De krant die de Snowden leaks bracht heeft zichzelf <a target="_blank" href="https://archive.is/D74Gl" title="http://www.theguardian.com/uk-news/tim-hunt">vrijwillig tot Daily Mail roddel gereduceerd</a>, met wetenschappers beschaamd in plaats van bewonderd voor wat ze bereikt hebben. Het is geen samenzwering wanneer de clickbait blijft vloeien en iedereen gretig in rij staat om mee te doen; ze halen moeiteloos hun portemonnee boven om post-katholieke aflaten te kopen voor de patriarchische erfzonde en de blanke koloniale schuld.</p>

<p>PS: Mensen afwimpelen als kwaad is geen geldig antwoord (dat noemen we "projectie"), noch ze <em>kwasten</em>, <em>lastposten</em> of <em>geïnternaliseerde vrouwenhaters/homofoben/racisten</em> noemen voor dingen die ze helemaal niet gedaan hebben. Social Justice wou dat iedereen zich zou <em>educaten</em>, wel, we gaven ze net wat ze gevraagd hebben—<em>ook het minder positieve</em>—en zij en de media blijven het verkeerd voorstellen. Er was geen groot bedrog vanwege gamers, geen <a target="_blank" href="https://archive.is/KeUR9">reefer madness haatbende</a> die vrouwen uit gaming jaagt omdat ze vrouwen zijn. Enkel ongetalenteerde sjoemelaars, een hoop personages met onduidelijke bedoelingen, een hoop trolls in derde persoon, en veel meer volk dat rond hen probeert werk gedaan te krijgen terwijl ze uitgescholden worden. Ik ben niet <a target="_blank" href="https://www.youtube.com/watch?v=rGIY5Vyj4YM">mad as hell</a>, ik heb er eenvoudigweg genoeg van.</p>

<p>(En ze zullen mij waarschijnlijk rechts noemen, zonder er bij stil te staan dat nationaal socialisme de Volkswagen voor een bepaalde reden gemaakt heeft. Ja ik weet het, Godwin. <a target="_blank" href="https://archive.is/du2rV" title="https://sli.mg/HdI5Uf">Wir haben es nicht gewußt</a>.)</p>

<p><iframe style="width: 100%" height="380" src="https://www.youtube.com/embed/L61ZCN8rrWw" frameborder="0" allowfullscreen="allowfullscreen"></iframe></p>

<div class="c"></div>

<p class="tc mt4 mb0"><img src="https://acko.net/files/occupy-www-street/darmok-and-jalad.jpg" alt="Darmok and Jalad at Tanagra" class="natural spoiler" onclick="this.classList.toggle('active')" /></p>

<p class="tc mt0"><a target="_blank" href="http://knowyourmeme.com/memes/people/ben-garrison"><img src="https://acko.net/files/occupy-www-street/ben-garrison.png" class="flat natural" width="100" height="59" title="No, I'm not Zyklon Ben." /></a></p>

<p class="tc mt3 mb0"><strong>De boom der vrijheid moet van tijd tot tijd gevoed worden<br>met het bloed van vaderlanders en tirannen,</strong></p>

<p class="tc mb5 mt0"><span class="spoiler block" onclick="this.classList.toggle('active')">maar ze hebben het met ketchup geprobeerd.</span></p>

<p class="mt3 mb2"><iframe style="width: 60%; margin: 0 auto;" height="240" src="https://www.youtube.com/embed/EKc-fqsIxGk" frameborder="0" allowfullscreen="allowfullscreen"></iframe></p>

<p class="mt2 mb2"><iframe style="width: 60%; margin: 0 auto;" height="240" src="https://www.youtube.com/embed/5MRUgLf72AM" frameborder="0" allowfullscreen="allowfullscreen"></iframe></p>

<p class="mt2 mb5"><iframe style="width: 60%; margin: 0 auto;" height="240" src="https://www.youtube.com/embed/atazfJqCutQ" frameborder="0" allowfullscreen="allowfullscreen"></iframe></p>

</div></div>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Occupy WWW Street (FR)]]></title>
    <link href="http://acko.net/blog/occupy-www-street-fr/"/>
    <updated>2015-11-30T00:00:00+01:00</updated>
    <id>http://acko.net/blog/occupy-www-street-fr</id>
    <content type="html"><![CDATA[<div class="g8 i2 first serif"><div class="pad">

<h2 class="sub">L'Activisme en Ligne et les Médias à l'Époque de la Justice Sociale</h2>

</div></div>

<div class="c"></div>

<div class="g8 i2 serif"><div>

<p class="outlined lang mt1 mb1 tc"><a href="/blog/occupy-www-street-en/">English</a> - <a href="/blog/occupy-www-street-fr/"><strong>Français</strong></a> - <a href="/blog/occupy-www-street-nl/">Nederlands</a></p>

</div></div>

<div class="g12 serif"><div class="pad">

<blockquote class="mt1 mb2 lift outlined">
  <em class="big">"Je n'ai pas accès aux méthodes d'analyse inhabituelles, et la connaissance particulière que je possède de langue n'a aucune incidence sur les questions sociales ou politiques. […] Pour l'analyse des idéologies […] un peu d'ouverture d'esprit, de l'intelligence normale, et du scepticisme sain suffisent généralement." –&nbsp;NC</em>
</blockquote>

</div></div>

<div class="g10 i1 mt0 serif"><div class="pad">

<p class="outlined">Je m'apporte <a href="/blog/storms-and-teacups/">encore</a> ma police sérieuse. C'est difficile de parler de l'actualité sans inviter des tonnes de contexte, donc je voudrais revenir sur l'histoire récente.</p>

<p class="outlined"><em>Rappel, personne n'aime le messager porteur de mauvaises nouvelles, et l'hérésie appartient au Moyen Âge.</em></p>

<h2 class="mt4">Le Seul Col Bleu dans le Village</h2>

<p>Septembre 2011, la gauche américaine se trouve dynamisée. Le message est direct et vise les grands objectifs: le 99% de la population demande aux 1% avares de tirer sa révérence et d'arrêter de se servir des entreprises comme des matraques. Parc Zuccotti à New York se remplit des activistes qui campent; ils discutent et partagent leur message.</p>

</div></div>

<div class="c m1"></div>

<div class="wide mt2">
  <a target="_blank" href="https://www.youtube.com/watch?v=6yrT-0Xbrn4"><img src="https://acko.net/files/occupy-www-street/lagreca.jpg" alt="Jesse LaGreca instruit Fox News"></a>
</div>

<div class="g10 i1 mt1 serif"><div class="pad">

<p>Là aussi Jesse LaGreca. Avec son look caractéristique <em>American Joe Average</em>, il surprend la plupart des gens, certainement le journaliste de Fox News qui l'approche. Dans son <a target="_blank" href="https://www.youtube.com/watch?v=6yrT-0Xbrn4">interview spontané</a>, jamais diffusée par Fox, LaGreca instruit le pauvre homme et d'y va pleins gaz sans s'arrêter. Il souligne les questions suggestives tout de suite, et remarque que s'il y a une chose sur laquelle les nouvelles—<em>"la machine de propagande dans les médias"</em>—ne veulent pas faire rapport, ce sont eux-mêmes. Que peut-être il est temps pour la démocratie participative qui <em>"n'est pas financée par des caméras de reportage."</em></p>

<p>Un journaliste se présente à expliquer le monde et découvre que cette histoire s'agit de lui-même, du moins en partie. Le refus suit rapidement: <em>"Nous sommes ici pour vous donner la possibilité […] de diffuser tout ce que vous voulez, pour vous offrir de la couverture équitable […] Alors, il y a une exception dans le cas, parce que vous ne pourriez pas partager votre message sans nous."</em></p>

<p>Il riposte: <em>"Certainement. Vous vous souvenez quand Glenn Beck […] a décrit le président comme une personne qui déteste les blancs et la culture blanche. C'était un moment faible dans l'histoire des américains et en quelque sorte vous avez joué un grand rôle dans cette affaire. Donc je suis heureux de voir votre change d'avis en faisant attention à [la reste du pays], plutôt que la marge extrême droite qui préfère totalement détruire la classe moyenne. […] On voudrait voir un petit peu plus de la justice économique, ou de la justice sociale, vous savez, des trucs Jésus-Christ, comme nourrir les pauvres, les soins de santé pour les malades. […] Je m'amuse beaucoup que les gens tiennent la constitution quand ils crient aux soldats homosexuels, mais ils ne peuvent pas comprendre qu'un système de santé à la recherche du profit ne marche pas. […] Si nous voulons que le président fait plus, parlons sur un niveau qui fonctionne en lieu de lui demander pour son certificat de naissance, […]"</em></p>

<p><em>Shots fired, critical hit, no chance of recovery.</em> Aucune surprise cet entretien a été "oublié," et aucune surprise quelqu'un l'a <a target="_blank" href="https://archive.is/aUFoA" title="http://observer.com/2011/10/exclusive-occupy-wall-street-activist-slams-fox-news-anchor-in-un-aired-interview-video/">dévoilé</a> comme <em>trop extrême pour Fox news</em>. Les manchettes prévisibles nous ont demandé s'il était <em>L'Homme Le Plus Intelligent De Wall Street</em> et nous expliquaient que <em>Jesse LaGreca Continue à Détruire La Partialité Des Médias de Occupy Wall Street</em>. Tous des journalistes pressés de montrer qu'<em>ils</em> offriraient une voix à n'importe qui.</p> 

<p>LaGreca lui-même <a target="_blank" href="https://archive.is/tEjrc#selection-495.650-495.893" title="http://observer.com/2011/10/jesse-lagreca-on-abcs-this-week/">l'a évoqué</a> en passant à l'antenne: <em>"La réalité c'est que je suis le seul col blue que vous verrez dans le journal du dimanche… les nouvelles politiques… peut-être jamais. Et je pense que c'est très indicatif des échecs des médias à rapporter les nouvelles les plus importantes…"</em> Il est interrompu immédiatement, <em>"Nous essayons de faire de notre mieux, Jesse."</em></p>

<p>Lorsqu'on lui demande s'il y a une exigence politique qui peut résumer Occupy, quelque chose <em>"[qu']il s'agit de faire,"</em> il prend le virage curieux:</p>

<p><em>"Je pense que tout le mouvement s'agit de la justice économique. En fait pour moi — et je ne parle pas au nom de Occupy Wall Street, je vous donne mon opinion personelle — je croix que c'est une question des droits économiques, et je croix que c'est une question des droits sociaux, et de la justice sociale. Et pour les gens qui sont fâchés que le mot “sociale” est utilisée avec le mot “justice,” je vous invite de relire la constitution."</em></p>

<p>Bien sûr. J'ai fait mon devoir et j'ai <a target="_blank" href="https://archive.is/fQr94" title="https://melissafong.wordpress.com/2014/04/04/why-suey_park-wont-enact-the-labour-women-of-colour-dont-owe-you-an-education/">entrepris le labeur de m'éduquer</a>. La Constitution des États-Unis ne fait en aucun cas référence à quelque chose de sociale concernante la justice. Apparemment, les gens qui l'ont écrit pensaient que simplement <em>"établir de la Justice"</em> suffisait. Elle ne classe pas les citoyens par ordre de privilège, elle ne définit pas les pouvoirs relativement à l'oppression, et fait aucune allusion à <em>punching up</em>. Ça c'est la justice sociale aujourd'hui n'est-ce pas? Ou entendait-il des <em>trucs Jésus-Christ</em>? Je ne suis pas un avocat de droit constitutionnel, n'hésitez pas à me corriger.</p>

<p>Plus gênant, sur le sujet de l'inspiration par d'autres manifestations, en Grèce et en Europe et ailleurs, LaGreca affirme que <em>"nous ne suivions vraiment personne."</em></p>

</div></div>

<aside class="g5 mt1"><div class="pad">
  <a target="_blank" href="http://www.webcitation.org/63DZ1nIDl" title="http://www.adbusters.org/blogs/adbusters-blog/occupywallstreet.html"><img src="https://acko.net/files/occupy-www-street/adbusters-occupy.jpg" class="fit" /></a>
</div></aside>

<div class="g7 serif"><div class="pad">

<p>Occupy a peut-être connu une croissance organique de groupes existants, mais il a été galvanisée par <a target="_blank" href="https://archive.is/55OJB" title="https://www.adbusters.org/blogs/adbusters-blog/million-man-march-wall-street.html">un blog</a> et <a target="_blank" href="http://www.webcitation.org/63DZ1nIDl" title="http://www.adbusters.org/blogs/adbusters-blog/occupywallstreet.html">un bulletin</a> diffusé aux abonnés de Adbusters, <em>"vous 90,000 rédempteurs, rebelles et radicaux là-bas,"</em> qui a déclaré qu'un <em>"changement mondiale des tactiques révolutionnaires est en cours,"</em> et leur a demandé s'ils étaient prêts à avoir leur propre <em>"moment Tahrir."</em> Les 267 commentaires ont reçu ce message avec enthousiasme et engagement.</p>

<p>LaGreca n'était pas inconnu non plus, il était contributeur de trois ans chez Daily Kos. Les gens là <a target="_blank" href="https://archive.is/6isVY" title="http://www.dailykos.com/story/2011/10/05/1023035/--UPDATE-Jesse-LaGreca-taking-on-Hannity-NY-Observer-MoT-Smartest-Man-on-Wall-Street">l'ont encouragé</a> pour <em>"[refuter] le stéreotype des médias de ces ne0-hippies obtuses privés de leurs droits"</em> et répandaient la bonne parole en apprécièrent le spectacle: <em>"Je devine que Hannity a peur. Comme il faut!"</em></p>

</div></div>

<div class="g10 i1 mt1 serif"><div class="pad">

<p>Ceci n'est pas un acte d'accusation au sujet de Mr. LaGreca, il me paraît plutôt plein de bon sens, et assez convaincant. Mais il y a quatre ans.
  Malgré le ample temps d'antenne que sa nouvelle <a target="_blank" href="http://knowyourmeme.com/memes/streisand-effect">célébrité-à-Streisand</a> lui a rendu, et le désir des journalistes de faire de mieux, la situation ne s'est pas améliorée, ni chez Fox News ni chez les autres organes de presse qui se définissent par l'opposition.</p>

<p>S'il y a une chose que les médias aiment, elle est l'exception qui confirme la règle: on ne peut pas galvaniser la population sans accès aux réseaux et plateformes existantes. Quand vous vous engagez dans une guerre contre les médias, ils vous <a target="_blank" href="https://archive.is/IyrOb" title="http://www.tv.com/shows/black-mirror/community/post/black-mirror-15-million-merits-review-138498157166/">transforment en produit de divertissement</a>, et les masses vont l'adorer. Vous peut-être aussi.</p>


<h2 class="mt3">Les Activistes sont Morts</h2>

<p>Qu'est-ce qui c'est passé? Juin 2012, forum communautaire et association à but non lucratif <em>OccupyWallSt.org</em> annonce que <a target="_blank" href="https://archive.is/KhRmS" title="http://occupywallst.org/article/reports-occupys-death-have-been-greatly-exaggerate/">Les Rapports de la Mort de Occupy ont été Grandement Exagérées</a>.</p>

<p class="tc"><a class="block" target="_blank" href="https://archive.is/KhRmS" title="http://occupywallst.org/article/reports-occupys-death-have-been-greatly-exaggerate/"><img src="https://acko.net/files/occupy-www-street/occupy-logo.png" alt="OccupyWallSt.org logo" class="natural" /></a></p>

<blockquote>

<p><em>"Depuis l'expulsion des manifestants à Freedom Plaza en novembre dernier, il est devenu un cliché des médias de rendre compte de la “mort de Occupy.” Articles ont été publiés dans l'ensemble du Web, qui racontent allègrement le second souffle de Occupy échoué, ce “Printemps Américain” terne, et la grève générale de mai qui n'a pas exactement arrêté le système.</em></p>

<p><em>Ce n'est pas une surprise que les médias générales sont avides de faire rapport sur la fin présumée de Occupy. Même si vous ignorez que les médias détenus par l'entreprise ont bien le désir de ne jamais voir la victoire des mouvements comme Occupy, la règle du pouce est qu'ils ont besoin d'un récit dramatique pour tout ce qu'ils rapportent. Chaque histoire doit avoir un arc captivant avec un début, un milieu et une fin."</em></p>

</blockquote>

</div></div>

<div class="g12 mt0 serif"><div class="pad">

<p class="tc mt0 mb0"><a target="_blank" href="https://www.reddit.com/comments/mkuww"><img src="https://acko.net/files/occupy-www-street/casually-pepperspray.jpg" alt="Casually pepper spraying cop" class="fit" /></a></p>

</div></div>

<div class="g10 i1 mt0 serif"><div class="pad">

<blockquote>

<p><em>"Selon la presse […] beaucoup de gens ont répondu au appel, [mais] le mouvement n'a pas avancé dramatiquement jusqu'à la police a été photographié en s'attaquant aux manifestants non-violents et en appliquant du gaz poivre. Sa disparition précoce était quand la police a démantelé violemment les divers campements. Maintenant, sauf quelques grincheux qui ne peuvent pas comprendre que Occupy est fini, tout ce qu'il reste est sa rhétorique populiste du 99%—qui a été adopté consciencieusement par les organisations Democratiques comme MoveOn.org pour ravitailler la machine d'élection Obama."</em></p>

</blockquote>

<p>Tant pis. Il y a quelque chose à la télé ce soir? Un débat présidentiel quoi?</p>

<blockquote>
<p><em>"Ce récit populaire […] a ainsi réussi a se faire absorber par ceux qui sont encore actives dans le mouvement Occupy. […Les réunions] ressemblent parfois des groupes de soutien publics. Sur Internet, des articles vaguement Paul Krugman-esques sont publiés et republiés en autosatisfaction qui applaudissent Occupy pour “au moins déplacer le dialogue public,” […] pour rappeler à l'autre qu'au moins Occupy a eu un peu d'effet."</em></p>
</blockquote>

<p>Super chouette. L'article raconte en longueur continue que les américains voient leur histoire comme <em>"quelque chose les autres ont fait jadis (surtout les hommes blancs de la classe supérieure)"</em> et oublient <em>"[qu']il a fallu des centaines de milliers de personnes—notamment les jeunes, les femmes, et les hommes ouvriers–pour supporter et diffuser les idées de la démocratie. […] Nous voyons Occupy comme un spectacle […] de gens qui sont très différents de nous. […] Les célébrités et artistes, les anarchistes de style bloc noir, et les maires despotiques sont les personages de ce drame, [qui luttent] pour les manchettes sur la scène de nos villes sales."</em></p>

<blockquote>
<p><em>"La vraie magie de Occupy était qu'il a rejeté tout ça. Personne n'avait plus de pouvoir que quiconque, […] personne dans Occupy donnait de la merde que nous nous sommes ignorés par les médias traditionnels. Nous n'avons pas besoin des dilettantes au Time Magazine pour nous féliciter de notre capacité à protester. […] Le but de Occupy était de se réunir dans une communauté des égaux, la revendication d'un avenir différent de celui qu'ils nous ont donné, et de raviver une tradition de progrès démocratique […] Il était un effort d'explorer de nouvelles façons d'interagir avec les autres."</em></p>
</blockquote>

</div></div>

<div class="c m1"></div>

<div class="wide mt3">
  <img src="https://acko.net/files/occupy-www-street/occupy-vancouver.jpg" alt="Occupy Vancouver" />
</div>

<div class="g8 i2 mb1">
<p class="credit"><a target="_blank" href="http://www.vancouverobserver.com/blogs/thescene/2011/10/15/occupy-vancouver-protesters-photos">1</a> <a target="_blank" href="http://weheartit.com/entry/group/210433">2</a> <a target="_blank" href="http://www.vancouverobserver.com/politics/news/2011/11/06/what-are-occupy-vancouvers-demands">3</a> <a target="_blank" href="https://skepticedge.wordpress.com/">4</a> <a target="_blank" href="http://jodieponto.com/photoblog/2011/10/16/occupy-vancouver/">5</a></p>
</div>

<div class="g10 i1 mt0 serif"><div class="pad">
  
<p>L'auteur insiste sur le fait que <em>"Occupy n'était pas une mode passagère ou une chimère de gauche."</em> D'accord. Sauf le <em>"sentiment dans l'air,"</em> les effets énumérés sont que <em>"plus de 7200 manifestants de Occupy sont arrêtés dans les États-Unis"</em> et beaucoup d'eux sont <em>"battus et torturés."</em> Occupy a été diabolisé puisque <em>"les médias ont été poussés à rendre compte […] sous un jour défavorable."</em> Quoi d'autre?</p>

<blockquote><p><em>"Il faut se souvenir que “occupy” était […] un appel à l'action, et non pas l'action elle-même. [Il] était utile pour faire sortir dans la rue les individus et les organisations précédemment isolées ou axées sur un seul problème. […] Ce qui est important, c'est la grande communauté d'opposition, étant formé à travers de nombreux obstacles sociaux, et ceux qui détiennent le pouvoir ont beaucoup de peur."</em></p></blockquote>

<p>Ne prêtez pas attention aux attitudes à la fin, apercevez-vous de quelque chose beaucoup plus révélatrice.</p>

<p>Le résultat de Occupy, selon un article qui veut prouver sa valeur durable, est que 7200 personnes se sont fait entendre comme <em>des victimes et des martyrs</em>, supportées par la résistance passive et idealiste d'ailleurs. Ils ont glissé vers une non-pertinence déprimée, car ceux qui devraient faire rapport au service de l'interêt public ont donné une impression incorrecte. Ils l'ont trouvé acceptable.</p>

<p>Les meilleurs mensonges sont à moitié vrai. Ce rituel-de-absolument-non-deuil collective était une part de vérité, dûr comme fer, emballé dans l'auto-illusion narcissique. Peut-être il est le syndrome de Stockholm. Ceci sont les espèces Pokémon évolués de la culture de la victime, où la victimisation est célébrée, parce qu'elle peut fournir du pouvoir énorme.</p>

<h2 class="mt3">Burgers et Frites Maison au Ketchup</h2>

<p>Je ne crois pas l'histoire officielle, ni des médias ni du mouvement, parce que je suis allé voir moi-même en Vancouver. Je n'entends pas la <em>Verité 9/11</em> ou les activistes de l'herbe: ils appartiennent au territoire, où iraient-ils autrement? Non, je m'apercevais de deux groupes en particulier.</p>

<p>Premièrement, il y avait les redresseurs idealistes au lait qui jouaient au <em>microphone humain</em>—aussi facilement que chez Parc Biscotti!—avec les mecs et nanas trop à la mode pour être pauvre. Cela m'a paru sinistre: comme d'autres endroits de Occupy, le matériel audio était interdit et alors ils étaient contraints de se repeter lentement, comme le chant de mantras. Complémentaire était  le manque général d'orientation et modération. Ajoutez des gens qui ont tant de sentiments qu'ils ne savent plus ce qu'ils doivent en faire, et il ne mène à rien très lentement, comme Twitter dans le Matrix.</p>

</div></div>

<div class="c"></div>

<div class="wide mt1">
  <a target="_blank" href="http://www.matthewjohnburgess.com/essays-on-writing/" class="block"><img src="https://acko.net/files/occupy-www-street/human-mic.jpg" alt="The human microphone" /></a>
</div>

<div class="c"></div>

<div class="g10 i1 mt2 serif"><div class="pad">
  
<p>Je ne voyais pas de discussion ouvert pour le débat éclairé, pas de rassemblement communautaire, c'était une tribune pénible aucune personne ordinaire n'écouterait plus que cinq minutes. Ils avaient entendu parler de l'activisme, ils même savaient comme celui-ci se présente et sonne, mais ils ne le disaient que du bout des lèvres. Exactement comme chez des plusieurs de livestreams d'ailleurs que j'ai vu.</p>

<p>Deuxièmement, les vrais sans-abris de la ville: les chômeurs et les inemployables, les malades mentaux, les toxicomanes chroniques ou des autres rejeté par la société, qui errent dans l'espace qu'ils occupent déjà tous les jours. Combinez les deux, quelle surprise, les grands principes d'égalité, des privileges, de diversité, d'habilisme, d'expression libre et d'appropriation partagée sont testés. Les jeunes qui campent aux festivaux d'été avec des toilettes désinfectées et des robinets en abondance apprennent qu'un endroit public dans l'hiver est moins agréable. Par ailleurs, en cas de conflit ou de crime inévitable, probablement il n'est pas très judicieux de se fonder sur l'arrêt de ceux qui mettent les mots <em>"triggered"</em> et <em>"culture du viol"</em> dans la bouche comme des friandises.</p>

</div></div>

<div class="c"></div>

<div class="wide mt2">
  <a target="_blank" href="https://www.saybrook.edu/newexistentialists/posts/10-31-11"><img src="https://acko.net/files/occupy-www-street/occupy-empathy.jpg" alt="Scene from Occupy" /></a>
</div>

<div class="c"></div>

<div class="g10 i1 mt2 serif"><div class="pad">

<p>Au lieu d'affronter la réalité, ils ont adopté la <a target="_blank" href="http://www.webcitation.org/69wFEoszj" title="http://www2.timesdispatch.com/news/rtd-opinion/2011/nov/04/tdopin02-hinkle-ows-protesters-have-strange-ideas--ar-1433590/">liasse progressive</a>, qui <em>"encourage les femmes et les groupes traditionnellement marginalisés [à] prendre la parole devant les hommes, surtout les hommes blancs."</em> Si vous creusez un peu, vous entendez dire qu'il y avait <a target="_blank" href="https://archive.is/N9as3" title="https://sli.mg/Vkzcmj">de friction</a> de <em>"réunions déraillés par les femmes et les minorités obsédées par la politique d'identité. […] [Les plans] ont presque toujours rencontré des objections des féministes, des activistes homosexuels, noirs, latinos, etc. Souvent ces personnes n'avaient même pas des préoccupations spécifiques. […] Ils se sont simplement levés pour se plaindre du manque de diversité, de la victimisation de leur groupe d'identité spécifique, etc. […] Des gars blancs intelligents, capables, organisés ont quitté la manifestation. Ils sont partis frustrés."</em></p>

<p>Vous trouverez beaucoup d'histoires des brouilles et d'abus, ils ont fourni une matière fertile pour les médias, sociaux ou non. Ce qui s'est vraiment passé n'était considéré que rarement pertinent, et de tels problèmes sont devenus plus importants que le système financier et économique, ou les établissements qui le soutiennent. Apparemment on devrait <a target="_blank" href="https://archive.is/irNvT#selection-823.91-823.139" title="http://www.dominionpaper.ca/articles/4268">affronter la violence liée au genre dans le mouvement Occupy</a>: <em>"Exactement ce qui s'est passé est incertain. Les uns prétendent qu'il y avait une tentative de viol. Les autres prendent l'incident à la légère, qu'un homme ivre a envahi l'espace vital d'une jeune femme."</em> Pensez-vous que cette distinction peut être importante?</p>
  
<p>Les médias juste-de-vue-sociale <a target="_blank" href="https://archive.is/SFZiU" titke="http://www.xojane.com/issues/occupy-wall-street-rape">ont dégagé l'hyperbole</a>, que les hommes se souviennent <em>"de violer personne, jamais"</em>—merci xojane—et que <em>"les espaces réservées aux femmes sont construites pour faciliter des conversations avancées concernantes les enjeux qui affectent les femmes (et, idéalement, quiconque se ne considérant pas d'homme) sans être interrompu sans cesse par la nécessité d'éduquer des autres dans le rassemblement."</em></p>

<p>En d'autres termes… Les gars, restez dehors et taisez-vous. Les filles, pas de questions. Bien fait la justice sociale, t'as apporté tous ce que tu détestes à ta petite utopie, et les autres ont été chassés.</p>

<p>C'est facile de désigner du doigt les personages marginales déjantés, avec leurs cheveux moches et leurs drôles de verres. Si vous êtes Stephen Colbert, vous pouvez <a target="_blank" href="https://archive.is/EymTY" title="http://newsbusters.org/blogs/pj-gladnick/2011/11/05/colbert-ows-interviewee-ketchup-reveals-self-wacko">vous moquer d'eux devant des millions sans merci</a> en gagnant beaucoup de points. Le vrai problème c'est la masse d'activistes eux-mêmes et les médias auquel ils croient. Ils souhaitent se dérailler par des enjeux qui ils eux-mêmes reconnaîtrent comme un microcosme d'une communauté humaine. L'erreur c'est d'attribuer une signification universelle aux incidents isolés pendant que l'on ignore les éléphants dans le salon. C'est une maladie concentré dans les médias, mais pas seulement là.</p>

<p>C'est difficile de prendre au sérieux une <em>Conversation Importante sur La <a target="_blank" href="https://archive.is/XN8Vh" title="http://www.fudco.com/chip/deconstr.html">Déconstruction des Enjeux</a></em>, si la justice sociale n'a jamais appris à socialiser.</p>

</div></div>

<div class="c"></div>

<div class="wide mt1">
  <a target="_blank" href="https://archive.is/EymTY" title="http://newsbusters.org/blogs/pj-gladnick/2011/11/05/colbert-ows-interviewee-ketchup-reveals-self-wacko"><img src="https://acko.net/files/occupy-www-street/ketchup.jpg" alt="Burgers and Fries and Ketchup" /></a>
</div>

<div class="c"></div>

<div class="g10 i1 mt2 serif"><div class="pad">
  
<h2>/Chomsky/ Avait Raison</h2>

<p>Dans <em>"La Langue et La Responsabilité"</em> (1979), Noam Chomsky converse quelque peu en eloquence. Il explique en grande partie ses théories de niche de la linguistique. Mais le premier chapitre <em>"La Politique"</em> s'ouvre avec des sujets assez différents, et des idées qui retourneraient ensuite dans <em>"La Fabrication du Consentement: L'Économie Politique des Médias de Masse"</em> (1988). Lorsqu'on lui demande quelle est la relation entre son méthodes et son écrits politiques plus célèbres:</p>

</div></div>

<div class="c m1"></div>

<aside class="g8"><div class="pad">
  <a target="_blank" href="http://www.vulgartrader.com/post/32392205367/hipster-chomsky"><img src="https://acko.net/files/occupy-www-street/chomsky.jpg" alt="Noam Chomsky" /></a>
</div></aside>

<div class="g4 serif"><div class="pad">

<blockquote class="flush">
<p><em>"Je n'ai pas accès aux méthodes d'analyse inhabituelles, et la connaissance particulière que je possède de langue n'a aucune incidence sur les questions sociales ou politiques. […] Pour l'analyse des idéologies […] un peu d'ouverture d'esprit, de l'intelligence normale, et du scepticisme sain suffisent généralement."</em></p>
</blockquote>

</div></div>

<div class="g10 i1 mt1 serif"><div class="pad">

<blockquote>
<p><em>"Prenons le rôle de l'intelligentsia dans une société comme la nôtre. Cette classe sociale, qui comprend les historiens et les autres universitaires, les journalistes, les commentateurs politiques et ainsi de suite, s'engage pour analyser et présenter une image des réalités sociales. […] Ils servent de médiateurs entre les faits sociaux et la masse de la population: ils créent la justification idéologique pour la pratique sociale. Regardez l'œuvre des spécialistes sur les questions d'actualité et comparez leur interpretation avec se qui s'est passé, comparez ce qu'ils disent avec les faits. Vous trouverez souvent des grandes divergences, assez systématiques […]."</em></p>

<p><em>"Avec un peu d'effort et dévouement, chacun qui veut sortir du système […] peut voir à travers les méthodes de distorsion. […] Chacun est capable de le faire. […] Souvent mal exécutées, […] des analyses sociales et politiques sont produites pour défendre des intérêts spécifiques au lieu de rendre compte des événements réels."</em> <em>(p3–4)</em></p>
</blockquote>

<p>Ensuite il parle de la situation précise dans les États-Unis. Il compare les faits enregistrés avec les explications acceptées des affaires comme le maccarthysme, la révolution des étudiants, le mouvement des droits civiles, la guerre de Vietnam, entre autres choses. Il aborde les opérations américaines à la maison et à l'étranger, légitimes ou illégitimes. Le thème dominant est assez évident:</p>

<blockquote>
<p><em>"Ici dans les États-Unis il y a une uniformité idéologique d'une ampleur surprenante pour un pays aussi complexe. Fondamentalement, deux raisons l'expliquent. Premièrement, il y a la homogénéité idéologique notable de l'intelligentsia américaine en général, qui dévient rarement d'une des variantes de l'idéologie état-capitaliste (libérale ou conservatrice), un fait qui lui-même demande une explication. Deuxièmement que les médias de masse sont des institutions capitalistes. […] Il n'est guère étonnant que ces institutions reflètent l'idéologie des intérêts économiques dominants."</em> <em>(p9)</em></p>
</blockquote>

<p>Un article principal du <em>New York Times</em> de 1975, une rétrospective de la guerre de Vietnam, explique qu'il y a ceux qui pensent que <em>"la guerre […] avait pu être menée différemment"</em> et ceux qui pensent <em>"qu'un Vietnam du Sud non-Communiste réalisable était toujours un mythe."</em> Chomsky fait tapis:</p>

<blockquote>

<p><em>"Ils ne mentionnent même pas la possibilité logique d'une troisième position: notamment, que les États-Unis n'avaient pas […] le droit juridique ou moral, de recourir à la force dans les affaires internes du Vietnam. […] À noter que quand le <em>Times</em> fixe le spectre du débat idéologique, la position du plupart du mouvement de paix est simplement exclu de toute considération. Pas qu'elle est fausse, mais plutôt impensable, inexprimable. […]</em></p>

<p><em>Voici une excellente illustration de la fonctionnement de la propagande dans une démocratie. Un état totalitaire énonce simplement la doctrine officielle—clairement, explicitement. […] on exprime son opposition à ses propres risques et périls. Dans un système de propagande démocratique, personne n'est punis (en théorie) pour faire objection au dogme officiel. En fait, la dissidence est encouragée.</em></p>

<p><em>Ce que le système tente de faire est de fixer les limites des pensées possibles: les supporteurs de la doctrine officielle à une extrémité, et les critiques à l'autre—vigoureux, courageux, et tenue en haute estime pour leur indépendance de jugement. Les faucons et les colombes. Mais nous découvrons qu'ils ont certaines d'hypothèses tacites en commun, et que ce sont ces hypothèses qui sont vraiment cruciales.</em></p>

<p><em>Sans doute un système de propagande est plus efficace quand il insinue ses doctrines au lieu de les affirmer, […] Plus le débat est vigoureux, plus les doctrines de base du système de propagande sont instillées, implicitement assumées de tous les côtés. Alors le prétexte élaboré que la presse est une force dissidente critique—peut-être trop critique pour la santé de la démocratie—alors qu'en fait elle est presque complètement subordonnée aux principes de base du système idéologique."</em> <em>(p36–39)</em></p>

</blockquote>

<p>C'était les années 70. À quoi est-ce que ça pourrait ressembler dans un monde connecté?</p>

<h2 class="mt3">Ne Me Traque Pas Mec</h2>

<p>En 2006, Gawker Media a lancé les <a target="_blank" href="https://archive.is/JM9mj" title="http://gawker.com/160338/introducing-gawker-stalker-maps">Gawker Stalker Maps</a>, un site qui demandait les visiteurs de partager des observations des célébrités en public. Les stars de Hollywood ont vu les paparazzi suivis par des gens encore plus étranges d'Internet, et ils n'en ont pas&nbsp;ri.</p>

<p class="tc"><a target="_blank" href="https://www.youtube.com/watch?v=2-avakrRUaU" class="block"><img src="https://acko.net/files/occupy-www-street/gawker-stalker-kimmel.jpg" class="natural" /></a></p>

<p>Lorsqu'elle est appelé pour défendre cette pratique un an plus tard, rédactrice Emily Gould <a target="_blank" href="https://www.youtube.com/watch?v=2-avakrRUaU">fonce vers l'or à la gymnastique mentale</a>. Malgré la question initiale des renseignements <em>"si vite que vous les pouvez envoyer,"</em> elle prétend que la carte n'est mis à jour que lentement. Elle rejette en riant des preuves d'avoir publié des mensonges, d'être de <em>"journalisme citoyen"</em> que <em>"tout le monde sait qu'il n'est pas vérifié,"</em> bien qu'une grande partie du contenu soit rédactionnelle. Elle se prend les pieds et ne récupère pas élégamment, quand elle parle du <em>"stalking… je veux dire… ce n'est pas du <em class="normal">vrai</em> stalking"</em> parce qu'il n'y a aucune expectative de se ne pas faire remarquer en public, et il reconnaît que les vedettes ne sont que des gens normaux. Puis elle demande immédiatement si les célébrités <em>"ne sont pas protégés des pierres jetées par des tas d'argent,"</em> est sifflée et recule <em>"[qu']il n'est pas convenable de répandre des mensonges sur les gens,"</em> invalidant tout ce qu'elle a dit.</p>

<p>Kimmel en jette aussi une remarque drôlement anachronique: <em>"Je ne comprends pas pourquoi quelqu'un voudrait acheter de la pub sur un site Web."</em></p>

<p>Une occasion rare. Les célébrités distantes sont humanisées, les médias anciens demandent des explications des jeunes, et la jolie tête parlante se montre opportuniste, prise en flagrant délit. Une belle opportunité de prouver les médias savent mieux, non? Aaron Sorkin l'a vu aussi et a inclu ce moment d'enseignement dans un épisode de <em>"The Newsroom,"</em> <a target="_blank" href="https://www.youtube.com/watch?v=SWjREXKdXbM">en copiant du dialogue</a>:</p>
  
</div></div>

<div class="c"></div>

<div class="wide mt1">
  <a target="_blank" href="https://www.youtube.com/watch?v=SWjREXKdXbM"><img src="https://acko.net/files/occupy-www-street/the-newsroom.jpg" alt="The Newsroom" /></a>
</div>

<div class="c"></div>

<div class="g10 i1 mt1 serif"><div class="pad">

<p>Sauf qu'évidemment, tous les arêtes vives ont bien été supprimées. Au lieu d'une rédactrice d'un tabloïd Internet, il y a un type Zuckerberg en rondeur qui nous vend son entreprise en technologie et son app. Ses réfutations semblent plus authentiques, et il est renversé par les accusations soudaines, pas invité expressément pour les aborder. Les célébrités ne sont pas représentées, ils sont ailleurs, en train d'être traquées et assassinées. Il est un technocrate oublieux qui nuit à la société avec son autisme insensible, elle est la journaliste fatale qui lui demande le compte rendu.</p>

<p>Vous voyez comment une histoire qu'il s'agit complètement des médias a été traduite en quelque chose entièrement différente, sans changer la plupart des mots? Tout depend du détaillage et de la présentation. L'excuse qu'ils ne peuvent pas être entièrement réalistes ou littéraux ne s'applique pas. Je trouve tout ça un peu trop <em>opportun</em> pour promouvoir quelques certains récits. Il confond les sociopathes qui dirigent certaines entreprises avec les personnes qui maintiennent la technologie, avec un vernissage subtil de <em>nerd bashing</em>.</p>

<p>Il peut être un bon exemple de ce dont Chomsky a parlé: quand ils présentent une interprétation des faits, les portiers américains appliquent une optique qui éclaire seulement un cadre très étroit et prévisible, tout en précisant au contraire. Comme vous montrer <em>comment on fait vraiment les nouvelles</em>, un <em>prétexte élaboré que la presse est une force dissidente critique</em>.</p>

</div></div>

<div class="c"></div>

<div class="wide mt1">
  <a target="_blank" href="https://www.youtube.com/watch?v=SWjREXKdXbM"><img src="https://acko.net/files/occupy-www-street/the-newsroom-2.jpg" alt="The Newsroom" /></a>
</div>

<div class="c"></div>

<div class="g10 i1 mt1 serif"><div class="pad">

<p>Ou il peut être un episode médiocre d'un programme TV défaillant. Mais je suis sûr qu'il y a déjà de nombreux d'articles d'opinion et commentaires qui le discutent en détail de profondeur douloureuse.</p>

<p>De toute façon, en essayant de rendre la réalité dans leur fantaisie, ils l'ont refait à leur propre image. Ils le font souvent. Pas seulement ceux qui <a target="_blank" href="https://archive.is/t4EVf" title="http://newsdiffs.org/diff/934341/934454/www.nytimes.com/2015/07/11/technology/ellen-pao-reddit-chief-executive-resignation.html">écrivent de la fiction</a>.</p>

<h2 class="mt3">Célébrez la Diversité</h2>

<p>Où est-ce qu'on l'aurait pu voir une fois de plus récemment? Regardons. Qu'est-ce que vous faites quand vous êtes journaliste notable de divertissement, disons, les jeux vidéo, qui apprend que les revues professionnelles sont en grande partie obsolète dans un monde de YouTube, Twitch, Twitter, Reddit, etc.?</p>

<p>Peut-être vous auriez une crise existentielle quand vous vous rends compte d'avoir être juste un <a target="_blank" href="https://archive.is/cjyTj">mégaphone ivre</a> pour les intérêts commerciaux institutionnels, et vous <a target="_blank" href="http://deepfreeze.it/article.php?a=unfair#alexander">deviendriez un porte-parole pour vos propres intérêts</a> au lieu. Partagez sur Twitter <em>"Fuck ethics get money,"</em> et donnez des conférences dans lesquelles vous ne <em>"prétendez pas d'être impartiale."</em> Mettez en vedette les gens et les projets que vous aimez, promouvez des causes politiques et sociales que vous pensez être importantes, et choisissez les exclusivités vous-mêmes plutôt que les attender. Tout ça pendant que vous rejoignez la course mondiale du <a target="_blank" href="http://deepfreeze.it/article.php?a=quickdirty">piège à clics moderne</a>.</p>

<p>Si une clique assez grande le ferait, il pourrait apparaître une renaissance des indies. Mais elle ne nourrirait qu'une sélection d'idées étroite, et resterait seulement accessible aux ceux qui ont les contacts nécessaires et qui disent les bonnes choses. Si on le ferait dans une industrie qui vaut des milliards, il pourrait créer un ressentiment croissant. Surtout s'il y aurait <a target="_blank" href="http://www.tiki-toki.com/timeline/entry/343871/Corruption-consumer-hate-and-bad-journalism-in-games-journalism/">des années de reportage tendancieux, des conflits d'intérêts, et de promotion des agendas politiques</a>, avec plusieurs carrières goudronnées et à plumes, à cause des rumeurs irresponsables et des mensonges. <a target="_blank" href="https://archive.is/U0pBQ" title="https://paxdickinson.wordpress.com/2014/10/22/moral-panics-and-the-death-of-fun/">Dans l'hypothèse</a>, <a target="_blank" href="https://archive.is/68A2y" title="http://www.escapistmagazine.com/articles/view/video-games/gamergate-interviews/12397-Brad-Wardell-GamerGate-Interview.2">bien sûr</a>.</p>

</div></div>

<div class="c"></div>

<div class="wide mt1">
  <a target="_blank" href="http://deepfreeze.it"><img src="https://acko.net/files/occupy-www-street/ramiel.jpg" alt="Ramiel" /></a>
</div>

<div class="c"></div>

<div class="g10 i1 mt1 serif"><div class="pad">

<p>Dans une telle situation, imaginez-vous que des indications émergent qu'une des favoris des médias est en vrai une personne assez déconcertante. En se profilant comme enfant modèle et une activiste progressiste et développeur de jeux, elle se manipule et ment à travers de sa carrière et ses rélations. Particulièrement il s'agit d'un <a target="_blank" href="https://thezoepost.wordpress.com">avertissement et une description de maltraitance émotionnelle</a>, prouvée, par exemple, avec de nombreuses citations de Facebook. Les détails sont assez lubriques, notamment l'infidélité avec un ensemble de <em>Cinq Gars</em> dans l'industrie, incluant son chef et un rapporteur chez Kotaku. En outre, elle présente les Habits Neufs de l'Empereur, parce que son œuvre consiste principalement d'un seul livre-à-la-carte fait avec Twine qui passerait à peine comme projet de semestre de la première année. Il a été salué plutôt pour son sujet—la dépression—que pour sa réalisation.</p>

<p>Imaginez qu'en réponse, d'autres connaissances sortent de l'ombre pour décrire <a target="_blank" href="http://gamergate.wikia.com/wiki/Zoe_Quinn">d'autres choses qu'elle pourrait avoir fait</a>. Comme <a target="_blank" href="https://archive.is/zSifp" title="http://i.imgur.com/moFXvoa.jpg">faux témoignage</a> qu'un forum de gens déprimés l'a harcelée, <a target="_blank" href="http://knowyourmeme.com/photos/813691-quinnspiracy">dévoiler des renseignements personels</a> d'un organisme de bienfaisance rival, <a target="_blank" href="https://www.reddit.com/r/KotakuInAction/comments/2gtqoq/the_story_of_the_fine_young_capitalists_and_the/">convaincre ses amis de média</a> de les mettre sur la liste noire, saboter <a target="_blank" href="http://knowyourmeme.com/photos/815901-quinnspiracy">un programme de télé-réalité dans laquelle elle figure</a>, collecter de fonds à un événement qui n'a <a target="_blank" href="https://archive.is/HYm63" title="http://apgnation.com/articles/2014/09/09/6977/truth-gaming-interview-fine-young-capitalists">jamais eu lieu</a>, et en général d'être ressentie par ses contacts précédents.</p>

<p>Plus important, imaginez que ce scandale parvient à la révision ou découverte <a target="_blank" href="https://archive.is/3z33G" title="https://www.reddit.com/r/Games/comments/2ejs7v/gaming_journalists_patricia_hernandez_of_kotaku/">d'autres conflits d'intérêts</a> et <a target="_blank" href="https://archive.is/fEfRB" title="http://kotaku.com/a-different-way-to-respond-to-a-rape-accusation-update-1605542083">lynchages médiatiques</a>, et qu'on exige la responsabilité, parce qu'il s'agit <a target="_blank" href="https://www.youtube.com/watch?v=0QLz0CqtMVc#t=121">des plusieurs organes de presses influents</a> et beaucoup de gens <a target="_blank" href="https://archive.is/DIpqE#selection-4917.0-4928.0" title="http://www.gamespot.com/articles/who-wins-when-mass-effect-3s-ending-changes/1100-6367380/">en ont assez</a>.</p>

</div></div>

<aside class="g7"><div class="pad">
  <p><a class="block" target="_blank" href="https://archive.is/nWG2P" title="http://www.gamasutra.com/view/news/224400/Gamers_dont_have_to_be_your_audience_Gamers_are_over.php"><img src="https://acko.net/files/occupy-www-street/gamers-are-over.jpg" alt="Gamers are over" /></a></p>
</div></aside>

<div class="g5 serif"><div class="pad">

<p>Plutôt qu'admettre que c'est devenu une farce monumentale et ils ont été trompés, les rapporteurs pourraient bien serrer les rangs. Ils tenteraient de concocter un conte que tout la critique qu'elle reçoit est de l'harcèlement, que les joueurs sont <a target="_blank" href="https://archive.is/Awcw9" title="http://www.gamasutra.com/view/news/224400/Gamers_dont_have_to_be_your_audience_Gamers_are_over.php">des frondeurs de merde et des hyperconsommateurs gémissants</a> qui détestent les femmes, en declarant que l'identité des joueurs est mort dans <a target="_blank" href="https://pixietalksgamergate.wordpress.com/gamers-are-dead-article-analysis/">dix articles publiés sur 24 heurs</a>. Pourtant qu'ils aliéneraient leur lectorat ancien, ils attireraient le nouveaux segment, plus politique, qui verrait tous leurs préjugés sur les jeux vidéo et les joueurs confirmés, ignorant que le média avait déjà connu des <a target="_blank" href="http://torment.wikia.com/wiki/Planescape:_Torment">thèmes sophistiqués</a> explorés <a target="_blank" href="http://silenthill.wikia.com/wiki/Silent_Hill_2">avec maturité</a>.</p>

</div></div>

<div class="g12 mt0 serif"><div class="pad">
  
<p class="mt0 mb2"><a class="block" target="_blank" href="https://archive.is/Awcw9" title="http://www.gamasutra.com/view/news/224400/Gamers_dont_have_to_be_your_audience_Gamers_are_over.php"><img src="https://acko.net/files/occupy-www-street/gamers-are-over-2.png" alt="Gamers are over" /></a></p>

</div></div>

<div class="g10 i1 mt0 serif"><div class="pad">

<p class="mt0">Pour les nouveaux lecteurs ça pourrait apparaître comme une invitation de s'engager, d'instruire la plèbe de Rome et de défendre les belles vierges des sauvages supposés, en pleine vue des amis sur les médias sociaux, <a target="_blank" href="http://knowyourmeme.com/memes/nods-respectfully-towards-you">hochant la tête respectueuse</a>. Ils pourraient reformuler les événements comme un "amant éconduit"—bien qu'<em>il</em> l'ait abandonné—qui a rassemblé une foule odieuse dans les profondeurs maléfiques de 4chan—bien qu'il l'ait posté premièrement sur <a target="_blank" href="https://archive.is/h5LTs" title="https://medium.com/@srachel_m/gamergate-launched-in-my-apartment-and-internet-im-sorry-not-that-sorry-13e5650fd172">Something Awful et Penny Arcade</a> et les sentiments aient été mitigés. Peut-êtres ils diraient qu'il a dévoilé du <em>"porno de vengeance"</em> quand en fait ce sont ses propres <a target="_blank" href="https://archive.is/rbmsg" title="http://www.skepticink.com/incredulous/2014/10/04/zoe-quinns-lying-cheating-claim-stabbing-killing-man-alleged-former-photographer/">photos érotiques à paiement</a> qui circulent. Ce sont tous des mensonges éhontés ou des admissions de ne pas avoir pris la peine de lire <a target="_blank" href="http://www.gamergatewiki.com/index.php/Timeline/Full">des sources</a>.</p>

<p>Il serait essentiel que les rapporteurs ne jamais mentionnent la campagne de censure qui pourrait se produire chez Reddit, 4chan et d'autres fora de joueurs, et l'effet de Streisand résolutoire qui entraînerait encore plus d'attention. Cela pourrait comprendre <a target="_blank" href="https://archive.is/rp54n" title="http://greggreenwell.tumblr.com/post/95136135894/zoe-quinn-is-using-false-dmca-claims-to-take-down">une fausse réclamation de droits d'auteur</a> addressée à une personnalité Youtube réputée, <a target="_blank" href="https://archive.is/i9Du4" title="http://www.reddit.com/r/gaming/comments/2dz0gs/totalbiscuit_discusses_the_state_of_games">aménager un fil de 25 000 commentaires en cimetière</a>, interdire l'accès aux personnes qui mentionnent certains noms, et déclarer que le sujet entier est défendu dans certains subreddits <em>alliés</em>. Parce qu'il faudrait un échange de faveurs dans les coulisses chez les modérateurs, cela pourrait laisser penser qu'il y a <a target="_blank" href="https://archive.is/7VKAE" title="http://blogjob.com/oneangrygamer/2015/03/reddit-mods-admit-to-censoring-gamergate-hiding-corruption/">même plus de la collusion</a> et même un complot, ce qui amène certaines personnes de migrer en dégoût de 4chan vers un forum auparavant mineur, et ensuite vers Voat de Reddit.</p>

<p>Même si quelqu'un savait exactement ce qui se passerait, <a target="_blank" href="https://archive.is/AemDy#selection-7417.0-7421.310" title="https://archive.4plebs.org/pol/thread/34317761/">une semaine à l'avance</a>, la presse des jeux n'affronterait pas en aucune circonstance les faits et n'admettrait pas l'erreur. Au lieu ils commenceraient à faire la guerre médiatique interminable aux consommateurs qui veulent principalement du responsabilité, et ainsi, deviendraient le plus grand aimant pour les trolls, les drames, les e-célébs et fausses bannières que l'internet verrait depuis sa création. Surtout ceux que <a target="_blank" href="https://archive.is/wbbOI#selection-2943.0-2950.0" title="http://gamergate.wikia.com/wiki/Reddit">le vieux internet connaît déjà</a>.</p>

<p>Exactement comme la victime d'abus Eron Gjoni avait averti, avant d'être étouffé par une ordonnance contestée et <a target="_blank" href="https://archive.is/pzSeP" title="https://www.reddit.com/r/KotakuInAction/comments/3hgq3b/zoe_quinn_moves_to_vacate_restraining_order_eron/">possiblement inconstitutionnelle</a>, un gros morceau d'une industry pourrait <a target="_blank" href="https://archive.is/RNzCI" title="http://gawker.com/how-we-got-rolled-by-the-dishonest-fascists-of-gamergat-1649496579">devenir fou</a> les mois suivants, à cause des machinations égocentriques d'une agresseuse. Les médias du jeu et les feministes auraient beaucoup de matière avec leur nouveau souffre-douleur. Vu que la presse mondiale est vraiment guère plus qu'un jeu au téléphone arabe, ils rapporteraient tous ce que ces <em>experts</em> disaient. Ils se fourniraient de la culpabilité par association sans rechercher, en diffusant une grande mensonge <a target="_blank" href="https://duckduckgo.com/?q=gamergate+harassment">aussi énorme</a> que Goebbels en contracterait le priapisme palpitant. Par exemple, si <a target="_blank" href="https://archive.is/Hy6AO" title="https://sli.mg/NUIXim">une commentaire introuvable</a> que personne n'en sait rien était exagérée momentanément à la <a target="_blank" href="https://archive.is/vAJ69" title="https://duckduckgo.com/?q=felicia+day+doxxed">mort de Lady Di dans le Royaume Champignon</a>. Si des citations aléatoire de <em>chat</em> seraient acceptées comme <a target="_blank" href="https://archive.is/MWvXC" title="http://www.theguardian.com/technology/2014/sep/12/zoe-quinn-gamergate-online-hate-mobs-depression-quest">d'espionnage</a> sur les opinions et motivations des milliers, sans autre recherche ou droit de réponse. Peut-être ils croirions sur parole une menteuse-en-série et <a target="_blank" href="https://archive.is/Qb96g" title="https://twitter.com/TheQuinnspiracy/status/166659770248474624">vétéran des dump d'enfers</a>, qui figure comme l'archiduc Ferdinand ainsi que <a target="_blank" href="https://archive.is/h8rlT">Jeanne d'Arc</a> dans son propre <a target="_blank" href="http://knowyourmeme.com/memes/events/donglegate-adria-richards">DongleGate</a>.</p>

<p>Tout ça pour <em>vidya</em>, les jeux vidéo, tout parce que une bande de rapporteurs se sont trouvés <a target="_blank" href="https://archive.is/6yr0p" title="http://deepfreeze.it/journo.php">les rois et reines</a> d'un palais personne n'en a vraiment besoin après tout. Ils ont découvert un public convaincu que Twitter et Tumblr était comment on fait l'activisme, que <a target="_blank" href="https://archive.is/iNbS5" title="http://www.thecrimson.com/column/words-words-words/article/2015/3/12/simplistic-social-justice-warrior/">les universités</a> et <a target="_blank" href="https://archive.is/c2RWs" title="http://www.examiner.com/article/calgary-expo-censors-the-honey-badger-brigade-s-anti-censorship-message">les congrès</a> devraient êtres des endroits où <a target="_blank" href="https://www.youtube.com/watch?v=iARHCxAMAO0">la recherche de bonne foi s'éteind</a>.</p>

<p>Mais ça serait de la folie.</p>

</div></div>

<div class="g12 mt0 serif"><div class="pad">

<p class="mt0 mb0"><a target="_blank" href="http://knowyourmeme.com/memes/events/shirtstorm"><img src="https://acko.net/files/occupy-www-street/matt-taylor-shirt.jpg" alt="Matt Taylor and his shirt" class="fit block"></a></p>

</div></div>

<div class="g10 i1 mt0 serif"><div class="pad">

<p>Encore plus hallucinant serait de voir que, contrairement à chaque fois auparavant, ce entrisme-par-voie-justice-sociale rencontrerait de l'opposition provocante. Que d'une façon ou d'une autre, l'impossible avait arrivé, et les gens de <a target="_blank" href="https://www.reddit.com/r/KotakuInAction/">Reddit</a> et <a target="_blank" href="https://8ch.net/">4chan</a> pourraient mettre de côté leurs différences assez longtemps pour qu'ils réussissent à <a target="_blank" href="https://gitgud.io/gamergate/gamergateop/tree/master/Operations/Operation-Disrespectful-Nod">obtenir des résultats</a>. Que tous les <a target="_blank" href="https://www.reddit.com/r/tumblrinaction">olympiades d'oppression</a>, la <a target="_blank" href="http://knowyourmeme.com/memes/events/shirtstorm">honte médiatique</a>, les  <a target="_blank" href="https://duckduckgo.com/?q=tim+hunt">fausses représentations</a>, les <a target="_blank" href="http://slatestarcodex.com/2014/07/07/social-justice-and-words-words-words/">jeux de mots</a>, les <a target="_blank" href="http://esr.ibiblio.org/?p=2122">pièges logiques</a> et le <a target="_blank" href="https://www.youtube.com/watch?v=IAF2UmyXe-4">ton moralisateur</a> pourraient avoir pressé assez de gens de dire: non, pour une fois, <em>toi</em> tu prends du recul. Qu'il tenteraient d'élever Twitter vers le plus rationel qu'il sera jamais, c'est-à-dire, pas du tout. Que les gens là seraient si dépendant de la validation digitale, qu'il prendraient le démantèlement immédiat de leur idées maladroits comme l'abus malveillant. Ils pourraient le dénommer <a target="_blank" href="https://duckduckgo.com/?q=cyberviolence+at+the+UN">la violence digitale</a> aux Nations Unies, montrant du picorage de quelques imbéciles, en <a target="_blank" href="https://archive.is/ITMjR" title="https://github.com/freebsdgirl/ggautoblocker">auto-bloquant le reste</a> comme une prophétie autoréalisatrice.</p>

<p class="mt2 mb2"><iframe style="width: 100%" height="400" src="https://www.youtube.com/embed/8NhIBhbqWo4" frameborder="0" allowfullscreen="allowfullscreen"></iframe></p>

<p>Vous pourriez découvrir que, un an plus tard, convaincre une <a target="_blank" href="http://spjairplay.com">Société de Journalistes Professionels</a> de reconnaître du biais de sélection, du <em>copy-paste</em>, de la paresse et de la flatterie est beaucoup plus difficile qu'en 2011 chez Fox News: cette fois, il y a des <a target="_blank" href="https://archive.is/mj8wM" title="http://risemiaminews.com/2015/08/breaking-gamer-gate-controversy-prompts-evacuation-of-koubek-center-in-miami/">alertes</a> à la <a target="_blank" href="https://archive.is/NmVco" title="http://nichegamer.com/2015/05/peaceful-gamergate-in-d-c-meetup-receives-false-bomb-threats/">bombe</a> et peu de gens semblent intéressés qui est responsable, ou même savent <a target="_blank" href="https://archive.is/fFzuD" title="http://gamepolitics.com/2015/08/12/challenge-accepted-interviewing-internet-hashtag/">comment en rendre compte</a>. Que l'infotocratie de Wikipédia <a target="_blank" href="https://archive.is/jehPF">décide</a> de <a target="_blank" href="https://archive.is/rUVo3">disséminer</a> de la <a target="_blank" href="https://archive.is/Nf6Wv">propagande totale</a> pendant un <a target="_blank" href="https://archive.is/Nf6Wv">certain temps</a>, coincés dans la <a target="_blank" href="https://archive.is/P2UdO" title="http://i.imgur.com/RnQJ3Hl.png">citogenèse</a> qu'ils <a target="_blank" href="https://archive.is/G4ppN" title="https://www.reddit.com/r/KotakuInAction/comments/2yfrfb/why_did_ryulong_get_banned_from_wikipedia/">créèrent</a>.</p>

<p>Parce qu'il est <a target="_blank" href="https://archive.is/ouEJX" title="https://www.patreon.com/zoe">bien plus lucratif</a> de <a target="_blank" href="https://archive.is/14BFg" title="https://archive.is/14BFg">ne savoir de rien</a> après de <a target="_blank" href="https://archive.is/UhZYI" title="http://www.cjr.org/the_kicker/gawker_bullying.php">se moquer</a> des <a target="_blank" href="https://archive.is/X7DJB" title="https://images.encyclopediadramatica.se/7/71/Brianna_Wu_Meme_original.png">enfants autistes</a> d'une position de <a target="_blank" href="https://archive.is/WhVcQ" title="https://pbs.twimg.com/media/B1Pg0IgIYAEKXX_.png:large">privilège non méritée</a>.</p> 

<h2 class="mt3">Le Meilleur des Mondes</h2>

<p>Enfin, bouclons la boucle sur ce fer à cheval dément.</p>

<p>Qu'arriverait-t-il si les tant attendues <em>"nouvelles façons d'interagir avec les autres"</em> existent déjà depuis 2001, sous la forme du Tableau d'Images Anonyme? Peut-être pas par coïncidence une invention du Japon, une culture qui a transformée la répression en une forme d'art. Un tableau où l'identité et le prestige n'ont aucune valeur par défaut, et par conséquent, <a target="_blank" href="http://twentytwo.fibreculturejournal.org/fcj-158-tits-or-gtfo-the-logics-of-misogyny-on-4chans-random-b/">des règles de jeu entièrement différentes s'appliquent</a>, faisant fond sur les pratiques du cyberspace ancien.</p>

<p>Il n'entraîne pas de forum utopique où les intellectuels mènent le débat comme les extraterrestres de Star Trek. Au contraire, vous traitez avec les humains. Il produit des endroits qui mélangent du scepticisme profond avec des insultes accessoires et de l'abus graphique, qui rendent chacun et tout également sans valeur et fictif, jusqu'à preuve du contraire. Apporter l'identité comme point générale en discussion n'est pas simplement malvenu, il est conspué avec exactement ces insultes qui infligent le plus de dommages. La pertinence vient de la résilience, qui est offensé perd immédiatement. L'ingéniosité, l'hyberbole et l'humour sont les mesures de contrôle pour balancer tout, comme des garde-fous contre <a target="_blank" href="https://www.youtube.com/watch?v=DlSx7m09R60">l'homme sans imagination</a> et son <a target="_blank" href="https://www.youtube.com/watch?v=WcebgKvAoh0">faillite intellectuelle</a>. Les idées doivent se rivaliser pour demeurer pertinents, à une seule condition: aucun sujet est défendu. Surtout les tabous. Ce n'est pas exactement pour les cœurs fragiles.</p>

<p>C'est la seule espace d'internet approchante une <em>"safe space"</em> universelle qui fonctionne, c'est-à-dire, pas du tout, pas comme les definitions du monde hors ligne. Néanmoins, une fois habitué, la pornographie, les trolls, les porte-paroles, les blagues sur les juifs, l'<em>enculisme</em>, ça devient tout si banal et transparent qu'il ne vaut pas la peine de s'inquiéter. Comme il vraiment faut.</p>

<p><iframe style="width: 100%" height="410" src="https://www.youtube.com/embed/3hPFPjQ4pjo?cc_load_policy=1" frameborder="0" allowfullscreen="allowfullscreen"></iframe></p>

<p class="mt2 mb2">Regardez, même qu'elle est aussi vulnérable aux plaintes et au tribalisme qui ont remporté Twitter son énorme succès, elle est <a target="_blank" href="https://archive.is/vWYnS" title="https://sli.mg/BVK1RH">beaucoup mieux équipée</a> comme une média et une culture d'en <a target="_blank" href="https://archive.is/kCa3S" title="https://sli.mg/x18eNq">traiter durablement</a>. Peut-être ils <a target="_blank" href="https://archive.is/Oxe8d" title="https://sli.mg/Tw3tn4">raseraient totalement leur propre village</a> pour le <a target="_blank" href="https://archive.is/oViUh" title="https://sli.mg/5z0jV6">rebâtir ailleurs</a> dans des moments de grande détresse. Quand il est tout remplaçable et compris de uns et de zéros de concièrge, est-ce vraiment plus qu'un changement de domicile? Il n'est pas si étrange: on était des nomades, avant que la promesse d'un estomac plein et un toit sur nos têtes nous a convaincus de sacrifier un petit peu de liberté pour plein de sécurité.</p>

<p><iframe style="width: 100%" height="410" src="https://www.youtube.com/embed/Vx5BNDPA5hE" frameborder="0" allowfullscreen="allowfullscreen"></iframe></p>

</div></div>

<div class="g9 mt0 serif"><div class="pad">

<p>Il y a bien du monde qui le savent, <a target="_blank" href="http://knowyourmeme.com/memes/events/project-chanology">rien de nouveau à cela</a>, et une génération grandit qui le considère aussi normal que Facebook et Snapchat. Des autres regardent les chans comme une porte de l'enfer qui donne lieu aux bigots, criminels et harceleurs. Comme la Verité 9/11 chez Occupy, quand vous supprimez certaines idées vers la contre-culture, ne vous étonnez pas quand le dialogue ouvert doit partager l'espace avec des voisins louches, sur une longue queue de frange et de vraie marginalisation. Le détestez à votre guise, mais ne l'ignorez pas.</p>

</div></div>

<aside class="g3"><div class="pad">
  <p class="mt0"><a class="block" target="_blank" href="http://knowyourmeme.com/memes/mark-bunkerwise-beard-man"><img src="https://acko.net/files/occupy-www-street/wise-beard-man-alt.jpg" alt="Mark Bunker, Wise Beard Man" /></a></p>
</div></aside>

<div class="g10 i1 mt0 serif"><div class="pad">

<p>Pour les vieux lecteurs: quand vous pensez que <em>Donjons et Dragons</em> endoctrine les jeunes dans des sorcières et mages, ou que les crèches tuent les enfants dans des rituels sataniques, vous vous attendez d'être ridiculisé comme bouffon de panique. Peut-être ils ont appris de <a target="_blank" href="https://archive.is/AFT86" title="http://i.imgur.com/jQ2VBPA.png">créer des ponts</a> que vous n'en saviez rien, <a target="_blank" href="https://archive.is/IgTMn" title="http://www.torychristman.com/2008/02/01/youth-speaks-to-tory-anonymous-vs-scientologys-abuses/">il y a longtemps</a>, et ils n'avaient pas besoin de se déclarer d'être du bon côté de l'histoire.</p>

<p>Encore plus hilarant sont les gens qui craignent notre cher Reddit, comme par example, des <a target="_blank" href="https://archive.is/dj7Jy" title="http://idibon.com/toxicity-in-reddit-communities-a-journey-to-the-darkest-depths-of-the-interwebs/">études d'internet terribles</a>. Ils font de leur mieux pour ignorer leurs propres observations et les trouble-fêtes de sa propre coalition, avec un <em>"mais les Femmes de Numerique"</em> spontané pour ajouter de la couleur. Ils voient ce qu'ils veulent voir, sans réaliser que prendre au pied tout ce qu'on y peut lire n'est pas simplement la faute numéro un des débutants, c'est souvent un mechanisme qui fonctionne précisément pour décourager les étrangers <em>peu motivés</em>. Les trolls qu'on repère tout de suite sont les <a target="_blank" href="https://archive.is/P3Qqu" title="http://www.breitbart.com/london/2014/12/10/the-madness-of-queen-shanley/">amateurs</a>, et certaines personnes sont si inutiles qu'ils sont une perte sèche pour chaque communauté en ligne où ils s'amarrent, souvent sans reproche.</p>

<p>L'internet pert son valeur unique dès que vous exigez qu'il fonctionne exactement comme la vie réelle. Nos intuitions et activités primaires ne s'adaptent pas bien en dehors d'un seul petit tribu, il paraît un <a target="_blank" href="https://archive.is/s0GhA" title="http://beautifultrouble.org/theory/dunbars-number/">fait empirique</a> aussi que <a target="_blank" href="http://knowyourmeme.com/memes/greater-internet-fuckwad-theory">l'expérience vécu</a>. Le reflexe humain par défaut est de compter sur l'identité partagée pour la validation sociale. Leur insecurité amène les gens à éviter la pensée approfondie, ayant trop de peur d'aller à contre-courant. Ils méconnaissent les idées au lieu de les assaillir, et succombent à la pensée de groupe et la chambre d'échos. L'antidote est toujours la même: l'humour et la réflexion affable, y compris la connaissance de soi-même, pas de panique morale.</p>

<p>Il y a une seule question nécessaire pour compléter ce tableau. Pourquoi sont-ils si avides de craindre que le monde des jeux—ou la société entière—déteste les femmes et les minorités, ne les veut pas, les considère inférieures, nécessitant de l'aide et de la sensibilisation unique, même quand il y a <a target="_blank" href="http://knowyourmeme.com/memes/notyourshield">des preuves et des témoins</a> qui nous disent autrement? Voici un indice: les <em>guerriers de la justice sociale</em> ont toujours énormes difficultés de simplement <em>considérer</em> des opinions différentes des leurs. Mais celui-ci, il ne le peuvent pas laisser béton.</p>

<p>L'enfer n'est pas simplement pavé de bonnes intentions, le Diable vous sert du pop-corn gratuit.</p>

<p><span class="spoiler" onclick="this.classList.toggle('active')">Et quand vous descendez dans le terrier du lapin, apportez assez de carburant pour brûler vers un <strong><a target="_blank" href="https://www.youtube.com/watch?v=F92l2s_bO-k">apoapse</a> <a target="_blank" href="https://www.youtube.com/watch?v=VQtnqU-mEDc">plus élevé</a></strong>.</span></p>

</div></div>

<div class="c m1"></div>

<div class="wide mt2">
  <a target="_blank" href="https://kerbalspaceprogram.com/en/"><img src="https://acko.net/files/occupy-www-street/kerbal.jpg" alt="Kerbal Space Program"></a>
</div>

<div class="g10 i1 mt1 serif"><div class="pad">

<p>Les médias <a target="_blank" href="https://gitgud.io/gamergate/gamergateop/tree/master/Current-Happenings">ont menti</a>, et j'ai vu beaucoup de gens se tromper que je pensait savoir mieux. C'était absolument baise-cerveau de voir des gens éduqués faire défaut si durement, et mettre ses favoris chemises brunes au nom du progrès. C'est comme ils ne sont jamais sortis du lycée, encore tentant de rejoindre la groupe populaire alors que s'en irriter. Jamais réalisant que le ton moralisateur ne marche que sur les velléitaires, et seulement pour un certain temps. <a target="_blank" href="https://www.youtube.com/watch?v=LHPqUddXCu8">Anita t'arrête svp</a>.</p>

<p><a target="_blank" href="https://archive.is/sxkgY" title="https://pbs.twimg.com/media/CTJXaFIWIAEEP72.jpg:large">Écoutez</a> et <a target="_blank" href="https://archive.is/Cd0rz" title="https://pbs.twimg.com/media/CTJXY8cWsAA-zQC.jpg:large">Croyez</a>, prends ta avec-nous-ou-contre-nous mentalité et va réfléchir longtemps à ce que tu as fait. L'Internet est sous surveillance complète, les ententes commerciales mondiales sont sur le point d'élever les entreprises à la niveau des états, et on n'a jamais été si prêt de censurer le net. Mais cette fois, ce n'est pas juste la menace de terrorisme, des pédophiles ou de la pornographie juvénile que l'on utilise pour effrayer les masses et faire taire les critiques. C'est <em>"la violence digitale contre les femmes"</em> et la <em>"suprématie blanc"</em> de la marge. Vous savez ce qui <em>"maintient un système rompue"</em>? Quand une agresseuse populaire jette aux oubliettes un homme victime et on <a target="_blank" href="https://archive.is/AGZie" title="http://shrink4men.com/2011/01/19/presto-change-o-darvo-deny-attack-and-reverse-victim-and-offender/">échange les étiquettes</a> sans honte en appliquant du ruban adhésif.</p>

<p>Regardez autour de vous. L'un des plus éminents activistes pour la liberté de presse vit en détention à domicile dans une ambassade, accusé du crime internationalement infâme des <a target="_blank" href="https://archive.is/a8br" title="http://georgewashington2.blogspot.de/2010/12/sex-charges-and-arrest-warrant-against.html">rapports sexuels consensuel mais sans préservatif,</a> c'est-à-dire, <em>"un viol."</em> Le sexisme et le racisme ont été utilisés comme armes pervertis de la victimisation inversée par ceux qui n'ont rien de substantiel à offrir mais qui peuvent tirer le grand profit. Le quotidien qui a publié les fuites Snowden s'a réduit au <a target="_blank" href="https://archive.is/D74Gl" title="http://www.theguardian.com/uk-news/tim-hunt">niveau de tabloïd</a>. Ils déshonorent les scientifiques plutôt que les admirer pour leurs réussites. Ce n'est pas de complot, pas dans un forêt des pièges à clics qui continue d'attracter des lecteurs consentants. Ils continuent de mettre la main au portefeuille pour acheter des indulgences post-catholiques pour la faute originelle patriarcale et la culpabilité oppresseur blanche.</p>

<p>PS: Rejeter les autres comme en colère n'est pas une riposte valable (ça s'appelle la <em>projection</em>), ni les décrire comme des bigots, des harceleurs ou des misogynistes/homophobes/racistes intériorisés à cause de choses qu'ils n'ont pas fait. La justice sociale désirait que tout le monde <em>s'éduque</em>, alors, nous leur avons donné exactement ce qu'ils voulaient—avec ses défauts et ses qualités—et ils de même que les médias continuent à le déformer. Il n'y avait pas de grande tromperie au nom de joueurs, pas de foule odieuse type <a target="_blank" href="https://archive.is/KeUR9">reefer madness</a> qui chasse les femmes du secteur pour être femmes. Juste des dilletantes sans talent, de nombreux de personages des intentions peu claires, beaucoup de trolls de tierce partie, et de nombreux autres qui essayent de travailler autour d'eux pendant qu'on les insulte. Je ne suis pas <a target="_blank" href="https://www.youtube.com/watch?v=rGIY5Vyj4YM">folle de rage</a>, je simplement n'en veux plus.</p>

<p>(Et ils me diront probablement de droite, sans réfléchir que le socialisme national créa le Volkswagen pour une raison. Oui je sais, Godwin. <a target="_blank" href="https://archive.is/du2rV" title="https://sli.mg/HdI5Uf">Wir haben es nicht gewußt</a>.)</p>

<p><iframe style="width: 100%" height="380" src="https://www.youtube.com/embed/L61ZCN8rrWw" frameborder="0" allowfullscreen="allowfullscreen"></iframe></p>

<div class="c"></div>

<p class="tc mt4 mb0"><img src="https://acko.net/files/occupy-www-street/darmok-and-jalad.jpg" alt="Darmok and Jalad at Tanagra" class="natural spoiler" onclick="this.classList.toggle('active')" /></p>

<p class="tc mt0"><a target="_blank" href="http://knowyourmeme.com/memes/people/ben-garrison"><img src="https://acko.net/files/occupy-www-street/ben-garrison.png" class="flat natural" width="100" height="59" title="No, I'm not Zyklon Ben." /></a></p>

<p class="tc mt3 mb0"><strong>L'arbre de la liberté doit être ravivé de temps en temps<br>avec le sang des patriotes et des tyrans,</strong></p>

<p class="tc mb5 mt0"><span class="spoiler block" onclick="this.classList.toggle('active')">mais ils ont essayé du ketchup.</span></p>

<p class="mt3 mb2"><iframe style="width: 60%; margin: 0 auto;" height="240" src="https://www.youtube.com/embed/EKc-fqsIxGk" frameborder="0" allowfullscreen="allowfullscreen"></iframe></p>

<p class="mt2 mb2"><iframe style="width: 60%; margin: 0 auto;" height="240" src="https://www.youtube.com/embed/5MRUgLf72AM" frameborder="0" allowfullscreen="allowfullscreen"></iframe></p>

<p class="mt2 mb5"><iframe style="width: 60%; margin: 0 auto;" height="240" src="https://www.youtube.com/embed/atazfJqCutQ" frameborder="0" allowfullscreen="allowfullscreen"></iframe></p>

</div></div>]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Occupy WWW Street (FI)]]></title>
    <link href="http://acko.net/blog/occupy-www-street-fi/"/>
    <updated>2015-11-30T00:00:00+01:00</updated>
    <id>http://acko.net/blog/occupy-www-street-fi</id>
    <content type="html"><![CDATA[<div class="g8 i2 first serif"><div class="pad">

<h2 class="sub">Internet Activism and Media in the Age of Social Justice</h2>

</div></div>

<div class="c"></div>

<div class="g8 i2 serif"><div>

<p class="outlined lang mt1 mb1 tc"><a href="/blog/occupy-www-street-en/">English</a> - <a href="/blog/occupy-www-street-fr/">Français</a> - <a href="/blog/occupy-www-street-nl/">Nederlands</a></p>

</div></div>

<div class="g12 serif"><div class="pad">

<blockquote class="mt1 mb2 lift outlined">
  <em class="big">"I don't have access to any unusual methods of analysis and what special knowledge I have concerning language has no bearing on social and political issues. […] For the analysis of ideology […] a bit of open-mindedness, normal intelligence, and healthy skepticism will generally suffice." –&nbsp;NC</em>
</blockquote>

</div></div>

<div class="g10 i1 mt0 serif"><div class="pad">

<p class="outlined">I'm bringing out my serious font <a href="/blog/storms-and-teacups/">again</a>. It's difficult to talk about current affairs without inviting in heaps of context, so I'd like to delve into some recent history.</p>

<p class="outlined"><em>Reminder, shooting the messenger is poor form, and heresy is medieval.</em></p>

<p><em>(🇫🇮 &nbsp;&nbsp;Links have been unarchived)</em></p>

<h2 class="mt4">The Only Working Class Man In The Village</h2>

<p>It's September 2011, the American left has been galvanized. The message is straightforward and aimed at big targets: the 99% of the public is telling the 1% scrooges to stop being greedy and not use corporations as a club. Zuccotti Park in New York fills with activists camping out, discussing and spreading their message.</p>

</div></div>

<div class="c m1"></div>

<div class="wide mt2">
  <a target="_blank" rel="nofollow" href="https://www.youtube.com/watch?v=6yrT-0Xbrn4"><img src="https://acko.net/files/occupy-www-street/lagreca.jpg" alt="Jesse LaGreca schools Fox News"></a>
</div>

<div class="g10 i1 mt1 serif"><div class="pad">

<p>Cue Jesse LaGreca. With his quintessential American Joe Average look, he takes most folks by surprise, most certainly the Fox News reporter who approaches him. In the <a target="_blank" rel="nofollow" href="https://www.youtube.com/watch?v=6yrT-0Xbrn4">impromptu interview</a>, unaired by Fox, LaGreca schools the man and powers through, knocking it out of the park. He calls out the leading questions right away, and notes that if there's one thing the news—<em>"the propaganda machine in the media"</em>—doesn't wish to report on, it's itself. That maybe it's time to have some participatory democracy that <em>"isn't funded by news cameras."</em></p>

<p>A reporter shows up expecting to report on the world and discovers this story is, at least in part, about themselves. The denial is quick: <em>"We’re here giving you an opportunity on the record […] to put any message you want out there, to give you fair coverage […] So, there is an exception in the case, because you wouldn’t be able to get your message out there without us."</em></p>

<p>He snaps back: <em>"Certainly. Take for instance when Glenn Beck […] called the president a person who hates white people and white culture. That was a low moment in Americans' history and you guys kind of had a big part in it. So I'm glad you're coming around and kind of paying attention to the [rest of the country] as opposed to the far right fringe who would just prefer to destroy the middle class entirely. […] People would like to see a little bit more economic justice, or social justice, you know, Jesus stuff, as far as feeding the poor, healthcare for the sick. […] I find it really entertaining that people like to hold the bill of rights up while they're screaming at gay soldiers, but they can't wrap their heads around the fact that a for-profit health care system doesn't work. […] If we want the president to do more, let's talk to him on a level that works instead of asking for his birth certificate, […]"</em></p>

<p>Shots fired, critical hit, no chance of recovery. It's no surprise it was left on the cutting room floor, and no surprise it <a target="_blank" rel="nofollow" href="http://observer.com/2011/10/exclusive-occupy-wall-street-activist-slams-fox-news-anchor-in-un-aired-interview-video/">would leak</a> and shoot to fame as being <em>too extreme for Fox news</em>. Predictable headlines asked if he was the <em>The Smartest Man on Wall Street</em> and explained that <em>Jesse LaGreca Continues to Destroy Media Bias of Occupy Wall Street</em>. All reporters eagerly falling over themselves to demonstrate how <em>they</em> were willing to give anyone a voice.</p>

<p>This was an angle LaGreca himself <a target="_blank" rel="nofollow" href="http://observer.com/2011/10/jesse-lagreca-on-abcs-this-week/">brought up</a> on air: <em>"The reality is that I’m the only working class person you’re going to see on Sunday news… political news… maybe ever. And I think that is very indicative of the failures of our media to report on the news that matter most importantly…"</em> The host immediately cuts in, <em>"We are trying our best, Jesse."</em></p>

<p>However when asked if there was a political demand that could sum up Occupy, something to <em>"make this about,"</em> he takes a curious turn:</p>

<p><em>"I think the entire movement is about economic justice. I mean to me – and I’m not speaking on behalf of Occupy Wall Street, I’m just giving my personal opinion – I think it’s a matter of economic rights, and I think it’s a matter of social rights, and social justice. And to the people who would take offense to the word “social” being placed before the word “justice,” I’d invite them to re-read the Constitution."</em></p>

<p>Ok. I went through the trouble and <a target="_blank" rel="nofollow" href="https://melissafong.wordpress.com/2014/04/04/why-suey_park-wont-enact-the-labour-women-of-colour-dont-owe-you-an-education/">enacted the labor to educate myself</a>. The United States Constitution makes no references to anything social with regards to justice. Those who wrote it seemed to think that merely <em>"establishing Justice"</em> was plenty enough. It does not rank citizens by privilege, does not define powers in relation to oppression and makes no allusions to punching up. That's what social justice is nowadays, right? Or did he mean the <em>"Jesus stuff"</em>? I'm no constitutional lawyer, feel free to correct me.</p>

<p>More awkwardly, on the subject of taking their cues from other protests, in Greece and Europe and elsewhere, LaGreca claimed <em>"we didn’t take our cue leading off of anybody really."</em></p>

</div></div>

<aside class="g5 mt1"><div class="pad">
  <a target="_blank" rel="nofollow" href="http://www.webcitation.org/63DZ1nIDl" title="http://www.adbusters.org/blogs/adbusters-blog/occupywallstreet.html"><img src="https://acko.net/files/occupy-www-street/adbusters-occupy.jpg" class="fit" /></a>
</div></aside>

<div class="g7 serif"><div class="pad">

<p>Occupy may have grown organically from existing groups, but it was jumpstarted by an early <a target="_blank" rel="nofollow" href="https://www.adbusters.org/blogs/adbusters-blog/million-man-march-wall-street.html">blog post</a> and a <a target="_blank" rel="nofollow" href="http://www.webcitation.org/63DZ1nIDl" title="http://www.adbusters.org/blogs/adbusters-blog/occupywallstreet.html">message blast to Adbusters' subscriber base</a> of <em>"you 90,000 redeemers, rebels and radicals out there,"</em> declaring a <em>"worldwide shift in revolutionary tactics is underway,"</em> asking if they were ready to have their own <em>"Tahrir moment."</em> The 267 comments show this message was received with enthusiasm and commitment.</p>

<p>LaGreca also wasn't a nobody, he'd been a Daily Kos contributor for three years already. The site's community <a target="_blank" rel="nofollow" href="http://www.dailykos.com/story/2011/10/05/1023035/--UPDATE-Jesse-LaGreca-taking-on-Hannity-NY-Observer-MoT-Smartest-Man-on-Wall-Street">cheered him on</a> for <em>"not [fitting] into the media’s stereotype of the obtusely disenfranchised ne0-hippies"</em> and helped get the word out while commenting on the spectacle: <em>"My guess is that Hannity is scared. As he should be!"</em></p>

</div></div>

<div class="g10 i1 mt1 serif"><div class="pad">

<p>This is not an indictment of Mr. LaGreca, he mainly talked a lot of sense and said it well. However, four years have passed. Despite the generous airtime afforded by his newfound <a target="_blank" rel="nofollow" href="http://knowyourmeme.com/memes/streisand-effect">fame-by-Streisand</a> and the stated desire from reporters to do better, the situation did not improve, not with Fox News and not with the other outlets who define themselves through opposition.</p>

<p>If there's one thing the media loves, it's the exception that confirms the rule: you cannot galvanize the public without access to existing networks and platforms. When you go up against the media, they will <a target="_blank" rel="nofollow" href="http://www.tv.com/shows/black-mirror/community/post/black-mirror-15-million-merits-review-138498157166/">repackage you into an entertainment product</a>, and the masses will love it. You might love it too.</p>

<h2 class="mt3">Activists Are Dead</h2>

<p>So what happened? In June 2012, community hub and non-profit OccupyWallSt.org posts that <a target="_blank" rel="nofollow" href="http://occupywallst.org/article/reports-occupys-death-have-been-greatly-exaggerate/">Reports of Occupy’s Death Have Been Greatly Exaggerated</a>.</p>

<p class="tc"><a class="block" target="_blank" rel="nofollow" href="http://occupywallst.org/article/reports-occupys-death-have-been-greatly-exaggerate/"><img src="https://acko.net/files/occupy-www-street/occupy-logo.png" alt="OccupyWallSt.org logo" class="natural" /></a></p>

<blockquote>

<p><em>"Since the eviction of the protestors at Freedom Plaza last November, it’s become a media cliché to report on the “Death of Occupy.” Articles pop up all over the web, blithely reporting on the failed second wind of Occupy, this lackluster “American Spring,” and the May Day general strike that didn’t quite shut the system down.</em></p>

<p><em>It should be no surprise that the mainstream media is eager to report on Occupy’s supposed demise. Even ignoring the fact that the corporate-owned media has a strong desire to never see social movements such as Occupy succeed, the media, as a rule, generally needs to put a dramatic narrative to everything it reports. To them, every story ought to have a captivating story arch with a beginning, middle, and an end."</em></p>

</blockquote>

</div></div>

<div class="g12 mt0 serif"><div class="pad">

<p class="tc mt0 mb0"><a target="_blank" rel="nofollow" href="https://www.reddit.com/comments/mkuww"><img src="https://acko.net/files/occupy-www-street/casually-pepperspray.jpg" alt="Casually pepper spraying cop" class="fit" /></a></p>

</div></div>

<div class="g10 i1 mt0 serif"><div class="pad">

<blockquote>

<p><em>"In the media’s eyes […] many people heeded the call, [yet] the movement only received its dramatic momentum when cops were photographed attacking and pepper-spraying the nonviolent protestors. It reached its early demise when the police violently cleared out the various encampments. Now, except for a few curmudgeons who can’t seem to understand that Occupy is over, all that remains of Occupy is its populist rhetoric of the 99%—which has been dutifully hawked up by Democratic front-groups such as MoveOn.org to help refuel the Obama election machine."</em></p>

</blockquote>

<p>So much for that then. What's on TV tonight? A presidential debate?</p>

<blockquote>
<p><em>"This popular narrative […] has been so successful that even those who are still active within the Occupy movement can’t help but absorb parts of it. […Meetings] sometimes border on something closely resembling a public support group. On the internet, vaguely self-congratulatory Paul Krugman-y articles, applauding Occupy for “at least shifting the public dialogue,” are posted and reposted […] to remind each other that Occupy at least had a little bit of an effect."</em></p>
</blockquote>

<p>Whelp, that sounds like fun. The post continues to explain at length how Americans see history as <em>"something other people (usually white, upper class men) did long ago"</em> and forget it <em>"took hundreds of thousands of people—especially young people, women, and working class men–to support and spread the ideas of democracy. […] [We] see Occupy as a spectacle […] by people very unlike ourselves. […] Occupy-friendly celebrities and artists, black block style anarchists, and our cities’ despotic mayors are the characters in this drama, [battling it out] for headlines on the stage of our trash-strewn cities."</em></p>

<blockquote>
<p><em>"The true magic of Occupy was that it rejected all of these things. No one had any more power than anyone else, […] nobody in Occupy really cared that we were ignored by the mainstream media. We don’t need a bunch of hacks at Time Magazine to commend us for our ability to protest. […] The goal of Occupy was to get together as a community of equals, to claim a future different than the ones they gave us, and to reignite a tradition of democratic progress […] It was a way of exploring new ways of interacting with others."</em></p>
</blockquote>

</div></div>

<div class="c m1"></div>

<div class="wide mt3">
  <img src="https://acko.net/files/occupy-www-street/occupy-vancouver.jpg" alt="Occupy Vancouver" />
</div>

<div class="g8 i2 mb1">
<p class="credit"><a target="_blank" rel="nofollow" href="http://www.vancouverobserver.com/blogs/thescene/2011/10/15/occupy-vancouver-protesters-photos">1</a> <a target="_blank" rel="nofollow" href="http://weheartit.com/entry/group/210433">2</a> <a target="_blank" rel="nofollow" href="http://www.vancouverobserver.com/politics/news/2011/11/06/what-are-occupy-vancouvers-demands">3</a> <a target="_blank" rel="nofollow" href="https://skepticedge.wordpress.com/">4</a> <a target="_blank" rel="nofollow" href="http://jodieponto.com/photoblog/2011/10/16/occupy-vancouver/">5</a></p>
</div>

<div class="g10 i1 mt0 serif"><div class="pad">
  
<p>The author insists we should not <em>"[shrug] off Occupy as a momentary fad or a leftist pipedream."</em> Ok. Aside from a <em>"feeling in the air"</em>, the listed effects are that <em>"over 7,200 Occupy protestors have been arrested in the United States"</em> with many <em>"beaten and tortured."</em> Occupy has been demonized because <em>"the media has been strong-armed into reporting […] in an unfavorable light."</em> What else?</p>

<blockquote><p><em>"It needs to be remembered that the word “occupy” is […] a call to action, not the action itself. [It] was useful for getting individuals and organizations previously isolated or focused on one-issue grievances out into the streets. […] What is important is that there’s wide community of opposition being formed across many social barriers, and those who hold power are very afraid."</em></p></blockquote>

<p>Ignore the posturing at the end, notice something far more telling.</p>

<p>The stated result of Occupy, in a post meant to prove its lasting value, is that 7,200 people made <em>victims and martyrs</em> out of themselves, sustained by passive idealistic resistance from abroad. They have slid into depressed irrelevance, misrepresented by those who should report in the public interest. They were ok with this.</p>

<p>The best lies are half truths. This collective totally-not-mourning ritual was a kernel of rock-hard reality wrapped in narcissistic self-delusion. One might even call it Stockholm Syndrome. These are the evolved Pokémon variants of victim culture, where victimhood is celebrated, because it can offer immense power.</p>

<h2 class="mt3">Burgers and Fries with Ketchup</h2>

<p>I especially don't buy into the official Occupy story, either the media's or the movement's, because I went and saw for myself in Vancouver. I'm not talking about the 9/11 Truthers or 420 weed signs: they come with the territory, where else would they go? No, I noticed two groups in particular.</p>

<p>First, the starry eyed latté kids manning the <em>human microphone</em> as they called it—just like in Biscotti Park!—with matching friends too fashionably kept to be poor. It struck me as sinister: because of the Occupy-wide suppression practice of banning audio equipment, they were reduced to slowly repeating each other, chanting mantras. This was further sabotaged by a general lack of focus or moderation. Throw in a mix of people who have more feelings than they know what to do with, and it goes nowhere veeeery slowly, like Twitter in bullet time.</p>

</div></div>

<div class="c"></div>

<div class="wide mt1">
  <a target="_blank" rel="nofollow" href="http://www.matthewjohnburgess.com/essays-on-writing/" class="block"><img src="https://acko.net/files/occupy-www-street/human-mic.jpg" alt="The human microphone" /></a>
</div>

<div class="c"></div>

<div class="g10 i1 mt2 serif"><div class="pad">

<p>This sight was no public forum for enlightened debate, no town hall gathering, it was an obnoxious soapbox no ordinary person would listen to for more than 5 minutes. They had heard of activism, even knew what it looked and sounded like, but they merely went through the motions. Same on many livestreams I tuned into elsewhere.</p>

<p>Second, the actual homeless of the city: unemployed and unemployable, mentally ill, chronically addicted or otherwise rejected by society, mulling around the space they already occupy every day. Put both groups together, quelle surprise, high minded principles about equality, privilege, diversity, ableism, free expression and shared ownership are tested. Youth whose idea of camping is a summer festival with sanitized portapotties and water stations found a public square in winter less accommodating. Furthermore, when the inevitable conflict and crime occurs, those who reach for the words <em>"triggered"</em> and <em>"rape culture"</em> like candy are probably not the best arbiters of actual justice.</p>

</div></div>

<div class="c"></div>

<div class="wide mt2">
  <a target="_blank" rel="nofollow" href="https://www.saybrook.edu/newexistentialists/posts/10-31-11"><img src="https://acko.net/files/occupy-www-street/occupy-empathy.jpg" alt="Scene from Occupy" /></a>
</div>

<div class="c"></div>

<div class="g10 i1 mt2 serif"><div class="pad">

<p>Instead of facing reality, they adopted the <a target="_blank" rel="nofollow" href="http://www.webcitation.org/69wFEoszj" title="http://www2.timesdispatch.com/news/rtd-opinion/2011/nov/04/tdopin02-hinkle-ows-protesters-have-strange-ideas--ar-1433590/">progressive stack</a>, which <em>"encourages women and traditionally marginalized groups [to] speak before men, especially white men."</em> If you dig into the grapevine, you will <a target="_blank" rel="nofollow" href="https://sli.mg/Vkzcmj">find grumblings</a> of <em>"meetings derailed by women and minorities obsessed with identity politics. […] [Plans] were almost always met with objection from feminists, queer activists, blacks, latinos, et al. A lot of the time these people didn't even have specific points of concern. […] They just stood up and started complaining about lack of diversity, the victimization of their specific identity group, etc. […] Smart, able, organized white guys left the protest. They threw up their hands and walked away."</em></p>

<p>You can find many stories of in-fighting and abuse, it provided ample fodder for the media, social or otherwise. What did or did not occur at specific events was rarely considered relevant, and such concerns became more important than the financial and economic system or the institutions that sustain it.  We apparently had to <a target="_blank" rel="nofollow" href="http://www.dominionpaper.ca/articles/4268">confront the gender-based violence in the Occupy movement</a>: <em>"Exactly what occurred is unclear. Some claim there was an attempted rape. Others shrug off the incident as nothing more than an invasion of a young woman’s personal space by an intoxicated man."</em> Do you suppose that distinction might be important?</p>
  
<p>Socially-just media <a target="_blank" rel="nofollow" href="http://www.xojane.com/issues/occupy-wall-street-rape">pushed the hyperbole</a>, reminding men <em>"don't rape anyone, ever"</em>—thx xojane—and that <em>"women-only spaces are built to expedite advanced conversations about issues that affect women (and, ideally, anyone who does not identify as a man) without these discussions being constantly interrupted by the need to educate other folks in the gathering."</em></p>

<p>So in other words… Men, stay out, shut up. Women, don't ask any questions. Great job social justice, you brought everything you claim to abhor into your idealistic little utopia, chasing off everyone else.</p>

<p>It's easy to point at the whacky fringe characters with funny hair and glasses as the culprits. If you're Stephen Colbert, you can even <a target="_blank" rel="nofollow" href="http://newsbusters.org/blogs/pj-gladnick/2011/11/05/colbert-ows-interviewee-ketchup-reveals-self-wacko">mercilessly mock them in front of millions</a> and score lots of points. The real problem is the activist mass itself and the media they buy into. They're too eager to derail themselves over issues they themselves admit are just a microcosm of a human community. The mistake is in assigning universal importance to isolated incidents while ignoring the elephants in the room. That's a disease concentrated in the media, but not only there.</p>

<p>It's difficult to take someone's <em>Important Conversation about <a target="_blank" rel="nofollow" href="http://www.fudco.com/chip/deconstr.html">Deconstructing Issues</a></em> seriously, when social justice doesn't know how to socialize.</p>

</div></div>

<div class="c"></div>

<div class="wide mt1">
  <a target="_blank" rel="nofollow" href="http://newsbusters.org/blogs/pj-gladnick/2011/11/05/colbert-ows-interviewee-ketchup-reveals-self-wacko"><img src="https://acko.net/files/occupy-www-street/ketchup.jpg" alt="Burgers and Fries and Ketchup" /></a>
</div>

<div class="c"></div>

<div class="g10 i1 mt2 serif"><div class="pad">
  
<h2>/Chomsky/ was Right</h2>

<p>In <em>Language and Responsibility</em> (1979), Noam Chomsky instead converses eloquently. He mainly explains his theories of linguistics, too niche for most. However, the book's opening chapter "Politics" starts off quite differently, with ideas that would later return in <em>Manufacturing Consent: The Political Economy of the Mass Media</em> (1988). Asked how his methods relate to his more well known political writings:</p>

</div></div>

<div class="c m1"></div>

<aside class="g8"><div class="pad">
  <a target="_blank" rel="nofollow" href="http://www.vulgartrader.com/post/32392205367/hipster-chomsky"><img src="https://acko.net/files/occupy-www-street/chomsky.jpg" alt="Noam Chomsky" /></a>
</div></aside>

<div class="g4 serif"><div class="pad">

<blockquote class="flush">
<p><em>"I don't have access to any unusual methods of analysis and what special knowledge I have concerning language has no bearing on social and political issues. […] For the analysis of ideology […] a bit of open-mindedness, normal intelligence, and healthy skepticism will generally suffice."</em></p>
</blockquote>

</div></div>

<div class="g10 i1 mt1 serif"><div class="pad">

<blockquote>
<p><em>"Take [the] intelligentsia in a society like ours. This social class, which includes historians and other scholars, journalists, political commentators and so on, undertakes to analyze and present some picture of social reality. […] They serve as mediators between the social facts and the mass of the population: they create the ideological justification for social practice. Look at the work of the specialists in contemporary affairs and compare their interpretation with the events, compare what they say with the world of fact. You will often find great and fairly systematic divergences […]."</em></p>

<p><em>"With a little industry and application, anyone who is willing to extricate himself from the system […] will readily see through the modes of distortion […] Everybody is capable of doing that. […] Often carried out poorly, […] social and political analysis is produced to defend special interests rather than to account for actual events."</em> <em>(p3-4)</em></p> 
</blockquote>

<p>He goes on to talk about the particular situation in the United States. He compares recorded facts with the accepted explanations of events such as the McCarthyist scare, the student revolution, the civil rights movement, the Vietnam war, and more. He covers US interventions at home and abroad, legitimate or illegitimate. The overriding theme is rather obvious:</p>

<blockquote>
<p><em>"Here in the United States there is an astonishing degree of ideological uniformity for such a complex country. Basically, there are two reasons for this. First, there is the remarkable ideological homogeneity of the American intelligentsia in general, who rarely depart from one of the variants of state capitalistic ideology (liberal or conservative), a fact which itself calls for explanation. The second is that the mass media are capitalist institutions. […] The fact that these institutions reflect the ideology of dominant economic interests is hardly surprising."</em> <em>(p9)</em></p>
</blockquote>

<p>An editorial in the <em>New York Times</em> from 1975, a retrospective on the Vietnam War, explains there are those who think <em>"the war […] could have been waged differently"</em> and those who think <em>"a viable non-Communist South Vietnam was always a myth."</em> Chomksy goes all in:</p>

<blockquote>
  
<p><em>"They don't even mention the logical possibility of a third position: namely, that the United States did not have […] the legal or moral right, to intervene by force in the internal affairs of Vietnam. […] Note that as the <em>Times</em> sets the spectrum of ideological debate, the position of much of the peace movement is simply excluded from consideration. Not that it is wrong, but rather unthinkable, inexpressible. […]</em></p>

<p><em>Here we have a marvellous illustration of the functioning of propaganda in a democracy. A totalitarian state simply enunciates official doctrine—clearly, explicitly. […] one can only express opposition at one's peril. In a democratic system of propaganda no one is punished (in theory) for objecting to official dogma. In fact, dissidence is encouraged.</em></p>

<p><em>What this system attempts to do is to fix the limits of possible thought: supporters of official doctrine at one end, and the critics—vigorous, courageous, and much admired for their independence of judgement—at the other. The hawks and the doves. But we discover they all share certain tacit assumptions, and that it is these assumptions that are really crucial.</em></p>

<p><em>No doubt a propaganda system is more effective when its doctrines are insinuated rather than asserted, […] The more vigorous the debate, the more effectively the basic doctrines of the propaganda system, tacitly assumed on all sides, are instilled. Hence the elaborate pretense that the press is a critical dissenting force—maybe even too critical for the health of democracy—when in fact it is almost entirely subservient to the basic principles of the ideological system."</em> <em>(p36-39)</em></p>
</blockquote>

<p>This was the 70s. What might that look like in the internet age?</p>


<h2 class="mt3">Don't Stalk Me Bro</h2>

<p>In 2006, Gawker Media launched the <a target="_blank" rel="nofollow" href="http://gawker.com/160338/introducing-gawker-stalker-maps">Gawker Stalker Maps</a>, a site where visitors were encouraged to share live updates of celebrity sightings in public. Hollywood stars found the usual gaggle of papparazzi joined by even weirder people from the internet, and were not amused.</p>

<p class="tc"><a target="_blank" rel="nofollow" href="https://www.youtube.com/watch?v=2-avakrRUaU" class="block"><img src="https://acko.net/files/occupy-www-street/gawker-stalker-kimmel.jpg" class="natural" /></a></p>

<p>When called to defend the practice a year later by Jimmy Kimmel, editor Emily Gould <a target="_blank" rel="nofollow" href="https://www.youtube.com/watch?v=2-avakrRUaU">goes for the gold in mental gymnastics</a>. Despite the original request for sightings <em>"as quickly as you can send them in,"</em> she claims the map is generally slow to update. She laughs away evidence of publishing lies as being <em>"citizen journalism"</em> which <em>"everybody knows […] isn't checked,"</em> despite much of the content being editorial. She trips and fails to catch herself, calling it <em>"a stalking…heh…it's not <em class="normal">actual</em> stalking",</em> because there is no expectation of not being noticed in public, and it's just acknowledging celebrities as real people. She then immediately asks if celebrities aren't <em>"protected by piles of money from any rocks thrown at them,"</em> gets called out and backpedals that <em>"it's not okay to say false things about anyone,"</em> invalidating everything she said before.</p>

<p>Kimmel throws a rather fun anachronistic zinger too: <em>"I don't know why anyone would buy advertising on a web site."</em></p>

<p>It's a rare occasion. Distant celebrities are humanized, old media takes new media to task, and the cheerful talking head is revealed for the blatant opportunist that she is. A chance to show the media does know better, right? Aaron Sorkin saw it too and featured this teachable moment in an episode of HBO's The Newsroom, <a target="_blank" rel="nofollow" href="https://www.youtube.com/watch?v=SWjREXKdXbM">lifting lines of dialog verbatim</a>:</p>

</div></div>

<div class="c"></div>

<div class="wide mt1">
  <a target="_blank" rel="nofollow" href="https://www.youtube.com/watch?v=SWjREXKdXbM"><img src="https://acko.net/files/occupy-www-street/the-newsroom.jpg" alt="The Newsroom" /></a>
</div>

<div class="c"></div>

<div class="g10 i1 mt1 serif"><div class="pad">

<p>Except of course, all the sharp edges have been filed off. Instead of a bubbly editor of an internet gossip rag, there's a pudgy Zuckerberg-type pushing his tech startup and app. His protestations seem more genuine, and he's taken aback by the sudden accusations, rather than invited to explicitly address them. The celebrities are not represented, they are off-screen, being stalked and murdered. He's an oblivious technocrat harming society with his self-serving autism, she's the approachable-yet-merciless reporter bringing him to task.</p>

<p>You see how a story that was entirely about the media was turned into something completely different, without even changing most of the words? It's all in the delivery and presentation. The excuse that it can't be entirely realistic or literal does not apply. I find the whole thing rather <em>convenient</em> in pushing certain narratives. It confuses the sociopaths who run certain businesses with the people who maintain the tech, and clear-coats it with some subtle nerd bashing.</p>

<p>It could be a pretty good example of what Chomsky was talking about: when presenting an interpretation of fact, American gatekeepers apply a lens that shines only on a very narrow and predictable window, while insisting otherwise. Like showing you <em>how news is actually made</em>, an <em>elaborate pretense that the press is a critical dissenting force</em>.</p>

</div></div>

<div class="c"></div>

<div class="wide mt1">
  <a target="_blank" rel="nofollow" href="https://www.youtube.com/watch?v=SWjREXKdXbM"><img src="https://acko.net/files/occupy-www-street/the-newsroom-2.jpg" alt="The Newsroom" /></a>
</div>

<div class="c"></div>

<div class="g10 i1 mt1 serif"><div class="pad">

<p>Or it could just be a mediocre episode of a faltering TV show. But then I'm sure there are countless opinion pieces and comments already that debate this in excruciating detail.</p>

<p>Either way, in trying to bring reality into their fantasy, they have remade it in their own image. They tend to do that a lot. Not just the ones who <a target="_blank" rel="nofollow" href="http://newsdiffs.org/diff/934341/934454/www.nytimes.com/2015/07/11/technology/ellen-pao-reddit-chief-executive-resignation.html">write fiction</a>.</p>

<h2 class="mt3">Celebrate Diversity</h2>

<p>Where might we have seen all of this again recently? Let me lay it all out. What do you do when you're a notable reporter in entertainment, like say, video games, who finds that trade press is mostly obsolete in an age of YouTube, Twitch, Twitter, Reddit and more?</p>

<p>Well, you might have an existential crisis when you realize you were just being a <a target="_blank" rel="nofollow" href="https://twitter.com/leighalexander/status/464779743175655425">drunk megaphone</a> for corporate marketing interests, and <a target="_blank" rel="nofollow" href="http://deepfreeze.it/article.php?a=unfair#alexander">become a mouthpiece for your own interests</a> instead. Get on Twitter and post <em>"Fuck ethics get money,"</em> give talks in which you <em>"have no pretense of being unbiased."</em> Feature people and projects you like, promote political and social causes you think are important, and start picking the exclusives yourself instead of waiting for them to drop in your lap. All the while partaking in the global race to the bottom that is <a target="_blank" rel="nofollow" href="http://deepfreeze.it/article.php?a=quickdirty">modern clickbait</a>.</p>

<p>If enough people in a clique do this, it would create the appearance of an indie renaissance. However it would only feed attention to a very narrow scope of ideas, accessible only to those with specific connections who say the right things. If done in the context of a multi-billion dollar industry, this might create some resentment. Especially if there was <a target="_blank" rel="nofollow" href="http://www.tiki-toki.com/timeline/entry/343871/Corruption-consumer-hate-and-bad-journalism-in-games-journalism/">years of dubious reporting, conflicts of interest and agenda pushing</a> leading into it, with several careers permanently tarred and feathered through irresponsible rumour and lies. <a target="_blank" rel="nofollow" href="https://paxdickinson.wordpress.com/2014/10/22/moral-panics-and-the-death-of-fun/">Hypothetically</a> <a target="_blank" rel="nofollow" href="http://www.escapistmagazine.com/articles/view/video-games/gamergate-interviews/12397-Brad-Wardell-GamerGate-Interview.2">speaking</a>, of course.</p>

</div></div>

<div class="c"></div>

<div class="wide mt1">
  <a target="_blank" rel="nofollow" href="http://deepfreeze.it"><img src="https://acko.net/files/occupy-www-street/ramiel.jpg" alt="Ramiel" /></a>
</div>

<div class="c"></div>

<div class="g10 i1 mt1 serif"><div class="pad">

<p>In such an environment, imagine evidence pops up that one of the media clique's new darlings is actually a rather disturbing individual. While profiling herself as a paragon of virtue and a talented progressive activist and game developer, she manipulates and lies her way through her career and relationships. This comes in the form of a <a target="_blank" rel="nofollow" href="https://thezoepost.wordpress.com">warning and a detailed description of emotional abuse</a>, proven with, say, lengthy Facebook citations. The details are quite lurid and salacious, namely infidelity with a set of eponymous Five Guys in the industry, including her boss and a Kotaku reporter. On top of that, she is seen to wear the Emperor's New Clothes, in that her "work" mainly consists of a single choose-your-own-adventure Twine book that would barely pass as a first year student's midterm project. It was praised mainly for its subject—depression—rather than its execution.</p>

<p>Imagine that in response to this, other acquaintances come out of the woodwork to describe <a target="_blank" rel="nofollow" href="http://gamergate.wikia.com/wiki/Zoe_Quinn">other things she possibly did</a>. Like <a target="_blank" rel="nofollow" href="http://i.imgur.com/moFXvoa.jpg">framing a forum of depressed people as harassers</a> for attention, <a target="_blank" rel="nofollow" href="http://knowyourmeme.com/photos/813691-quinnspiracy">leaking personal data</a> on a rival charity, getting friends in media to <a target="_blank" rel="nofollow" href="https://www.reddit.com/r/KotakuInAction/comments/2gtqoq/the_story_of_the_fine_young_capitalists_and_the/">blacklist them</a>, sabotaging a <a target="_blank" rel="nofollow" href="http://knowyourmeme.com/photos/815901-quinnspiracy">reality TV game event</a> she was in, raising money for another event that <a target="_blank" rel="nofollow" href="http://apgnation.com/articles/2014/09/09/6977/truth-gaming-interview-fine-young-capitalists">never happened</a>, and generally being resented by past contacts.</p>

<p>More importantly, imagine that this scandal leads to <a target="_blank" rel="nofollow" href="https://www.reddit.com/r/Games/comments/2ejs7v/gaming_journalists_patricia_hernandez_of_kotaku/">other conflicts of interest</a> being revisited or discovered, <a target="_blank" rel="nofollow" href="http://kotaku.com/a-different-way-to-respond-to-a-rape-accusation-update-1605542083">previous media lynchings</a> to be brought up and accountability to be demanded, because it involves <a target="_blank" rel="nofollow" href="https://www.youtube.com/watch?v=0QLz0CqtMVc#t=121">multiple influential outlets</a> and a lot of people are <a target="_blank" rel="nofollow" href="http://www.gamespot.com/articles/who-wins-when-mass-effect-3s-ending-changes/1100-6367380/">tired of it</a>.</p>

</div></div>

<aside class="g7"><div class="pad">
  <p><a class="block" target="_blank" rel="nofollow" href="http://www.gamasutra.com/view/news/224400/Gamers_dont_have_to_be_your_audience_Gamers_are_over.php"><img src="https://acko.net/files/occupy-www-street/gamers-are-over.jpg" alt="Gamers are over" /></a></p>
</div></aside>

<div class="g5 serif"><div class="pad">
  
<p>Rather than admit it's all become a big joke and everyone's been played, reporters might choose to close ranks instead. They might concoct a tale that all the criticism she receives is harassment, that gamers are <a target="_blank" rel="nofollow" href="http://www.gamasutra.com/view/news/224400/Gamers_dont_have_to_be_your_audience_Gamers_are_over.php">obtuse shitslingers and wailing hyperconsumers</a> who hate women, declaring the gamer identity to be dead in <a target="_blank" rel="nofollow" href="https://pixietalksgamergate.wordpress.com/gamers-are-dead-article-analysis/">ten articles published in 24 hours</a>. Though it would alienate their core readership, it would please the newer, more political segment, who would see all their existing biases about video games and gamers confirmed, being ignorant of the medium's established history of <a target="_blank" rel="nofollow" href="http://torment.wikia.com/wiki/Planescape:_Torment">complicated themes</a> being <a target="_blank" rel="nofollow" href="http://silenthill.wikia.com/wiki/Silent_Hill_2">explored maturely</a>.</p>

</div></div>

<div class="g12 mt0 serif"><div class="pad">
  
<p class="mt0 mb2"><a class="block" target="_blank" rel="nofollow" href="http://www.gamasutra.com/view/news/224400/Gamers_dont_have_to_be_your_audience_Gamers_are_over.php"><img src="https://acko.net/files/occupy-www-street/gamers-are-over-2.png" alt="Gamers are over" /></a></p>

</div></div>

<div class="g10 i1 mt0 serif"><div class="pad">
  
<p class="mt0">New readers might see it as a license to step in, educate the plebs and defend the fair maidens from the supposed savage horde, in full sight of friends on social media, <a target="_blank" rel="nofollow" href="http://knowyourmeme.com/memes/nods-respectfully-towards-you">nodding respectfully</a>. They might rephrase events as a "jilted ex"—even though he dumped <em>her</em>—who has been rallying a hate mob on the evil depths of 4chan—even though he <a target="_blank" rel="nofollow" href="https://medium.com/@srachel_m/gamergate-launched-in-my-apartment-and-internet-im-sorry-not-that-sorry-13e5650fd172">posted on Something Awful and Penny Arcade first</a> and reaction was mixed. They might speak of him leaking "revenge porn" when in actuality it's her own <a target="_blank" rel="nofollow" href="http://www.skepticink.com/incredulous/2014/10/04/zoe-quinns-lying-cheating-claim-stabbing-killing-man-alleged-former-photographer/">paid erotica</a> circulating. All of these are either outright lies or an admission of not having bothered to read <a target="_blank" rel="nofollow" href="http://www.gamergatewiki.com/index.php/Timeline/Full">source material</a>.</p>

<p>It would be vital for reporters to never mention the coordinated censorship that might start to take place on Reddit, 4chan and other gaming forums, and the resulting Streisand effect that would only draw in more attention. This could include <a target="_blank" rel="nofollow" href="http://greggreenwell.tumblr.com/post/95136135894/zoe-quinn-is-using-false-dmca-claims-to-take-down">filing a false DMCA claim</a> on a prominent YouTuber, turning a <a target="_blank" rel="nofollow" href="http://www.reddit.com/r/gaming/comments/2dz0gs/totalbiscuit_discusses_the_state_of_games">25,000 comment thread into a graveyard</a>, banning people for merely mentioning certain names, and declaring the entire topic off-limits on certain <em>allied</em> subreddits. As moderator favors would be traded in behind the scenes to accomplish this, it might create the appearance of <a target="_blank" rel="nofollow" href="http://blogjob.com/oneangrygamer/2015/03/reddit-mods-admit-to-censoring-gamergate-hiding-corruption/">even more collusion</a> and even downright conspiracy, leading some to migrate away from 4chan in disgust to a previously minor image board, and later to Voat from Reddit.</p>

<p>Even if someone predicted exactly what would happen, a <a target="_blank" rel="nofollow" href="https://archive.4plebs.org/pol/thread/34317761/">week in advance</a>, the games press would not, under any circumstance, face facts and admit error. Instead they would begin a long and drawn out media war against consumers who mainly want accountability, and in doing so, become the biggest magnet for trolling, drama, e-celebs and false flags the internet would see since its inception. Especially ones <a target="_blank" rel="nofollow" href="http://gamergate.wikia.com/wiki/Reddit">the old internet already knew about</a>.</p>

<p>Exactly like abuse victim Eron Gjoni warned, before being gagged by a contested and <a target="_blank" rel="nofollow" href="https://www.reddit.com/r/KotakuInAction/comments/3hgq3b/zoe_quinn_moves_to_vacate_restraining_order_eron/">possibly unconstitutional legal order</a>, a whole segment of an industry might <a target="_blank" rel="nofollow" href="http://gawker.com/how-we-got-rolled-by-the-dishonest-fascists-of-gamergat-1649496579">go insane</a> the subsequent months, over an abuser's self-serving machinations. Gaming and feminist media would have material aplenty with their new scapegoat. As the global news press is really just one big game of telephone, they would all report whatever these <em>experts</em> told them. They'd wield guilt by association like a club without ever bothering to investigate, obliviously committing a <a target="_blank" rel="nofollow" href="https://duckduckgo.com/?q=gamergate+harassment">big lie</a> so massive it would give Goebbels throbbing priapism. Like say, if one <a target="_blank" rel="nofollow" href="https://sli.mg/NUIXim">untraceable internet comment</a> nobody can substantiate was momentarily turned into the <a target="_blank" rel="nofollow" href="https://duckduckgo.com/?q=felicia+day+doxxed">mushroom kingdom's death of Princess Diana</a>. If random IRC quotes were <a target="_blank" rel="nofollow" href="http://www.theguardian.com/technology/2014/sep/12/zoe-quinn-gamergate-online-hate-mobs-depression-quest">taken as spy-thriller facts</a> about the opinions and motivations of thousands, without further investigation or right of reply. They might accept the word of a serial liar and self-admitted <a target="_blank" rel="nofollow" href="https://twitter.com/TheQuinnspiracy/status/166659770248474624">helldump veteran</a>, starring as both Archduke Ferdinand and <a target="_blank" rel="nofollow" href="https://twitter.com/adriarichards/status/313442430848487424">Joan of Arc</a> in her very own <a target="_blank" rel="nofollow" href="http://knowyourmeme.com/memes/events/donglegate-adria-richards">DongleGate</a>.</p>

<p>All because of <em>vidya</em>, video games, all because a bunch of reporters found themselves <a target="_blank" rel="nofollow" href="http://deepfreeze.it/journo.php">kings and queens</a> of a castle nobody really needed after all. They found an audience convinced Twitter and Tumblr was how you did activism, and that <a target="_blank" rel="nofollow" href="http://www.thecrimson.com/column/words-words-words/article/2015/3/12/simplistic-social-justice-warrior/">campuses</a> and <a target="_blank" rel="nofollow" href="http://www.examiner.com/article/calgary-expo-censors-the-honey-badger-brigade-s-anti-censorship-message">conventions</a> should be places where <a target="_blank" rel="nofollow" href="https://www.youtube.com/watch?v=iARHCxAMAO0">good faith inquiry goes to die</a>.</p>

<p>But that would be crazy to imagine.</p>

</div></div>

<div class="g12 mt0 serif"><div class="pad">

<p class="mt0 mb0"><a target="_blank" rel="nofollow" href="http://knowyourmeme.com/memes/events/shirtstorm"><img src="https://acko.net/files/occupy-www-street/matt-taylor-shirt.jpg" alt="Matt Taylor and his shirt" class="fit block"></a></p>

</div></div>

<div class="g10 i1 mt0 serif"><div class="pad">

<p>Even crazier would be to see that, unlike every single time it's happened before, this kind of entryism-by-way-of-social-justice met a wall of defiant opposition. That somehow the impossible happened, and people from <a target="_blank" rel="nofollow" href="https://www.reddit.com/r/KotakuInAction/">Reddit</a> and <a target="_blank" rel="nofollow" href="https://8ch.net/">4chan</a> could put aside their differences long enough to actually <a target="_blank" rel="nofollow" href="https://gitgud.io/gamergate/gamergateop/tree/master/Operations/Operation-Disrespectful-Nod">get some real work done</a>. That all the <a target="_blank" rel="nofollow" href="https://www.reddit.com/r/tumblrinaction">oppression olympics</a>, the <a target="_blank" rel="nofollow" href="http://knowyourmeme.com/memes/events/shirtstorm">media shaming</a>, <a target="_blank" rel="nofollow" href="https://duckduckgo.com/?q=tim+hunt">the misrepresentation</a>, the <a target="_blank" rel="nofollow" href="http://slatestarcodex.com/2014/07/07/social-justice-and-words-words-words/">wordplay</a>, the <a target="_blank" rel="nofollow" href="http://esr.ibiblio.org/?p=2122">fallacies</a> and <a target="_blank" rel="nofollow" href="https://www.youtube.com/watch?v=IAF2UmyXe-4">moral posturing</a> had finally pushed enough people over the edge to say: no, for once, <em>you</em> move. That they might try turning Twitter into the closest thing it will ever be to a rational discussion space, which is to say, not at all. That people there would be so addicted to digital validation, they would mistake their clumsy ideas being dismantled for harassment. They might even <a target="_blank" rel="nofollow" href="https://duckduckgo.com/?q=cyberviolence+at+the+UN">call it cyberviolence</a> at the UN, cherry picking a few idiots out of the mass for effect, and <a target="_blank" rel="nofollow" href="https://github.com/freebsdgirl/ggautoblocker">autoblocking the rest</a> in a self-fulfilling prophecy.</p>

<p class="mt2 mb2"><iframe style="width: 100%" height="400" src="https://www.youtube.com/embed/8NhIBhbqWo4" frameborder="0" allowfullscreen="allowfullscreen"></iframe></p>

<p>You could discover, one year in, that trying to get a <a target="_blank" rel="nofollow" href="http://spjairplay.com">Society of Professional Journalists</a> to acknowledge rampant selection bias, copypasting, lazyness and pandering is far trickier than it was in 2011 with Fox News: this time there's <a target="_blank" rel="nofollow" href="http://risemiaminews.com/2015/08/breaking-gamer-gate-controversy-prompts-evacuation-of-koubek-center-in-miami/">bomb</a> <a target="_blank" rel="nofollow" href="http://nichegamer.com/2015/05/peaceful-gamergate-in-d-c-meetup-receives-false-bomb-threats/">threats</a> and few seem to care much who sent them, or even know <a target="_blank" rel="nofollow" href="http://gamepolitics.com/2015/08/12/challenge-accepted-interviewing-internet-hashtag/">how to go about reporting on this</a>. That Wikipedia's own infotocracy <a target="_blank" rel="nofollow" href="https://archive.is/jehPF">switches to</a> (⛔️) printing <a target="_blank" rel="nofollow" href="https://archive.is/rUVo3">outright propaganda</a> (⛔️) for <a target="_blank" rel="nofollow" href="https://archive.is/Nf6Wv">a while</a> (⛔️), stuck in <a target="_blank" rel="nofollow" href="http://i.imgur.com/RnQJ3Hl.png">citogenesis</a> of its <a target="_blank" rel="nofollow" href="https://www.reddit.com/r/KotakuInAction/comments/2yfrfb/why_did_ryulong_get_banned_from_wikipedia/">own creation</a>.</p>

<p>Because it's far more <a target="_blank" rel="nofollow" href="https://www.patreon.com/zoe">lucrative</a> to <a target="_blank" rel="nofollow" href="http://www.theguardian.com/technology/2014/oct/17/brianna-wu-gamergate-human-cost">play dumb</a> after <a target="_blank" rel="nofollow" href="https://images.encyclopediadramatica.se/7/71/Brianna_Wu_Meme_original.png">using autistic kids</a> for <a target="_blank" rel="nofollow" href="http://www.cjr.org/the_kicker/gawker_bullying.php">laughs</a> from a position of <a target="_blank" rel="nofollow" href="https://pbs.twimg.com/media/B1Pg0IgIYAEKXX_.png:large">unearned privilege</a>.</p>

<h2 class="mt3">Brave New World</h2>

<p>So let's come full circle on this horseshoe of insanity.</p>

<p>What if it turned out that Occupy's much desired <em>"new way of interacting with others"</em> already existed since 2001, in the form of the Anonymous Image Board? Perhaps not coincidentally an invention from Japan, a culture that turned repression into an art form. A board where identity and status has no value by default and as a result, <a target="_blank" rel="nofollow" href="http://twentytwo.fibreculturejournal.org/fcj-158-tits-or-gtfo-the-logics-of-misogyny-on-4chans-random-b/">entirely different rules of engagement apply</a>, building on the practices of early cyberspace.</p>

<p>It does not lead to a utopian forum where intellectuals debate like they're on the set of Star Trek. Quite the opposite, this is humans you're dealing with. It leads to places that mix deep skepticism with casual verbal and graphic abuse, making everyone and everything equally worthless and fake until proven otherwise. Bringing identity as a general point of argument is not just unwelcome, it is shouted down with exactly those insults that deal it the most damage. Relevancy comes from resilience, taking offense is an instant defeat. Ingenuity, hyperbole and humor are the controlling measures to balance it out, acting like constant bullshit tests <a target="_blank" rel="nofollow" href="https://www.youtube.com/watch?v=DlSx7m09R60">against the uninspired</a> and <a target="_blank" rel="nofollow" href="https://www.youtube.com/watch?v=WcebgKvAoh0">intellectually bankrupt</a>. Ideas are forced to compete to remain relevant, just with one caveat: no topic is off limits. Especially taboos. It's not exactly for the faint of heart.</p>

<p>It's the closest thing the internet has to a functional universal "safe space", which is to say, not one at all, not by any offline definition. Once you get used to it though, the porn, the trolling, the shilling, the jew jokes, the "faggotry," it all becomes so banal and transparent as to not be worth getting worked up about. Which is exactly how it should be.</p>

<p><iframe style="width: 100%" height="410" src="https://www.youtube.com/embed/3hPFPjQ4pjo?cc_load_policy=1" frameborder="0" allowfullscreen="allowfullscreen"></iframe></p>

<p class="mt2 mb2">Look, while it's equally vulnerable to all the same pettyness and tribalism that made Twitter a success story, it's <a target="_blank" rel="nofollow" href="https://sli.mg/BVK1RH">far more equipped</a> as both a medium and a culture to <a target="_blank" rel="nofollow" href="https://sli.mg/x18eNq">deal with it</a> sustainably. That might include <a target="_blank" rel="nofollow" href="https://sli.mg/Tw3tn4">razing their own village to the ground</a> and <a target="_blank" rel="nofollow" href="https://sli.mg/5z0jV6">rebuilding it anew</a> in times of great distress. Though when it's all made out of trivially replaceable ones and zeroes that do it for free, is that really something other than an address change? It's not that strange: we used to be nomads, before the promise of a full belly and a permanent roof over their heads made people sacrifice a little bit of freedom for a whole lot of security.</p>

<p><iframe style="width: 100%" height="410" src="https://www.youtube.com/embed/Vx5BNDPA5hE" frameborder="0" allowfullscreen="allowfullscreen"></iframe></p>

</div></div>

<div class="g9 mt0 serif"><div class="pad">

<p>There are many who know all this, <a target="_blank" rel="nofollow" href="http://knowyourmeme.com/memes/events/project-chanology">none of this is new</a>, and a generation is growing up with this stuff being as normal as Facebook and Snapchat. Yet others look at the chans as some sort of oblivion gate leading to endless bigots, criminals and harassers. Like 9/11 Truth at Occupy, when you push certain ideas into the counter-culture, don't be surprised when speaking openly requires sharing space with shady neighbours, on top of a very long tail of fringe and genuine marginalization. Once again, you may hate this, but you can't ignore it.</p>

</div></div>

<aside class="g3"><div class="pad">
  <p class="mt0"><a class="block" target="_blank" rel="nofollow" href="http://knowyourmeme.com/memes/mark-bunkerwise-beard-man"><img src="https://acko.net/files/occupy-www-street/wise-beard-man-alt.jpg" alt="Mark Bunker, Wise Beard Man" /></a></p>
</div></aside>

<div class="g10 i1 mt0 serif"><div class="pad">

<p>For older readers: when you think Dungeons and Dragons turns teenagers into witches and warlocks, or that day-cares murder children in Satanic rituals, expect to be mocked as the ignorant panicky fool that you are. They might have learned to <a target="_blank" rel="nofollow" href="http://i.imgur.com/jQ2VBPA.png">build bridges</a> you had no idea about <a target="_blank" rel="nofollow" href="http://www.torychristman.com/2008/02/01/youth-speaks-to-tory-anonymous-vs-scientologys-abuses/">long ago</a>, and they wouldn't need to declare themselves on the right side of history to do it.</p>

<p>Even more hilarious is when people say this about dear old Reddit, like say, in <a target="_blank" rel="nofollow" href="http://idibon.com/toxicity-in-reddit-communities-a-journey-to-the-darkest-depths-of-the-interwebs/">terrible internet studies</a>. They do their best to ignore their own conclusions and their own camp's troublemakers, with a random "but Women in Tech!" thrown in for good measure. They see what they want to see, not realizing that taking everything there at face value is not just noob mistake #1, it's often a mechanism that explicitly works to keep <em>unmotivated</em> outsiders out. The kind of trolls you can spot right away are the <a target="_blank" rel="nofollow" href="http://www.breitbart.com/london/2014/12/10/the-madness-of-queen-shanley/">amateurs</a>, and certain people are so useless they are a net drain on any online community they enter, often free from reproach.</p>

<p>The internet stops being uniquely valuable the moment you insist it should work exactly like real life. Our primary instincts and activities do not scale well beyond a single small tribe, that appears to be both <a target="_blank" rel="nofollow" href="http://beautifultrouble.org/theory/dunbars-number/">empirical fact</a> and <a target="_blank" rel="nofollow" href="http://knowyourmeme.com/memes/greater-internet-fuckwad-theory">lived experience</a>. The default human impulse is to rely on shared identity for social validation. Insecurity leads people to avoid thinking critically, too afraid of going against the grain. They shun ideas rather than tackling them, succumbing to groupthink and echo chambers. The antidote is the same it's always been: good natured humor and reflection, including on oneself, not moral panic.</p>

<p>There's only one question you need to answer to complete the picture. Why are they so eager to believe that all of gaming—or all of society even—hates women and minorities, does not want them around and treats them as inferior, needing special outreach and support, even when there's <a target="_blank" rel="nofollow" href="http://knowyourmeme.com/memes/notyourshield">evidence and testimony</a> to the contrary? I'll give you a hint: social justice warriors are terrible at even just <em>considering</em> opinions that differ from their own. Yet they can't shut up about this one.</p>

<p>The road to hell is not just paved with good intentions, the Devil provides free popcorn at every station.</p>

<p><span class="spoiler" onclick="this.classList.toggle('active')">And when entering rabbit holes, always bring enough fuel to burn back to a <strong><a target="_blank" rel="nofollow" href="https://www.youtube.com/watch?v=F92l2s_bO-k">higher</a> <a target="_blank" rel="nofollow" href="https://www.youtube.com/watch?v=VQtnqU-mEDc">apoapsis</a></strong>.</span></p>

</div></div>

<div class="c m1"></div>

<div class="wide mt2">
  <a target="_blank" rel="nofollow" href="https://kerbalspaceprogram.com/en/"><img src="https://acko.net/files/occupy-www-street/kerbal.jpg" alt="Kerbal Space Program"></a>
</div>

<div class="g10 i1 mt1 serif"><div class="pad">

<p>The <a target="_blank" rel="nofollow" href="https://gitgud.io/gamergate/gamergateop/tree/master/Current-Happenings">media lied</a>, and I saw many whom I thought knew better fall for it. It was an absolute mindfuck to see educated brains fail this hard and don their favorite brown shirt in the name of progress. It's like they never grew past their high school phase, still trying to become one of the cool kids while resenting them for it. Never realizing that moral posturing is a trick that only works on the weak willed, and it only works so long. <a target="_blank" rel="nofollow" href="https://www.youtube.com/watch?v=LHPqUddXCu8">Anita pls stop</a>.</p>
  
<p><a target="_blank" rel="nofollow" href="https://pbs.twimg.com/media/CTJXaFIWIAEEP72.jpg:large">Listen</a> and <a target="_blank" rel="nofollow" href="https://pbs.twimg.com/media/CTJXY8cWsAA-zQC.jpg:large">Believe</a>, take your with-us-or-against-us mindset and go think about what you've done. The internet is under total surveillance, global trade agreements threaten to elevate corporations to the level of states, and the willingness to censor the net is at an all time high. Only this time, it's not just the threat of terrorism, pedophiles and child porn used to scare the masses and silence critics. It's <em>"cyberviolence against women"</em> and fringe <em>"white supremacy."</em> You know what <em>"enables a broken system"</em>? Letting a popular female abuser steamroll over her gaslighted male victim and then shamelessly <a target="_blank" rel="nofollow" href="http://shrink4men.com/2011/01/19/presto-change-o-darvo-deny-attack-and-reverse-victim-and-offender/">swapping all the labels</a> while applying duct tape.</p>

<p>Look around. One of the most prominent press freedom activists lives in house arrest out of an embassy, accused of the internationally villainous crime of having had <a target="_blank" rel="nofollow" href="http://georgewashington2.blogspot.de/2010/12/sex-charges-and-arrest-warrant-against.html">consensual sex but without a condom</a>, i.e. <em>"rape."</em> Sexism and racism have been wielded as perverted weapons of reverse victimhood by those with nothing substantial to offer and everything to gain. The newspaper that broke the Snowden leaks has willingly <a target="_blank" rel="nofollow" href="http://www.theguardian.com/uk-news/tim-hunt">reduced itself to Daily Mail level</a>, shaming scientists instead of focusing on their accomplishments. It's not a conspiracy when the clickbait keeps flowing and people willingly line up to partake, forking over their dough for post-catholic indulgences against patriarchal original sin and white oppressor guilt.</p>

<p>PS: Calling people angry is not a valid retort (we call that "projection"), neither is calling them bigots, harassers or internalized misogynists/homophobes/uncle toms because of things they didn't do. Social justice wanted everyone to educate themselves, so we gave them exactly what they asked for—warts and all—and they and the media continue to misrepresent it. There was no grand deception on the gamers' part, no <a target="_blank" rel="nofollow" href="http://www.forbes.com/sites/erikkain/2015/02/12/law-order-svu-takes-on-gamergate-cant-press-reset-button/">reefer madness hate mob</a> that chases women out of gaming for being women. Only talentless hacks, plenty of characters with unclear intentions, a lot of third party trolls, and many more people trying to work around them while being called names. I'm not <a target="_blank" rel="nofollow" href="https://www.youtube.com/watch?v=rGIY5Vyj4YM">mad as hell</a>, I simply do not wish to take it anymore.</p>

<p>(And they will probably call me right wing, never stopping to think that national socialism created the Volkswagen for a reason. Yes I know, Godwin. <a target="_blank" rel="nofollow" href="https://sli.mg/HdI5Uf">Wir haben es nicht gewußt</a>.)</p>

<p><iframe style="width: 100%" height="380" src="https://www.youtube.com/embed/L61ZCN8rrWw" frameborder="0" allowfullscreen="allowfullscreen"></iframe></p>

<div class="c"></div>

<p class="tc mt4 mb0"><img src="https://acko.net/files/occupy-www-street/darmok-and-jalad.jpg" alt="Darmok and Jalad at Tanagra" class="natural spoiler" onclick="this.classList.toggle('active')" /></p>

<p class="tc mt0"><a target="_blank" rel="nofollow" href="http://knowyourmeme.com/memes/people/ben-garrison"><img src="https://acko.net/files/occupy-www-street/ben-garrison.png" class="flat natural" width="100" height="59" title="No, I'm not Zyklon Ben." /></a></p>

<p class="tc mt3 mb0"><strong>The tree of liberty must be refreshed from time to time<br>with the blood of patriots and tyrants,</strong></p>

<p class="tc mb5 mt0"><span class="spoiler block" onclick="this.classList.toggle('active')">but they tried to use ketchup.</span></p>

<p class="mt3 mb2"><iframe style="width: 60%; margin: 0 auto;" height="240" src="https://www.youtube.com/embed/EKc-fqsIxGk" frameborder="0" allowfullscreen="allowfullscreen"></iframe></p>

<p class="mt2 mb2"><iframe style="width: 60%; margin: 0 auto;" height="240" src="https://www.youtube.com/embed/5MRUgLf72AM" frameborder="0" allowfullscreen="allowfullscreen"></iframe></p>

<p class="mt2 mb5"><iframe style="width: 60%; margin: 0 auto;" height="240" src="https://www.youtube.com/embed/atazfJqCutQ" frameborder="0" allowfullscreen="allowfullscreen"></iframe></p>

</div></div>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Occupy WWW Street (EN)]]></title>
    <link href="http://acko.net/blog/occupy-www-street-en/"/>
    <updated>2015-11-30T00:00:00+01:00</updated>
    <id>http://acko.net/blog/occupy-www-street-en</id>
    <content type="html"><![CDATA[<div class="g8 i2 first serif"><div class="pad">

<h2 class="sub">Internet Activism and Media in the Age of Social Justice</h2>

</div></div>

<div class="c"></div>

<div class="g8 i2 serif"><div>

<p class="outlined lang mt1 mb1 tc"><a href="/blog/occupy-www-street-en/"><strong>English</strong></a> - <a href="/blog/occupy-www-street-fr/">Français</a> - <a href="/blog/occupy-www-street-nl/">Nederlands</a></p>

</div></div>

<div class="g12 serif"><div class="pad">

<blockquote class="mt1 mb2 lift outlined">
  <em class="big">"I don't have access to any unusual methods of analysis and what special knowledge I have concerning language has no bearing on social and political issues. […] For the analysis of ideology […] a bit of open-mindedness, normal intelligence, and healthy skepticism will generally suffice." –&nbsp;NC</em>
</blockquote>

</div></div>

<div class="g10 i1 mt0 serif"><div class="pad">

<p class="outlined">I'm bringing out my serious font <a href="/blog/storms-and-teacups/">again</a>. It's difficult to talk about current affairs without inviting in heaps of context, so I'd like to delve into some recent history.</p>

<p class="outlined"><em>Reminder, shooting the messenger is poor form, and heresy is medieval.</em></p>

<p><em>(🇫🇮 &nbsp;&nbsp;Finnish readers: <a href="/blog/occupy-www-street-fi/">unarchived links</a>)</em></p>

<h2 class="mt4">The Only Working Class Man In The Village</h2>

<p>It's September 2011, the American left has been galvanized. The message is straightforward and aimed at big targets: the 99% of the public is telling the 1% scrooges to stop being greedy and not use corporations as a club. Zuccotti Park in New York fills with activists camping out, discussing and spreading their message.</p>

</div></div>

<div class="c m1"></div>

<div class="wide mt2">
  <a target="_blank" href="https://www.youtube.com/watch?v=6yrT-0Xbrn4"><img src="https://acko.net/files/occupy-www-street/lagreca.jpg" alt="Jesse LaGreca schools Fox News"></a>
</div>

<div class="g10 i1 mt1 serif"><div class="pad">

<p>Cue Jesse LaGreca. With his quintessential American Joe Average look, he takes most folks by surprise, most certainly the Fox News reporter who approaches him. In the <a target="_blank" href="https://www.youtube.com/watch?v=6yrT-0Xbrn4">impromptu interview</a>, unaired by Fox, LaGreca schools the man and powers through, knocking it out of the park. He calls out the leading questions right away, and notes that if there's one thing the news—<em>"the propaganda machine in the media"</em>—doesn't wish to report on, it's itself. That maybe it's time to have some participatory democracy that <em>"isn't funded by news cameras."</em></p>

<p>A reporter shows up expecting to report on the world and discovers this story is, at least in part, about themselves. The denial is quick: <em>"We’re here giving you an opportunity on the record […] to put any message you want out there, to give you fair coverage […] So, there is an exception in the case, because you wouldn’t be able to get your message out there without us."</em></p>

<p>He snaps back: <em>"Certainly. Take for instance when Glenn Beck […] called the president a person who hates white people and white culture. That was a low moment in Americans' history and you guys kind of had a big part in it. So I'm glad you're coming around and kind of paying attention to the [rest of the country] as opposed to the far right fringe who would just prefer to destroy the middle class entirely. […] People would like to see a little bit more economic justice, or social justice, you know, Jesus stuff, as far as feeding the poor, healthcare for the sick. […] I find it really entertaining that people like to hold the bill of rights up while they're screaming at gay soldiers, but they can't wrap their heads around the fact that a for-profit health care system doesn't work. […] If we want the president to do more, let's talk to him on a level that works instead of asking for his birth certificate, […]"</em></p>

<p>Shots fired, critical hit, no chance of recovery. It's no surprise it was left on the cutting room floor, and no surprise it <a target="_blank" href="https://archive.is/aUFoA" title="http://observer.com/2011/10/exclusive-occupy-wall-street-activist-slams-fox-news-anchor-in-un-aired-interview-video/">would leak</a> and shoot to fame as being <em>too extreme for Fox news</em>. Predictable headlines asked if he was the <em>The Smartest Man on Wall Street</em> and explained that <em>Jesse LaGreca Continues to Destroy Media Bias of Occupy Wall Street</em>. All reporters eagerly falling over themselves to demonstrate how <em>they</em> were willing to give anyone a voice.</p>

<p>This was an angle LaGreca himself <a target="_blank" href="https://archive.is/tEjrc#selection-495.650-495.893" title="http://observer.com/2011/10/jesse-lagreca-on-abcs-this-week/">brought up</a> on air: <em>"The reality is that I’m the only working class person you’re going to see on Sunday news… political news… maybe ever. And I think that is very indicative of the failures of our media to report on the news that matter most importantly…"</em> The host immediately cuts in, <em>"We are trying our best, Jesse."</em></p>

<p>However when asked if there was a political demand that could sum up Occupy, something to <em>"make this about,"</em> he takes a curious turn:</p>

<p><em>"I think the entire movement is about economic justice. I mean to me – and I’m not speaking on behalf of Occupy Wall Street, I’m just giving my personal opinion – I think it’s a matter of economic rights, and I think it’s a matter of social rights, and social justice. And to the people who would take offense to the word “social” being placed before the word “justice,” I’d invite them to re-read the Constitution."</em></p>

<p>Ok. I went through the trouble and <a target="_blank" href="https://archive.is/fQr94" title="https://melissafong.wordpress.com/2014/04/04/why-suey_park-wont-enact-the-labour-women-of-colour-dont-owe-you-an-education/">enacted the labor to educate myself</a>. The United States Constitution makes no references to anything social with regards to justice. Those who wrote it seemed to think that merely <em>"establishing Justice"</em> was plenty enough. It does not rank citizens by privilege, does not define powers in relation to oppression and makes no allusions to punching up. That's what social justice is nowadays, right? Or did he mean the <em>"Jesus stuff"</em>? I'm no constitutional lawyer, feel free to correct me.</p>

<p>More awkwardly, on the subject of taking their cues from other protests, in Greece and Europe and elsewhere, LaGreca claimed <em>"we didn’t take our cue leading off of anybody really."</em></p>

</div></div>

<aside class="g5 mt1"><div class="pad">
  <a target="_blank" href="http://www.webcitation.org/63DZ1nIDl" title="http://www.adbusters.org/blogs/adbusters-blog/occupywallstreet.html"><img src="https://acko.net/files/occupy-www-street/adbusters-occupy.jpg" class="fit" /></a>
</div></aside>

<div class="g7 serif"><div class="pad">

<p>Occupy may have grown organically from existing groups, but it was jumpstarted by an early <a target="_blank" href="https://archive.is/55OJB" title="https://www.adbusters.org/blogs/adbusters-blog/million-man-march-wall-street.html">blog post</a> and a <a target="_blank" href="http://www.webcitation.org/63DZ1nIDl" title="http://www.adbusters.org/blogs/adbusters-blog/occupywallstreet.html">message blast to Adbusters' subscriber base</a> of <em>"you 90,000 redeemers, rebels and radicals out there,"</em> declaring a <em>"worldwide shift in revolutionary tactics is underway,"</em> asking if they were ready to have their own <em>"Tahrir moment."</em> The 267 comments show this message was received with enthusiasm and commitment.</p>

<p>LaGreca also wasn't a nobody, he'd been a Daily Kos contributor for three years already. The site's community <a target="_blank" href="https://archive.is/6isVY" title="http://www.dailykos.com/story/2011/10/05/1023035/--UPDATE-Jesse-LaGreca-taking-on-Hannity-NY-Observer-MoT-Smartest-Man-on-Wall-Street">cheered him on</a> for <em>"not [fitting] into the media’s stereotype of the obtusely disenfranchised ne0-hippies"</em> and helped get the word out while commenting on the spectacle: <em>"My guess is that Hannity is scared. As he should be!"</em></p>

</div></div>

<div class="g10 i1 mt1 serif"><div class="pad">

<p>This is not an indictment of Mr. LaGreca, he mainly talked a lot of sense and said it well. However, four years have passed. Despite the generous airtime afforded by his newfound <a target="_blank" href="http://knowyourmeme.com/memes/streisand-effect">fame-by-Streisand</a> and the stated desire from reporters to do better, the situation did not improve, not with Fox News and not with the other outlets who define themselves through opposition.</p>

<p>If there's one thing the media loves, it's the exception that confirms the rule: you cannot galvanize the public without access to existing networks and platforms. When you go up against the media, they will <a target="_blank" href="https://archive.is/IyrOb" title="http://www.tv.com/shows/black-mirror/community/post/black-mirror-15-million-merits-review-138498157166/">repackage you into an entertainment product</a>, and the masses will love it. You might love it too.</p>

<h2 class="mt3">Activists Are Dead</h2>

<p>So what happened? In June 2012, community hub and non-profit OccupyWallSt.org posts that <a target="_blank" href="https://archive.is/KhRmS" title="http://occupywallst.org/article/reports-occupys-death-have-been-greatly-exaggerate/">Reports of Occupy’s Death Have Been Greatly Exaggerated</a>.</p>

<p class="tc"><a class="block" target="_blank" href="https://archive.is/KhRmS" title="http://occupywallst.org/article/reports-occupys-death-have-been-greatly-exaggerate/"><img src="https://acko.net/files/occupy-www-street/occupy-logo.png" alt="OccupyWallSt.org logo" class="natural" /></a></p>

<blockquote>

<p><em>"Since the eviction of the protestors at Freedom Plaza last November, it’s become a media cliché to report on the “Death of Occupy.” Articles pop up all over the web, blithely reporting on the failed second wind of Occupy, this lackluster “American Spring,” and the May Day general strike that didn’t quite shut the system down.</em></p>

<p><em>It should be no surprise that the mainstream media is eager to report on Occupy’s supposed demise. Even ignoring the fact that the corporate-owned media has a strong desire to never see social movements such as Occupy succeed, the media, as a rule, generally needs to put a dramatic narrative to everything it reports. To them, every story ought to have a captivating story arch with a beginning, middle, and an end."</em></p>

</blockquote>

</div></div>

<div class="g12 mt0 serif"><div class="pad">

<p class="tc mt0 mb0"><a target="_blank" href="https://www.reddit.com/comments/mkuww"><img src="https://acko.net/files/occupy-www-street/casually-pepperspray.jpg" alt="Casually pepper spraying cop" class="fit" /></a></p>

</div></div>

<div class="g10 i1 mt0 serif"><div class="pad">

<blockquote>

<p><em>"In the media’s eyes […] many people heeded the call, [yet] the movement only received its dramatic momentum when cops were photographed attacking and pepper-spraying the nonviolent protestors. It reached its early demise when the police violently cleared out the various encampments. Now, except for a few curmudgeons who can’t seem to understand that Occupy is over, all that remains of Occupy is its populist rhetoric of the 99%—which has been dutifully hawked up by Democratic front-groups such as MoveOn.org to help refuel the Obama election machine."</em></p>

</blockquote>

<p>So much for that then. What's on TV tonight? A presidential debate?</p>

<blockquote>
<p><em>"This popular narrative […] has been so successful that even those who are still active within the Occupy movement can’t help but absorb parts of it. […Meetings] sometimes border on something closely resembling a public support group. On the internet, vaguely self-congratulatory Paul Krugman-y articles, applauding Occupy for “at least shifting the public dialogue,” are posted and reposted […] to remind each other that Occupy at least had a little bit of an effect."</em></p>
</blockquote>

<p>Whelp, that sounds like fun. The post continues to explain at length how Americans see history as <em>"something other people (usually white, upper class men) did long ago"</em> and forget it <em>"took hundreds of thousands of people—especially young people, women, and working class men–to support and spread the ideas of democracy. […] [We] see Occupy as a spectacle […] by people very unlike ourselves. […] Occupy-friendly celebrities and artists, black block style anarchists, and our cities’ despotic mayors are the characters in this drama, [battling it out] for headlines on the stage of our trash-strewn cities."</em></p>

<blockquote>
<p><em>"The true magic of Occupy was that it rejected all of these things. No one had any more power than anyone else, […] nobody in Occupy really cared that we were ignored by the mainstream media. We don’t need a bunch of hacks at Time Magazine to commend us for our ability to protest. […] The goal of Occupy was to get together as a community of equals, to claim a future different than the ones they gave us, and to reignite a tradition of democratic progress […] It was a way of exploring new ways of interacting with others."</em></p>
</blockquote>

</div></div>

<div class="c m1"></div>

<div class="wide mt3">
  <img src="https://acko.net/files/occupy-www-street/occupy-vancouver.jpg" alt="Occupy Vancouver" />
</div>

<div class="g8 i2 mb1">
<p class="credit"><a target="_blank" href="http://www.vancouverobserver.com/blogs/thescene/2011/10/15/occupy-vancouver-protesters-photos">1</a> <a target="_blank" href="http://weheartit.com/entry/group/210433">2</a> <a target="_blank" href="http://www.vancouverobserver.com/politics/news/2011/11/06/what-are-occupy-vancouvers-demands">3</a> <a target="_blank" href="https://skepticedge.wordpress.com/">4</a> <a target="_blank" href="http://jodieponto.com/photoblog/2011/10/16/occupy-vancouver/">5</a></p>
</div>

<div class="g10 i1 mt0 serif"><div class="pad">
  
<p>The author insists we should not <em>"[shrug] off Occupy as a momentary fad or a leftist pipedream."</em> Ok. Aside from a <em>"feeling in the air"</em>, the listed effects are that <em>"over 7,200 Occupy protestors have been arrested in the United States"</em> with many <em>"beaten and tortured."</em> Occupy has been demonized because <em>"the media has been strong-armed into reporting […] in an unfavorable light."</em> What else?</p>

<blockquote><p><em>"It needs to be remembered that the word “occupy” is […] a call to action, not the action itself. [It] was useful for getting individuals and organizations previously isolated or focused on one-issue grievances out into the streets. […] What is important is that there’s wide community of opposition being formed across many social barriers, and those who hold power are very afraid."</em></p></blockquote>

<p>Ignore the posturing at the end, notice something far more telling.</p>

<p>The stated result of Occupy, in a post meant to prove its lasting value, is that 7,200 people made <em>victims and martyrs</em> out of themselves, sustained by passive idealistic resistance from abroad. They have slid into depressed irrelevance, misrepresented by those who should report in the public interest. They were ok with this.</p>

<p>The best lies are half truths. This collective totally-not-mourning ritual was a kernel of rock-hard reality wrapped in narcissistic self-delusion. One might even call it Stockholm Syndrome. These are the evolved Pokémon variants of victim culture, where victimhood is celebrated, because it can offer immense power.</p>

<h2 class="mt3">Burgers and Fries with Ketchup</h2>

<p>I especially don't buy into the official Occupy story, either the media's or the movement's, because I went and saw for myself in Vancouver. I'm not talking about the 9/11 Truthers or 420 weed signs: they come with the territory, where else would they go? No, I noticed two groups in particular.</p>

<p>First, the starry eyed latté kids manning the <em>human microphone</em> as they called it—just like in Biscotti Park!—with matching friends too fashionably kept to be poor. It struck me as sinister: because of the Occupy-wide suppression practice of banning audio equipment, they were reduced to slowly repeating each other, chanting mantras. This was further sabotaged by a general lack of focus or moderation. Throw in a mix of people who have more feelings than they know what to do with, and it goes nowhere veeeery slowly, like Twitter in bullet time.</p>

</div></div>

<div class="c"></div>

<div class="wide mt1">
  <a target="_blank" href="http://www.matthewjohnburgess.com/essays-on-writing/" class="block"><img src="https://acko.net/files/occupy-www-street/human-mic.jpg" alt="The human microphone" /></a>
</div>

<div class="c"></div>

<div class="g10 i1 mt2 serif"><div class="pad">

<p>This sight was no public forum for enlightened debate, no town hall gathering, it was an obnoxious soapbox no ordinary person would listen to for more than 5 minutes. They had heard of activism, even knew what it looked and sounded like, but they merely went through the motions. Same on many livestreams I tuned into elsewhere.</p>

<p>Second, the actual homeless of the city: unemployed and unemployable, mentally ill, chronically addicted or otherwise rejected by society, mulling around the space they already occupy every day. Put both groups together, quelle surprise, high minded principles about equality, privilege, diversity, ableism, free expression and shared ownership are tested. Youth whose idea of camping is a summer festival with sanitized portapotties and water stations found a public square in winter less accommodating. Furthermore, when the inevitable conflict and crime occurs, those who reach for the words <em>"triggered"</em> and <em>"rape culture"</em> like candy are probably not the best arbiters of actual justice.</p>

</div></div>

<div class="c"></div>

<div class="wide mt2">
  <a target="_blank" href="https://www.saybrook.edu/newexistentialists/posts/10-31-11"><img src="https://acko.net/files/occupy-www-street/occupy-empathy.jpg" alt="Scene from Occupy" /></a>
</div>

<div class="c"></div>

<div class="g10 i1 mt2 serif"><div class="pad">

<p>Instead of facing reality, they adopted the <a target="_blank" href="http://www.webcitation.org/69wFEoszj" title="http://www2.timesdispatch.com/news/rtd-opinion/2011/nov/04/tdopin02-hinkle-ows-protesters-have-strange-ideas--ar-1433590/">progressive stack</a>, which <em>"encourages women and traditionally marginalized groups [to] speak before men, especially white men."</em> If you dig into the grapevine, you will <a target="_blank" href="https://archive.is/N9as3" title="https://sli.mg/Vkzcmj">find grumblings</a> of <em>"meetings derailed by women and minorities obsessed with identity politics. […] [Plans] were almost always met with objection from feminists, queer activists, blacks, latinos, et al. A lot of the time these people didn't even have specific points of concern. […] They just stood up and started complaining about lack of diversity, the victimization of their specific identity group, etc. […] Smart, able, organized white guys left the protest. They threw up their hands and walked away."</em></p>

<p>You can find many stories of in-fighting and abuse, it provided ample fodder for the media, social or otherwise. What did or did not occur at specific events was rarely considered relevant, and such concerns became more important than the financial and economic system or the institutions that sustain it.  We apparently had to <a target="_blank" href="https://archive.is/irNvT#selection-823.91-823.139" title="http://www.dominionpaper.ca/articles/4268">confront the gender-based violence in the Occupy movement</a>: <em>"Exactly what occurred is unclear. Some claim there was an attempted rape. Others shrug off the incident as nothing more than an invasion of a young woman’s personal space by an intoxicated man."</em> Do you suppose that distinction might be important?</p>
  
<p>Socially-just media <a target="_blank" href="https://archive.is/SFZiU" titke="http://www.xojane.com/issues/occupy-wall-street-rape">pushed the hyperbole</a>, reminding men <em>"don't rape anyone, ever"</em>—thx xojane—and that <em>"women-only spaces are built to expedite advanced conversations about issues that affect women (and, ideally, anyone who does not identify as a man) without these discussions being constantly interrupted by the need to educate other folks in the gathering."</em></p>

<p>So in other words… Men, stay out, shut up. Women, don't ask any questions. Great job social justice, you brought everything you claim to abhor into your idealistic little utopia, chasing off everyone else.</p>

<p>It's easy to point at the whacky fringe characters with funny hair and glasses as the culprits. If you're Stephen Colbert, you can even <a target="_blank" href="https://archive.is/EymTY" title="http://newsbusters.org/blogs/pj-gladnick/2011/11/05/colbert-ows-interviewee-ketchup-reveals-self-wacko">mercilessly mock them in front of millions</a> and score lots of points. The real problem is the activist mass itself and the media they buy into. They're too eager to derail themselves over issues they themselves admit are just a microcosm of a human community. The mistake is in assigning universal importance to isolated incidents while ignoring the elephants in the room. That's a disease concentrated in the media, but not only there.</p>

<p>It's difficult to take someone's <em>Important Conversation about <a target="_blank" href="https://archive.is/XN8Vh" title="http://www.fudco.com/chip/deconstr.html">Deconstructing Issues</a></em> seriously, when social justice doesn't know how to socialize.</p>

</div></div>

<div class="c"></div>

<div class="wide mt1">
  <a target="_blank" href="https://archive.is/EymTY" title="http://newsbusters.org/blogs/pj-gladnick/2011/11/05/colbert-ows-interviewee-ketchup-reveals-self-wacko"><img src="https://acko.net/files/occupy-www-street/ketchup.jpg" alt="Burgers and Fries and Ketchup" /></a>
</div>

<div class="c"></div>

<div class="g10 i1 mt2 serif"><div class="pad">
  
<h2>/Chomsky/ was Right</h2>

<p>In <em>Language and Responsibility</em> (1979), Noam Chomsky instead converses eloquently. He mainly explains his theories of linguistics, too niche for most. However, the book's opening chapter "Politics" starts off quite differently, with ideas that would later return in <em>Manufacturing Consent: The Political Economy of the Mass Media</em> (1988). Asked how his methods relate to his more well known political writings:</p>

</div></div>

<div class="c m1"></div>

<aside class="g8"><div class="pad">
  <a target="_blank" href="http://www.vulgartrader.com/post/32392205367/hipster-chomsky"><img src="https://acko.net/files/occupy-www-street/chomsky.jpg" alt="Noam Chomsky" /></a>
</div></aside>

<div class="g4 serif"><div class="pad">

<blockquote class="flush">
<p><em>"I don't have access to any unusual methods of analysis and what special knowledge I have concerning language has no bearing on social and political issues. […] For the analysis of ideology […] a bit of open-mindedness, normal intelligence, and healthy skepticism will generally suffice."</em></p>
</blockquote>

</div></div>

<div class="g10 i1 mt1 serif"><div class="pad">

<blockquote>
<p><em>"Take [the] intelligentsia in a society like ours. This social class, which includes historians and other scholars, journalists, political commentators and so on, undertakes to analyze and present some picture of social reality. […] They serve as mediators between the social facts and the mass of the population: they create the ideological justification for social practice. Look at the work of the specialists in contemporary affairs and compare their interpretation with the events, compare what they say with the world of fact. You will often find great and fairly systematic divergences […]."</em></p>

<p><em>"With a little industry and application, anyone who is willing to extricate himself from the system […] will readily see through the modes of distortion […] Everybody is capable of doing that. […] Often carried out poorly, […] social and political analysis is produced to defend special interests rather than to account for actual events."</em> <em>(p3-4)</em></p> 
</blockquote>

<p>He goes on to talk about the particular situation in the United States. He compares recorded facts with the accepted explanations of events such as the McCarthyist scare, the student revolution, the civil rights movement, the Vietnam war, and more. He covers US interventions at home and abroad, legitimate or illegitimate. The overriding theme is rather obvious:</p>

<blockquote>
<p><em>"Here in the United States there is an astonishing degree of ideological uniformity for such a complex country. Basically, there are two reasons for this. First, there is the remarkable ideological homogeneity of the American intelligentsia in general, who rarely depart from one of the variants of state capitalistic ideology (liberal or conservative), a fact which itself calls for explanation. The second is that the mass media are capitalist institutions. […] The fact that these institutions reflect the ideology of dominant economic interests is hardly surprising."</em> <em>(p9)</em></p>
</blockquote>

<p>An editorial in the <em>New York Times</em> from 1975, a retrospective on the Vietnam War, explains there are those who think <em>"the war […] could have been waged differently"</em> and those who think <em>"a viable non-Communist South Vietnam was always a myth."</em> Chomksy goes all in:</p>

<blockquote>
  
<p><em>"They don't even mention the logical possibility of a third position: namely, that the United States did not have […] the legal or moral right, to intervene by force in the internal affairs of Vietnam. […] Note that as the <em>Times</em> sets the spectrum of ideological debate, the position of much of the peace movement is simply excluded from consideration. Not that it is wrong, but rather unthinkable, inexpressible. […]</em></p>

<p><em>Here we have a marvellous illustration of the functioning of propaganda in a democracy. A totalitarian state simply enunciates official doctrine—clearly, explicitly. […] one can only express opposition at one's peril. In a democratic system of propaganda no one is punished (in theory) for objecting to official dogma. In fact, dissidence is encouraged.</em></p>

<p><em>What this system attempts to do is to fix the limits of possible thought: supporters of official doctrine at one end, and the critics—vigorous, courageous, and much admired for their independence of judgement—at the other. The hawks and the doves. But we discover they all share certain tacit assumptions, and that it is these assumptions that are really crucial.</em></p>

<p><em>No doubt a propaganda system is more effective when its doctrines are insinuated rather than asserted, […] The more vigorous the debate, the more effectively the basic doctrines of the propaganda system, tacitly assumed on all sides, are instilled. Hence the elaborate pretense that the press is a critical dissenting force—maybe even too critical for the health of democracy—when in fact it is almost entirely subservient to the basic principles of the ideological system."</em> <em>(p36-39)</em></p>
</blockquote>

<p>This was the 70s. What might that look like in the internet age?</p>


<h2 class="mt3">Don't Stalk Me Bro</h2>

<p>In 2006, Gawker Media launched the <a target="_blank" href="https://archive.is/JM9mj" title="http://gawker.com/160338/introducing-gawker-stalker-maps">Gawker Stalker Maps</a>, a site where visitors were encouraged to share live updates of celebrity sightings in public. Hollywood stars found the usual gaggle of papparazzi joined by even weirder people from the internet, and were not amused.</p>

<p class="tc"><a target="_blank" href="https://www.youtube.com/watch?v=2-avakrRUaU" class="block"><img src="https://acko.net/files/occupy-www-street/gawker-stalker-kimmel.jpg" class="natural" /></a></p>

<p>When called to defend the practice a year later by Jimmy Kimmel, editor Emily Gould <a target="_blank" href="https://www.youtube.com/watch?v=2-avakrRUaU">goes for the gold in mental gymnastics</a>. Despite the original request for sightings <em>"as quickly as you can send them in,"</em> she claims the map is generally slow to update. She laughs away evidence of publishing lies as being <em>"citizen journalism"</em> which <em>"everybody knows […] isn't checked,"</em> despite much of the content being editorial. She trips and fails to catch herself, calling it <em>"a stalking…heh…it's not <em class="normal">actual</em> stalking",</em> because there is no expectation of not being noticed in public, and it's just acknowledging celebrities as real people. She then immediately asks if celebrities aren't <em>"protected by piles of money from any rocks thrown at them,"</em> gets called out and backpedals that <em>"it's not okay to say false things about anyone,"</em> invalidating everything she said before.</p>

<p>Kimmel throws a rather fun anachronistic zinger too: <em>"I don't know why anyone would buy advertising on a web site."</em></p>

<p>It's a rare occasion. Distant celebrities are humanized, old media takes new media to task, and the cheerful talking head is revealed for the blatant opportunist that she is. A chance to show the media does know better, right? Aaron Sorkin saw it too and featured this teachable moment in an episode of HBO's The Newsroom, <a target="_blank" href="https://www.youtube.com/watch?v=SWjREXKdXbM">lifting lines of dialog verbatim</a>:</p>

</div></div>

<div class="c"></div>

<div class="wide mt1">
  <a target="_blank" href="https://www.youtube.com/watch?v=SWjREXKdXbM"><img src="https://acko.net/files/occupy-www-street/the-newsroom.jpg" alt="The Newsroom" /></a>
</div>

<div class="c"></div>

<div class="g10 i1 mt1 serif"><div class="pad">

<p>Except of course, all the sharp edges have been filed off. Instead of a bubbly editor of an internet gossip rag, there's a pudgy Zuckerberg-type pushing his tech startup and app. His protestations seem more genuine, and he's taken aback by the sudden accusations, rather than invited to explicitly address them. The celebrities are not represented, they are off-screen, being stalked and murdered. He's an oblivious technocrat harming society with his self-serving autism, she's the approachable-yet-merciless reporter bringing him to task.</p>

<p>You see how a story that was entirely about the media was turned into something completely different, without even changing most of the words? It's all in the delivery and presentation. The excuse that it can't be entirely realistic or literal does not apply. I find the whole thing rather <em>convenient</em> in pushing certain narratives. It confuses the sociopaths who run certain businesses with the people who maintain the tech, and clear-coats it with some subtle nerd bashing.</p>

<p>It could be a pretty good example of what Chomsky was talking about: when presenting an interpretation of fact, American gatekeepers apply a lens that shines only on a very narrow and predictable window, while insisting otherwise. Like showing you <em>how news is actually made</em>, an <em>elaborate pretense that the press is a critical dissenting force</em>.</p>

</div></div>

<div class="c"></div>

<div class="wide mt1">
  <a target="_blank" href="https://www.youtube.com/watch?v=SWjREXKdXbM"><img src="https://acko.net/files/occupy-www-street/the-newsroom-2.jpg" alt="The Newsroom" /></a>
</div>

<div class="c"></div>

<div class="g10 i1 mt1 serif"><div class="pad">

<p>Or it could just be a mediocre episode of a faltering TV show. But then I'm sure there are countless opinion pieces and comments already that debate this in excruciating detail.</p>

<p>Either way, in trying to bring reality into their fantasy, they have remade it in their own image. They tend to do that a lot. Not just the ones who <a target="_blank" href="https://archive.is/t4EVf" title="http://newsdiffs.org/diff/934341/934454/www.nytimes.com/2015/07/11/technology/ellen-pao-reddit-chief-executive-resignation.html">write fiction</a>.</p>

<h2 class="mt3">Celebrate Diversity</h2>

<p>Where might we have seen all of this again recently? Let me lay it all out. What do you do when you're a notable reporter in entertainment, like say, video games, who finds that trade press is mostly obsolete in an age of YouTube, Twitch, Twitter, Reddit and more?</p>

<p>Well, you might have an existential crisis when you realize you were just being a <a target="_blank" href="https://archive.is/cjyTj">drunk megaphone</a> for corporate marketing interests, and <a target="_blank" href="http://deepfreeze.it/article.php?a=unfair#alexander">become a mouthpiece for your own interests</a> instead. Get on Twitter and post <em>"Fuck ethics get money,"</em> give talks in which you <em>"have no pretense of being unbiased."</em> Feature people and projects you like, promote political and social causes you think are important, and start picking the exclusives yourself instead of waiting for them to drop in your lap. All the while partaking in the global race to the bottom that is <a target="_blank" href="http://deepfreeze.it/article.php?a=quickdirty">modern clickbait</a>.</p>

<p>If enough people in a clique do this, it would create the appearance of an indie renaissance. However it would only feed attention to a very narrow scope of ideas, accessible only to those with specific connections who say the right things. If done in the context of a multi-billion dollar industry, this might create some resentment. Especially if there was <a target="_blank" href="http://www.tiki-toki.com/timeline/entry/343871/Corruption-consumer-hate-and-bad-journalism-in-games-journalism/">years of dubious reporting, conflicts of interest and agenda pushing</a> leading into it, with several careers permanently tarred and feathered through irresponsible rumour and lies. <a target="_blank" href="https://archive.is/U0pBQ" title="https://paxdickinson.wordpress.com/2014/10/22/moral-panics-and-the-death-of-fun/">Hypothetically</a> <a target="_blank" href="https://archive.is/68A2y" title="http://www.escapistmagazine.com/articles/view/video-games/gamergate-interviews/12397-Brad-Wardell-GamerGate-Interview.2">speaking</a>, of course.</p>

</div></div>

<div class="c"></div>

<div class="wide mt1">
  <a target="_blank" href="http://deepfreeze.it"><img src="https://acko.net/files/occupy-www-street/ramiel.jpg" alt="Ramiel" /></a>
</div>

<div class="c"></div>

<div class="g10 i1 mt1 serif"><div class="pad">

<p>In such an environment, imagine evidence pops up that one of the media clique's new darlings is actually a rather disturbing individual. While profiling herself as a paragon of virtue and a talented progressive activist and game developer, she manipulates and lies her way through her career and relationships. This comes in the form of a <a target="_blank" href="https://thezoepost.wordpress.com">warning and a detailed description of emotional abuse</a>, proven with, say, lengthy Facebook citations. The details are quite lurid and salacious, namely infidelity with a set of eponymous Five Guys in the industry, including her boss and a Kotaku reporter. On top of that, she is seen to wear the Emperor's New Clothes, in that her "work" mainly consists of a single choose-your-own-adventure Twine book that would barely pass as a first year student's midterm project. It was praised mainly for its subject—depression—rather than its execution.</p>

<p>Imagine that in response to this, other acquaintances come out of the woodwork to describe <a target="_blank" href="http://gamergate.wikia.com/wiki/Zoe_Quinn">other things she possibly did</a>. Like <a target="_blank" href="https://archive.is/zSifp" title="http://i.imgur.com/moFXvoa.jpg">framing a forum of depressed people as harassers</a> for attention, <a target="_blank" href="http://knowyourmeme.com/photos/813691-quinnspiracy">leaking personal data</a> on a rival charity, getting friends in media to <a target="_blank" href="https://www.reddit.com/r/KotakuInAction/comments/2gtqoq/the_story_of_the_fine_young_capitalists_and_the/">blacklist them</a>, sabotaging a <a target="_blank" href="http://knowyourmeme.com/photos/815901-quinnspiracy">reality TV game event</a> she was in, raising money for another event that <a target="_blank" href="https://archive.is/HYm63" title="http://apgnation.com/articles/2014/09/09/6977/truth-gaming-interview-fine-young-capitalists">never happened</a>, and generally being resented by past contacts.</p>

<p>More importantly, imagine that this scandal leads to <a target="_blank" href="https://archive.is/3z33G" title="https://www.reddit.com/r/Games/comments/2ejs7v/gaming_journalists_patricia_hernandez_of_kotaku/">other conflicts of interest</a> being revisited or discovered, <a target="_blank" href="https://archive.is/fEfRB" title="http://kotaku.com/a-different-way-to-respond-to-a-rape-accusation-update-1605542083">previous media lynchings</a> to be brought up and accountability to be demanded, because it involves <a target="_blank" href="https://www.youtube.com/watch?v=0QLz0CqtMVc#t=121">multiple influential outlets</a> and a lot of people are <a target="_blank" href="https://archive.is/DIpqE#selection-4917.0-4928.0" title="http://www.gamespot.com/articles/who-wins-when-mass-effect-3s-ending-changes/1100-6367380/">tired of it</a>.</p>

</div></div>

<aside class="g7"><div class="pad">
  <p><a class="block" target="_blank" href="https://archive.is/nWG2P" title="http://www.gamasutra.com/view/news/224400/Gamers_dont_have_to_be_your_audience_Gamers_are_over.php"><img src="https://acko.net/files/occupy-www-street/gamers-are-over.jpg" alt="Gamers are over" /></a></p>
</div></aside>

<div class="g5 serif"><div class="pad">
  
<p>Rather than admit it's all become a big joke and everyone's been played, reporters might choose to close ranks instead. They might concoct a tale that all the criticism she receives is harassment, that gamers are <a target="_blank" href="https://archive.is/Awcw9" title="http://www.gamasutra.com/view/news/224400/Gamers_dont_have_to_be_your_audience_Gamers_are_over.php">obtuse shitslingers and wailing hyperconsumers</a> who hate women, declaring the gamer identity to be dead in <a target="_blank" href="https://pixietalksgamergate.wordpress.com/gamers-are-dead-article-analysis/">ten articles published in 24 hours</a>. Though it would alienate their core readership, it would please the newer, more political segment, who would see all their existing biases about video games and gamers confirmed, being ignorant of the medium's established history of <a target="_blank" href="http://torment.wikia.com/wiki/Planescape:_Torment">complicated themes</a> being <a target="_blank" href="http://silenthill.wikia.com/wiki/Silent_Hill_2">explored maturely</a>.</p>

</div></div>

<div class="g12 mt0 serif"><div class="pad">
  
<p class="mt0 mb2"><a class="block" target="_blank" href="https://archive.is/Awcw9" title="http://www.gamasutra.com/view/news/224400/Gamers_dont_have_to_be_your_audience_Gamers_are_over.php"><img src="https://acko.net/files/occupy-www-street/gamers-are-over-2.png" alt="Gamers are over" /></a></p>

</div></div>

<div class="g10 i1 mt0 serif"><div class="pad">
  
<p class="mt0">New readers might see it as a license to step in, educate the plebs and defend the fair maidens from the supposed savage horde, in full sight of friends on social media, <a target="_blank" href="http://knowyourmeme.com/memes/nods-respectfully-towards-you">nodding respectfully</a>. They might rephrase events as a "jilted ex"—even though he dumped <em>her</em>—who has been rallying a hate mob on the evil depths of 4chan—even though he <a target="_blank" href="https://archive.is/h5LTs" title="https://medium.com/@srachel_m/gamergate-launched-in-my-apartment-and-internet-im-sorry-not-that-sorry-13e5650fd172">posted on Something Awful and Penny Arcade first</a> and reaction was mixed. They might speak of him leaking "revenge porn" when in actuality it's her own <a target="_blank" href="https://archive.is/rbmsg" title="http://www.skepticink.com/incredulous/2014/10/04/zoe-quinns-lying-cheating-claim-stabbing-killing-man-alleged-former-photographer/">paid erotica</a> circulating. All of these are either outright lies or an admission of not having bothered to read <a target="_blank" href="http://www.gamergatewiki.com/index.php/Timeline/Full">source material</a>.</p>

<p>It would be vital for reporters to never mention the coordinated censorship that might start to take place on Reddit, 4chan and other gaming forums, and the resulting Streisand effect that would only draw in more attention. This could include <a target="_blank" href="https://archive.is/rp54n" title="http://greggreenwell.tumblr.com/post/95136135894/zoe-quinn-is-using-false-dmca-claims-to-take-down">filing a false DMCA claim</a> on a prominent YouTuber, turning a <a target="_blank" href="https://archive.is/i9Du4" title="http://www.reddit.com/r/gaming/comments/2dz0gs/totalbiscuit_discusses_the_state_of_games">25,000 comment thread into a graveyard</a>, banning people for merely mentioning certain names, and declaring the entire topic off-limits on certain <em>allied</em> subreddits. As moderator favors would be traded in behind the scenes to accomplish this, it might create the appearance of <a target="_blank" href="https://archive.is/7VKAE" title="http://blogjob.com/oneangrygamer/2015/03/reddit-mods-admit-to-censoring-gamergate-hiding-corruption/">even more collusion</a> and even downright conspiracy, leading some to migrate away from 4chan in disgust to a previously minor image board, and later to Voat from Reddit.</p>

<p>Even if someone predicted exactly what would happen, a <a target="_blank" href="https://archive.is/AemDy#selection-7417.0-7421.310" title="https://archive.4plebs.org/pol/thread/34317761/">week in advance</a>, the games press would not, under any circumstance, face facts and admit error. Instead they would begin a long and drawn out media war against consumers who mainly want accountability, and in doing so, become the biggest magnet for trolling, drama, e-celebs and false flags the internet would see since its inception. Especially ones <a target="_blank" href="https://archive.is/wbbOI#selection-2943.0-2950.0" title="http://gamergate.wikia.com/wiki/Reddit">the old internet already knew about</a>.</p>

<p>Exactly like abuse victim Eron Gjoni warned, before being gagged by a contested and <a target="_blank" href="https://archive.is/pzSeP" title="https://www.reddit.com/r/KotakuInAction/comments/3hgq3b/zoe_quinn_moves_to_vacate_restraining_order_eron/">possibly unconstitutional legal order</a>, a whole segment of an industry might <a target="_blank" href="https://archive.is/RNzCI" title="http://gawker.com/how-we-got-rolled-by-the-dishonest-fascists-of-gamergat-1649496579">go insane</a> the subsequent months, over an abuser's self-serving machinations. Gaming and feminist media would have material aplenty with their new scapegoat. As the global news press is really just one big game of telephone, they would all report whatever these <em>experts</em> told them. They'd wield guilt by association like a club without ever bothering to investigate, obliviously committing a <a target="_blank" href="https://duckduckgo.com/?q=gamergate+harassment">big lie</a> so massive it would give Goebbels throbbing priapism. Like say, if one <a target="_blank" href="https://archive.is/Hy6AO" title="https://sli.mg/NUIXim">untraceable internet comment</a> nobody can substantiate was momentarily turned into the <a target="_blank" href="https://archive.is/vAJ69" title="https://duckduckgo.com/?q=felicia+day+doxxed">mushroom kingdom's death of Princess Diana</a>. If random IRC quotes were <a target="_blank" href="https://archive.is/MWvXC" title="http://www.theguardian.com/technology/2014/sep/12/zoe-quinn-gamergate-online-hate-mobs-depression-quest">taken as spy-thriller facts</a> about the opinions and motivations of thousands, without further investigation or right of reply. They might accept the word of a serial liar and self-admitted <a target="_blank" href="https://archive.is/Qb96g" title="https://twitter.com/TheQuinnspiracy/status/166659770248474624">helldump veteran</a>, starring as both Archduke Ferdinand and <a target="_blank" href="https://archive.is/h8rlT">Joan of Arc</a> in her very own <a target="_blank" href="http://knowyourmeme.com/memes/events/donglegate-adria-richards">DongleGate</a>.</p>

<p>All because of <em>vidya</em>, video games, all because a bunch of reporters found themselves <a target="_blank" href="https://archive.is/6yr0p" title="http://deepfreeze.it/journo.php">kings and queens</a> of a castle nobody really needed after all. They found an audience convinced Twitter and Tumblr was how you did activism, and that <a target="_blank" href="https://archive.is/iNbS5" title="http://www.thecrimson.com/column/words-words-words/article/2015/3/12/simplistic-social-justice-warrior/">campuses</a> and <a target="_blank" href="https://archive.is/c2RWs" title="http://www.examiner.com/article/calgary-expo-censors-the-honey-badger-brigade-s-anti-censorship-message">conventions</a> should be places where <a target="_blank" href="https://www.youtube.com/watch?v=iARHCxAMAO0">good faith inquiry goes to die</a>.</p>

<p>But that would be crazy to imagine.</p>

</div></div>

<div class="g12 mt0 serif"><div class="pad">

<p class="mt0 mb0"><a target="_blank" href="http://knowyourmeme.com/memes/events/shirtstorm"><img src="https://acko.net/files/occupy-www-street/matt-taylor-shirt.jpg" alt="Matt Taylor and his shirt" class="fit block"></a></p>

</div></div>

<div class="g10 i1 mt0 serif"><div class="pad">

<p>Even crazier would be to see that, unlike every single time it's happened before, this kind of entryism-by-way-of-social-justice met a wall of defiant opposition. That somehow the impossible happened, and people from <a target="_blank" href="https://www.reddit.com/r/KotakuInAction/">Reddit</a> and <a target="_blank" href="https://8ch.net/">4chan</a> could put aside their differences long enough to actually <a target="_blank" href="https://gitgud.io/gamergate/gamergateop/tree/master/Operations/Operation-Disrespectful-Nod">get some real work done</a>. That all the <a target="_blank" href="https://www.reddit.com/r/tumblrinaction">oppression olympics</a>, the <a target="_blank" href="http://knowyourmeme.com/memes/events/shirtstorm">media shaming</a>, <a target="_blank" href="https://duckduckgo.com/?q=tim+hunt">the misrepresentation</a>, the <a target="_blank" href="http://slatestarcodex.com/2014/07/07/social-justice-and-words-words-words/">wordplay</a>, the <a target="_blank" href="http://esr.ibiblio.org/?p=2122">fallacies</a> and <a target="_blank" href="https://www.youtube.com/watch?v=IAF2UmyXe-4">moral posturing</a> had finally pushed enough people over the edge to say: no, for once, <em>you</em> move. That they might try turning Twitter into the closest thing it will ever be to a rational discussion space, which is to say, not at all. That people there would be so addicted to digital validation, they would mistake their clumsy ideas being dismantled for harassment. They might even <a target="_blank" href="https://duckduckgo.com/?q=cyberviolence+at+the+UN">call it cyberviolence</a> at the UN, cherry picking a few idiots out of the mass for effect, and <a target="_blank" href="https://archive.is/ITMjR" title="https://github.com/freebsdgirl/ggautoblocker">autoblocking the rest</a> in a self-fulfilling prophecy.</p>

<p class="mt2 mb2"><iframe style="width: 100%" height="400" src="https://www.youtube.com/embed/8NhIBhbqWo4" frameborder="0" allowfullscreen="allowfullscreen"></iframe></p>

<p>You could discover, one year in, that trying to get a <a target="_blank" href="http://spjairplay.com">Society of Professional Journalists</a> to acknowledge rampant selection bias, copypasting, lazyness and pandering is far trickier than it was in 2011 with Fox News: this time there's <a target="_blank" href="https://archive.is/mj8wM" title="http://risemiaminews.com/2015/08/breaking-gamer-gate-controversy-prompts-evacuation-of-koubek-center-in-miami/">bomb</a> <a target="_blank" href="https://archive.is/NmVco" title="http://nichegamer.com/2015/05/peaceful-gamergate-in-d-c-meetup-receives-false-bomb-threats/">threats</a> and few seem to care much who sent them, or even know <a target="_blank" href="https://archive.is/fFzuD" title="http://gamepolitics.com/2015/08/12/challenge-accepted-interviewing-internet-hashtag/">how to go about reporting on this</a>. That Wikipedia's own infotocracy <a target="_blank" href="https://archive.is/jehPF">switches to</a> printing <a target="_blank" href="https://archive.is/rUVo3">outright propaganda</a> for <a target="_blank" href="https://archive.is/Nf6Wv">a while</a>, stuck in <a target="_blank" href="https://archive.is/P2UdO" title="http://i.imgur.com/RnQJ3Hl.png">citogenesis</a> of its <a target="_blank" href="https://archive.is/G4ppN" title="https://www.reddit.com/r/KotakuInAction/comments/2yfrfb/why_did_ryulong_get_banned_from_wikipedia/">own creation</a>.</p>

<p>Because it's far more <a target="_blank" href="https://archive.is/ouEJX" title="https://www.patreon.com/zoe">lucrative</a> to <a target="_blank" href="https://archive.is/14BFg" title="https://archive.is/14BFg">play dumb</a> after <a target="_blank" href="https://archive.is/X7DJB" title="https://images.encyclopediadramatica.se/7/71/Brianna_Wu_Meme_original.png">using autistic kids</a> for <a target="_blank" href="https://archive.is/UhZYI" title="http://www.cjr.org/the_kicker/gawker_bullying.php">laughs</a> from a position of <a target="_blank" href="https://archive.is/WhVcQ" title="https://pbs.twimg.com/media/B1Pg0IgIYAEKXX_.png:large">unearned privilege</a>.</p>

<h2 class="mt3">Brave New World</h2>

<p>So let's come full circle on this horseshoe of insanity.</p>

<p>What if it turned out that Occupy's much desired <em>"new way of interacting with others"</em> already existed since 2001, in the form of the Anonymous Image Board? Perhaps not coincidentally an invention from Japan, a culture that turned repression into an art form. A board where identity and status has no value by default and as a result, <a target="_blank" href="http://twentytwo.fibreculturejournal.org/fcj-158-tits-or-gtfo-the-logics-of-misogyny-on-4chans-random-b/">entirely different rules of engagement apply</a>, building on the practices of early cyberspace.</p>

<p>It does not lead to a utopian forum where intellectuals debate like they're on the set of Star Trek. Quite the opposite, this is humans you're dealing with. It leads to places that mix deep skepticism with casual verbal and graphic abuse, making everyone and everything equally worthless and fake until proven otherwise. Bringing identity as a general point of argument is not just unwelcome, it is shouted down with exactly those insults that deal it the most damage. Relevancy comes from resilience, taking offense is an instant defeat. Ingenuity, hyperbole and humor are the controlling measures to balance it out, acting like constant bullshit tests <a target="_blank" href="https://www.youtube.com/watch?v=DlSx7m09R60">against the uninspired</a> and <a target="_blank" href="https://www.youtube.com/watch?v=WcebgKvAoh0">intellectually bankrupt</a>. Ideas are forced to compete to remain relevant, just with one caveat: no topic is off limits. Especially taboos. It's not exactly for the faint of heart.</p>

<p>It's the closest thing the internet has to a functional universal "safe space", which is to say, not one at all, not by any offline definition. Once you get used to it though, the porn, the trolling, the shilling, the jew jokes, the "faggotry," it all becomes so banal and transparent as to not be worth getting worked up about. Which is exactly how it should be.</p>

<p><iframe style="width: 100%" height="410" src="https://www.youtube.com/embed/3hPFPjQ4pjo?cc_load_policy=1" frameborder="0" allowfullscreen="allowfullscreen"></iframe></p>

<p class="mt2 mb2">Look, while it's equally vulnerable to all the same pettyness and tribalism that made Twitter a success story, it's <a target="_blank" href="https://archive.is/vWYnS" title="https://sli.mg/BVK1RH">far more equipped</a> as both a medium and a culture to <a target="_blank" href="https://archive.is/kCa3S" title="https://sli.mg/x18eNq">deal with it</a> sustainably. That might include <a target="_blank" href="https://archive.is/Oxe8d" title="https://sli.mg/Tw3tn4">razing their own village to the ground</a> and <a target="_blank" href="https://archive.is/oViUh" title="https://sli.mg/5z0jV6">rebuilding it anew</a> in times of great distress. Though when it's all made out of trivially replaceable ones and zeroes that do it for free, is that really something other than an address change? It's not that strange: we used to be nomads, before the promise of a full belly and a permanent roof over their heads made people sacrifice a little bit of freedom for a whole lot of security.</p>

<p><iframe style="width: 100%" height="410" src="https://www.youtube.com/embed/Vx5BNDPA5hE" frameborder="0" allowfullscreen="allowfullscreen"></iframe></p>

</div></div>

<div class="g9 mt0 serif"><div class="pad">

<p>There are many who know all this, <a target="_blank" href="http://knowyourmeme.com/memes/events/project-chanology">none of this is new</a>, and a generation is growing up with this stuff being as normal as Facebook and Snapchat. Yet others look at the chans as some sort of oblivion gate leading to endless bigots, criminals and harassers. Like 9/11 Truth at Occupy, when you push certain ideas into the counter-culture, don't be surprised when speaking openly requires sharing space with shady neighbours, on top of a very long tail of fringe and genuine marginalization. Once again, you may hate this, but you can't ignore it.</p>

</div></div>

<aside class="g3"><div class="pad">
  <p class="mt0"><a class="block" target="_blank" href="http://knowyourmeme.com/memes/mark-bunkerwise-beard-man"><img src="https://acko.net/files/occupy-www-street/wise-beard-man-alt.jpg" alt="Mark Bunker, Wise Beard Man" /></a></p>
</div></aside>

<div class="g10 i1 mt0 serif"><div class="pad">

<p>For older readers: when you think Dungeons and Dragons turns teenagers into witches and warlocks, or that day-cares murder children in Satanic rituals, expect to be mocked as the ignorant panicky fool that you are. They might have learned to <a target="_blank" href="https://archive.is/AFT86" title="http://i.imgur.com/jQ2VBPA.png">build bridges</a> you had no idea about <a target="_blank" href="https://archive.is/IgTMn" title="http://www.torychristman.com/2008/02/01/youth-speaks-to-tory-anonymous-vs-scientologys-abuses/">long ago</a>, and they wouldn't need to declare themselves on the right side of history to do it.</p>

<p>Even more hilarious is when people say this about dear old Reddit, like say, in <a target="_blank" href="https://archive.is/dj7Jy" title="http://idibon.com/toxicity-in-reddit-communities-a-journey-to-the-darkest-depths-of-the-interwebs/">terrible internet studies</a>. They do their best to ignore their own conclusions and their own camp's troublemakers, with a random "but Women in Tech!" thrown in for good measure. They see what they want to see, not realizing that taking everything there at face value is not just noob mistake #1, it's often a mechanism that explicitly works to keep <em>unmotivated</em> outsiders out. The kind of trolls you can spot right away are the <a target="_blank" href="https://archive.is/P3Qqu" title="http://www.breitbart.com/london/2014/12/10/the-madness-of-queen-shanley/">amateurs</a>, and certain people are so useless they are a net drain on any online community they enter, often free from reproach.</p>

<p>The internet stops being uniquely valuable the moment you insist it should work exactly like real life. Our primary instincts and activities do not scale well beyond a single small tribe, that appears to be both <a target="_blank" href="https://archive.is/s0GhA" title="http://beautifultrouble.org/theory/dunbars-number/">empirical fact</a> and <a target="_blank" href="http://knowyourmeme.com/memes/greater-internet-fuckwad-theory">lived experience</a>. The default human impulse is to rely on shared identity for social validation. Insecurity leads people to avoid thinking critically, too afraid of going against the grain. They shun ideas rather than tackling them, succumbing to groupthink and echo chambers. The antidote is the same it's always been: good natured humor and reflection, including on oneself, not moral panic.</p>

<p>There's only one question you need to answer to complete the picture. Why are they so eager to believe that all of gaming—or all of society even—hates women and minorities, does not want them around and treats them as inferior, needing special outreach and support, even when there's <a target="_blank" href="http://knowyourmeme.com/memes/notyourshield">evidence and testimony</a> to the contrary? I'll give you a hint: social justice warriors are terrible at even just <em>considering</em> opinions that differ from their own. Yet they can't shut up about this one.</p>

<p>The road to hell is not just paved with good intentions, the Devil provides free popcorn at every station.</p>

<p><span class="spoiler" onclick="this.classList.toggle('active')">And when entering rabbit holes, always bring enough fuel to burn back to a <strong><a target="_blank" href="https://www.youtube.com/watch?v=F92l2s_bO-k">higher</a> <a target="_blank" href="https://www.youtube.com/watch?v=VQtnqU-mEDc">apoapsis</a></strong>.</span></p>

</div></div>

<div class="c m1"></div>

<div class="wide mt2">
  <a target="_blank" href="https://kerbalspaceprogram.com/en/"><img src="https://acko.net/files/occupy-www-street/kerbal.jpg" alt="Kerbal Space Program"></a>
</div>

<div class="g10 i1 mt1 serif"><div class="pad">

<p>The <a target="_blank" href="https://gitgud.io/gamergate/gamergateop/tree/master/Current-Happenings">media lied</a>, and I saw many whom I thought knew better fall for it. It was an absolute mindfuck to see educated brains fail this hard and don their favorite brown shirt in the name of progress. It's like they never grew past their high school phase, still trying to become one of the cool kids while resenting them for it. Never realizing that moral posturing is a trick that only works on the weak willed, and it only works so long. <a target="_blank" href="https://www.youtube.com/watch?v=LHPqUddXCu8">Anita pls stop</a>.</p>
  
<p><a target="_blank" href="https://archive.is/sxkgY" title="https://pbs.twimg.com/media/CTJXaFIWIAEEP72.jpg:large">Listen</a> and <a target="_blank" href="https://archive.is/Cd0rz" title="https://pbs.twimg.com/media/CTJXY8cWsAA-zQC.jpg:large">Believe</a>, take your with-us-or-against-us mindset and go think about what you've done. The internet is under total surveillance, global trade agreements threaten to elevate corporations to the level of states, and the willingness to censor the net is at an all time high. Only this time, it's not just the threat of terrorism, pedophiles and child porn used to scare the masses and silence critics. It's <em>"cyberviolence against women"</em> and fringe <em>"white supremacy."</em> You know what <em>"enables a broken system"</em>? Letting a popular female abuser steamroll over her gaslighted male victim and then shamelessly <a target="_blank" href="https://archive.is/AGZie" title="http://shrink4men.com/2011/01/19/presto-change-o-darvo-deny-attack-and-reverse-victim-and-offender/">swapping all the labels</a> while applying duct tape.</p>

<p>Look around. One of the most prominent press freedom activists lives in house arrest out of an embassy, accused of the internationally villainous crime of having had <a target="_blank" href="https://archive.is/a8br" title="http://georgewashington2.blogspot.de/2010/12/sex-charges-and-arrest-warrant-against.html">consensual sex but without a condom</a>, i.e. <em>"rape."</em> Sexism and racism have been wielded as perverted weapons of reverse victimhood by those with nothing substantial to offer and everything to gain. The newspaper that broke the Snowden leaks has willingly <a target="_blank" href="https://archive.is/D74Gl" title="http://www.theguardian.com/uk-news/tim-hunt">reduced itself to Daily Mail level</a>, shaming scientists instead of focusing on their accomplishments. It's not a conspiracy when the clickbait keeps flowing and people willingly line up to partake, forking over their dough for post-catholic indulgences against patriarchal original sin and white oppressor guilt.</p>

<p>PS: Calling people angry is not a valid retort (we call that "projection"), neither is calling them bigots, harassers or internalized misogynists/homophobes/uncle toms because of things they didn't do. Social justice wanted everyone to educate themselves, so we gave them exactly what they asked for—warts and all—and they and the media continue to misrepresent it. There was no grand deception on the gamers' part, no <a target="_blank" href="https://archive.is/KeUR9">reefer madness hate mob</a> that chases women out of gaming for being women. Only talentless hacks, plenty of characters with unclear intentions, a lot of third party trolls, and many more people trying to work around them while being called names. I'm not <a target="_blank" href="https://www.youtube.com/watch?v=rGIY5Vyj4YM">mad as hell</a>, I simply do not wish to take it anymore.</p>

<p>(And they will probably call me right wing, never stopping to think that national socialism created the Volkswagen for a reason. Yes I know, Godwin. <a target="_blank" href="https://archive.is/du2rV" title="https://sli.mg/HdI5Uf">Wir haben es nicht gewußt</a>.)</p>

<p><iframe style="width: 100%" height="380" src="https://www.youtube.com/embed/L61ZCN8rrWw" frameborder="0" allowfullscreen="allowfullscreen"></iframe></p>

<div class="c"></div>

<p class="tc mt4 mb0"><img src="https://acko.net/files/occupy-www-street/darmok-and-jalad.jpg" alt="Darmok and Jalad at Tanagra" class="natural spoiler" onclick="this.classList.toggle('active')" /></p>

<p class="tc mt0"><a target="_blank" href="http://knowyourmeme.com/memes/people/ben-garrison"><img src="https://acko.net/files/occupy-www-street/ben-garrison.png" class="flat natural" width="100" height="59" title="No, I'm not Zyklon Ben." /></a></p>

<p class="tc mt3 mb0"><strong>The tree of liberty must be refreshed from time to time<br>with the blood of patriots and tyrants,</strong></p>

<p class="tc mb5 mt0"><span class="spoiler block" onclick="this.classList.toggle('active')">but they tried to use ketchup.</span></p>

<p class="mt3 mb2"><iframe style="width: 60%; margin: 0 auto;" height="240" src="https://www.youtube.com/embed/EKc-fqsIxGk" frameborder="0" allowfullscreen="allowfullscreen"></iframe></p>

<p class="mt2 mb2"><iframe style="width: 60%; margin: 0 auto;" height="240" src="https://www.youtube.com/embed/5MRUgLf72AM" frameborder="0" allowfullscreen="allowfullscreen"></iframe></p>

<p class="mt2 mb5"><iframe style="width: 60%; margin: 0 auto;" height="240" src="https://www.youtube.com/embed/atazfJqCutQ" frameborder="0" allowfullscreen="allowfullscreen"></iframe></p>

<p><em>Note: some people have pointed out the statements above could be seen as quite ominous. I'd just like to clarify, it's meant to be funny and somewhat absurd in a if-we-can't-cry-at-least-we-can-laugh way, not to be taken literally as a call for conflict.</em></p>

</div></div>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Yak Shading]]></title>
    <link href="http://acko.net/blog/yak-shading/"/>
    <updated>2015-09-27T00:00:00+02:00</updated>
    <id>http://acko.net/blog/yak-shading</id>
    <content type="html"><![CDATA[<script src="/files/katex/katex.min.js"></script>
<script src="/files/katex/contrib/auto-render.min.js"></script>
<link rel="stylesheet" type="text/css" href="/files/katex/katex.min.css" />

<div class="g8 i2 first"><div class="pad">

<h2 class="sub">Data-Driven Geometry</h2>

<p>MathBox primitives need to take arbitrary data, transform it on the fly, and render it as styled geometry based on their attributes. Done as much as possible on the graphics hardware.</p>

<p>Three.js can render points, lines, triangles, but only with a few predetermined strategies. The alternative is to write your own vertex and fragment shader and do everything from scratch. Each new use case means a new <code>ShaderMaterial</code> with its own properties, so called <em>uniforms</em>. If the stock geometry doesn't suffice, you can make your own triangles by filling a raw <code>BufferGeometry</code> and assign custom per-vertex attributes. Essentially, to leverage GPU computation with Three.js—most engines, really—you have to ignore most of it.</p>


<h2>Virtual Geometry</h2>

<p>Shader computations are mainly rote transforms. For example, if you want to draw a line between two points, you'll have to make a long rectangle, made out of two triangles. But this simple idea gets complicated quickly once you add corner joins, depth scaling, 3D clipping, and so on. Doing this to an entire data set at once is what GPUs are made for, through vertex shaders which transform points.</p>

</div></div>

<div class="wide full">

  <div class="iframe c">
    <iframe src="/files/mathbox2/iframe-line-shader.html?22e389c1" class="mathbox janky" height="150" style="width: 100%;"></iframe>
  </div>

</div>

<div class="c"></div>

<div class="wide m1">
   <img src="/files/mathbox2/yakshading2.png" class="flat" alt="Vertex Shader" style="position: relative; left: -5px;">
</div>

<div class="g8 i2 m1"><div class="pad">
<p>Vertex shaders can only do 1-to-1 mappings. This isn't a problem by itself. You can use a <em>gather</em> approach to do N-to-1 mapping, where all the necessary data is pre-arranged into attribute arrays, with the data repeated and interleaved per vertex as necessary.</p>
</div></div>

<div class="c"></div>

<div class="wide m2">
   <img src="/files/mathbox2/yakshading3.png" class="flat" alt="Vertex Shader Attributes" style="position: relative; left: -5px;">
</div>

<div class="g8 i2 m1"><div class="pad">
<p>The proper tool for this is a geometry shader: a program that creates new geometry by N-to-M mapping of data, like making triangles out of points. WebGL doesn't support geometry shaders, won't any time soon, but you can emulate them with texture samplers. A texture image is just a big typed array, and you have random access unlike vertex attributes.</p>

</div></div>

<div class="c"></div>

<div class="wide m2">
   <img src="/files/mathbox2/yakshading.png" class="flat" alt="Yak Shading" style="position: relative; left: -5px;">
</div>

<div class="g8 i2 m1"><div class="pad">

<p>The original geometry acts only as a template, directing the shader's real data lookups. You lose some performance this way, but it's not too bad. Any procedural sampling pattern works, drawing 1 shape or 10,000. As textures can be rendered <em>to</em>, not just from, this also enables transform feedback, using the result of one pass to create new geometry in another.</p>

<p>All geometry rendered this way is 100% static as far as Three.js is concerned. New values are uploaded directly to GPU memory just before the rendering starts. The only gotcha is handling variable size input, because reallocation is costly. Pre-allocating a larger texture is easy, but clipping off the excess geometry in an O(1) fashion on the JS side is hard. In most cases there's the work around of dynamically generating <em>degenerate</em> triangles in a shader, which collapse down to invisible edges or points. This way, MathBox can accept variable sized arrays in multiple dimensions and will do its best to minimize disruption. If attribute instancing was more standard in WebGL, this wouldn't be such an issue, but as it stands, the workarounds are very necessary.</p>

</div></div>

<div class="wide full">

  <div class="iframe c">
    <iframe src="/files/mathbox2/iframe-cylindrical-stream.html?22e389c1" class="mathbox autosize janky" height="320"></iframe>
  </div>

</div>

<div class="g8 i2"><div class="pad">

<h2>Vertex Party</h2>

<p>If you squint <em>very</em> hard it looks a bit like React for live geometry. Except instead of a diffing algorithm, there's a few events, some texture uploads, a handful of draw calls and then an idle CPU. It's ideal for drawing thousands of things that look similar and follow the same rules. It can handle not just basic GL primitives like lines or triangles, but higher level shapes like 3D arrows or sprites.</p>

<p>My first prototype of this was my last <a href="http://christmasexperiments.com/2013/22/" target="_blank">christmas demo</a>. It was messy and tedious to make, especially the shaders, but it performed excellently: the final scene renders ~200,000 triangles. Despite being a layer around Three.js … around WebGL … around OpenGL … around a driver … around a GPU … performance has far exceeded my expectations. Even complex scenes run great on my Android phone, easily 10x faster than MathBox 1, in some cases more like 1000x.</p>

<p>Of course compared to cutting edge DirectX or <a href="http://en.wikipedia.org/wiki/OpenCL">OpenCL</a> (not a typo), this is still very limited. In today's GPUs, the charade of attributes, geometries, vertices and samples has mostly been stripped away. What remains is buffers and massive operations on them, exposed raw in new APIs like AMD's Mantle and iOS's Metal. My vertex trickery acts like a polyfill, virtualizing WebGL's capabilities to bring them closer to the present. It goes a bit beyond what geometry shaders can provide, but still lacks many useful things like atomic append queues or stream compaction.</p>

<p>For large geometries, the set up cost can be noticeable though. Shader compilation time also grows with transform complexity, doubly so on Windows where shaders are recompiled to HLSL / Direct3D. This makes drawing ops the heaviest MathBox primitives to spawn and reallocate. You could call this the MathBox version of the dreaded 'paint' of HTML. Once warmed up though, most other properties can be animated instantly, including the data being displayed: this is the opposite of how HTML works. Hence you can mostly spawn things ahead of time, revealing and hiding objects as needed, with minimal overhead and jank at runtime.</p>

<p>This all relies on carefully constructed shaders which have to be wired up in all their individual permutations. This needed to be solved programmatically, which is where we go last.</p>

<ul>
  <li><a href="/blog/mathbox2/">MathBox² - PowerPoint Must Die</a></li>
  <li><a href="/blog/a-dom-for-robots/">A DOM for Robots - Modelling Live Data</a></li>
  <li><em>Yak Shading - Data Driven Geometry</em></li>
  <li><a href="/blog/shadergraph-2/">ShaderGraph 2 - Functional GLSL</a></li>
</ul>

</div></div>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Shader&shy;Graph 2]]></title>
    <link href="http://acko.net/blog/shadergraph-2/"/>
    <updated>2015-09-27T00:00:00+02:00</updated>
    <id>http://acko.net/blog/shadergraph-2</id>
    <content type="html"><![CDATA[<script src="/files/katex/katex.min.js"></script>
<script src="/files/katex/contrib/auto-render.min.js"></script>
<link rel="stylesheet" type="text/css" href="/files/katex/katex.min.css" />

<div class="g8 i2 first"><div class="pad">

<h2 class="sub">Functional GLSL</h2>

<p>For MathBox 1, I already needed to generate GL shaders programmatically. So I built ShaderGraph. You gave it snippets of GLSL code, each with a function inside. It would connect them for you, matching up the inputs and outputs. It supported directed graphs of calls with splits and joins, which were compiled down into a single shader. To help build up the graph progressively, it came with a simple chainable factory API.</p>

<p>It worked despite being several steps short of being a real compiler and having gaps in its functionality. It also committed the cardinal sin of regex code parsing, and hence accepted only a small subset of GLSL. All in all it was a bit of a happy mess, weaving vertex and fragment shaders together in a very ad-hoc fashion. Each snippet could only appear once in a shader, as it was still just a dumb code concatenator. I needed a proper way to compose shaders.</p>

</div></div>

<div class="c"></div>

<div class="wide full">

  <div class="iframe c">
    <iframe src="/files/mathbox2/iframe-shadergraph-1.html?22e389c1" class="janky" height="190" style="width: 100%;"></iframe>
  </div>
  
  <p class="tc"><em><small>Select a node to view its code</small></em></p>

</div>

<div class="c"></div>

<div class="g8 i2"><div class="pad">

<h2 class="mt2">Instanced Data Flow</h2>

<p>Enter <a href="https://github.com/unconed/shadergraph" target="_blank">ShaderGraph 2</a>. It's a total rewrite using Chris Dickinson's bona fide <code>glsl-parser</code>. It still parses snippets and connects them into a directed graph to be compiled. But a snippet is now a full GLSL program whose <code>main()</code> function can have open inputs and outputs. What's more, it now also <em>links</em> code in the proper sense of the word: linking up module entry points as callbacks.</p>

<p>Basically, snippets can now have inputs and outputs that are themselves functions. These connections don't obey the typical data flow of a directed graph and instead are for function calls. A callback connection provides a path along which calls are made and values are returned.</p>

</div></div>

<div class="c"></div>

<div class="wide full mt1">

  <div class="iframe c">
    <iframe src="/files/mathbox2/iframe-shadergraph-2.html?22e389c1" class="janky" height="300" style="width: 100%;"></iframe>
  </div>

</div>

<div class="c"></div>

<div class="g8 i2"><div class="pad">

<p class="mt0">Snippets can be instanced multiple times, including their uniforms, attributes and varyings (if requested). Uniforms are bound to Three.js-style registers as you build the graph incrementally. So it's a module system, sort of, which enables functional shader building. Using callbacks as micro-interfaces feels very natural in practice, especially with bound parameters. You can decorate existing functions, e.g. turning a texture sampler into a convolution filter.</p>

</div></div>

<div class="c"></div>

<div class="wide full">

  <div class="iframe c">
    <iframe src="/files/mathbox2/iframe-shadergraph-7.html?22e389c1" class="janky" height="300" style="width: 100%;"></iframe>
  </div>

</div>

<div class="c"></div>

<div class="g8 i2 mt0"><div class="pad">

<pre><code class="language-javascript">// Build shader graph
var shader = shadergraph.shader();
shader
  .callback()
    .pipe('sampleColor')
    .fan()
      .pipe('sepiaColor')
    .next()
      .pipe('invertColor')
    .join()
    .pipe('combineColors')
  .join()
  .pipe('convolveColor');</code></pre>

<div class="c"></div>

<h2>GLSL Composer</h2>

<p>If you know GLSL, you can write ShaderGraph snippets: there is no extra syntax, you just add inputs and outputs to your <code>main()</code> function. You can use <code>in/out/inout</code> qualifiers or return a value. If there's no main function, the last defined function is exported.</p>

<pre><code class="language-glsl">vec3 callback(vec3 arg1, vec3 arg2);</code></pre>

<div class="c"></div>

<p>To create a <em>callback input</em> in a snippet, you declare a function prototype in GLSL without a body. The function name and signature is used to create the outlet.</p>

<p>To create a <em>callback output</em>, you use the factory API. You can <code>.require()</code> a snippet directly, or bundle up a subgraph with <code>.callback().….join()</code>. In the latter case, the function signature includes all unconnected inputs and outputs inside. Outlets are auto-matched by name, type and order, with the semantics from v1 cleaned up.</p>

<p>Building basic pipes is easy: <code>.pipe(…).pipe(…).…</code>, passing in a snippet or factory. For forked graphs, you can <code>.fan()</code> (1-to-N) or <code>.split()</code> (N-to-N), use <code>.next()</code> to begin a new branch, and then <code>.join()</code> at the end. There's a few other operations, nothing crazy.</p>

<pre><code class="language-javascript">var v = shadergraph.shader();

// Graphs generated elsewhere
v.pipe(vertexColor(color, mask));
v.require(vertexPosition(position, material, map, 2, stpq));

v.pipe('line.position',    uniforms, defs);
v.pipe('project.position', uniforms);
</code></pre>  

<div class="c"></div>

<p>By connecting pairs you create a functional data flow that compiles down to vanilla GLSL. It's not functional programming <em>in</em> GLSL, it just enables useful run-time assembly patterns, letting the snippets do the heavy lifting the old fashioned way.</p>

</div></div>

<div class="c"></div>

<div class="wide full mt2">

  <div class="iframe c">
    <iframe src="/files/mathbox2/iframe-shadergraph-5.html?22e389c1" class="mathbox janky" height="440" style="width: 100%;"></iframe>
  </div>

  <div class="iframe c">
    <iframe src="/files/mathbox2/iframe-shadergraph-4.html?22e389c1" class="janky" height="440" style="width: 100%;"></iframe>
  </div>

</div>

<div class="c"></div>

<div class="g8 i2"><div class="pad">

<p>As GPUs are massively parallel pure function applicators, the resulting mega-shaders are a great fit.</p>

</div></div>

<div class="wide full">

  <div class="iframe c">
    <iframe src="/files/mathbox2/iframe-shadergraph-3.html?22e389c1" class="mathbox janky" height="725" style="width: 100%;"></iframe>
  </div>

</div>

<div class="c"></div>

<div class="g8 i2"><div class="pad">

<h2><code>$ cat *.glsl | magic</code></h2>

<p>The process still comes down to concatenating the code in a clever way, with global symbols namespaced to be unique. Function bodies are generated to call snippets in the right order, and the callbacks are linked. In the trivial case it links a callback by <code>#define</code>ing the two symbols to be the same. It can also impedance match compatible signatures like <code>void main(in float, out vec2)</code> and <code>vec2 main(float)</code> by inserting an intermediate call.</p>

</div></div>

<div class="g10 i1"><div class="pad">
  
  <pre><code class="language-glsl small" style="max-height: 480px; overflow-y: auto;">precision highp float;
precision highp int;
uniform mat4 modelMatrix;
uniform mat4 modelViewMatrix;
uniform mat4 projectionMatrix;
uniform mat4 viewMatrix;
uniform mat3 normalMatrix;
uniform vec3 cameraPosition;
#define _sn_191_getPosition _pg_103_
#define _sn_190_getPosition _pg_102_
#define _sn_189_getSample _pg_100_
#define _pg_99_ _sn_185_warpVertex
#define _pg_103_ _sn_190_getMeshPosition
#define _pg_100_ _sn_188_getTransitionSDFMask
#define _pg_101_ _sn_189_maskLevel
vec2 _sn_180_truncateVec(vec4 v) { return v.xy; }
uniform vec2 _sn_181_dataResolution;
uniform vec2 _sn_181_dataPointer;

vec2 _sn_181_map2DData(vec2 xy) {
  return fract((xy + _sn_181_dataPointer) * _sn_181_dataResolution);
}

uniform sampler2D _sn_182_dataTexture;

vec4 _sn_182_sample2D(vec2 uv) {
  return texture2D(_sn_182_dataTexture, uv);
}

vec4 _sn_183_swizzle(vec4 xyzw) {
  return vec4(xyzw.x, xyzw.w, 0.0, 0.0);
}
uniform float _sn_184_polarBend;
uniform float _sn_184_polarFocus;
uniform float _sn_184_polarAspect;
uniform float _sn_184_polarHelix;

uniform mat4 _sn_184_viewMatrix;

vec4 _sn_184_getPolarPosition(vec4 position, inout vec4 stpq) {
  if (_sn_184_polarBend &gt; 0.0) {

    if (_sn_184_polarBend &lt; 0.001) {
      
      
      
      
      vec2 pb = position.xy * _sn_184_polarBend;
      float ppbbx = pb.x * pb.x;
      return _sn_184_viewMatrix * vec4(
        position.x * (1.0 - _sn_184_polarBend + (pb.y * _sn_184_polarAspect)),
        position.y * (1.0 - .5 * ppbbx) - (.5 * ppbbx) * _sn_184_polarFocus / _sn_184_polarAspect,
        position.z + position.x * _sn_184_polarHelix * _sn_184_polarBend,
        1.0
      );
    }
    else {
      vec2 xy = position.xy * vec2(_sn_184_polarBend, _sn_184_polarAspect);
      float radius = _sn_184_polarFocus + xy.y;
      return _sn_184_viewMatrix * vec4(
        sin(xy.x) * radius,
        (cos(xy.x) * radius - _sn_184_polarFocus) / _sn_184_polarAspect,
        position.z + position.x * _sn_184_polarHelix * _sn_184_polarBend,
        1.0
      );
    }
  }
  else {
    return _sn_184_viewMatrix * vec4(position.xyz, 1.0);
  }
}
uniform float _sn_185_time;
uniform float _sn_185_intensity;

vec4 _sn_185_warpVertex(vec4 xyzw, inout vec4 stpq) {
  xyzw +=   0.2 * _sn_185_intensity * (sin(xyzw.yzwx * 1.91 + _sn_185_time + sin(xyzw.wxyz * 1.74 + _sn_185_time)));
  xyzw +=   0.1 * _sn_185_intensity * (sin(xyzw.yzwx * 4.03 + _sn_185_time + sin(xyzw.wxyz * 2.74 + _sn_185_time)));
  xyzw +=  0.05 * _sn_185_intensity * (sin(xyzw.yzwx * 8.39 + _sn_185_time + sin(xyzw.wxyz * 4.18 + _sn_185_time)));
  xyzw += 0.025 * _sn_185_intensity * (sin(xyzw.yzwx * 15.1 + _sn_185_time + sin(xyzw.wxyz * 9.18 + _sn_185_time)));

  return xyzw;
}



vec4 _sn_186_getViewPosition(vec4 position, inout vec4 stpq) {
  return (viewMatrix * vec4(position.xyz, 1.0));
}

vec3 _sn_187_getRootPosition(vec4 position, in vec4 stpq) {
  return position.xyz;
}
vec3 _pg_102_(vec4 _io_510_v, in vec4 _io_519_stpq) {
  vec2 _io_509_return;
  vec2 _io_511_return;
  vec4 _io_513_return;
  vec4 _io_515_return;
  vec4 _io_517_return;
  vec4 _io_520_stpq;
  vec4 _io_527_return;
  vec4 _io_528_stpq;
  vec4 _io_529_return;
  vec4 _io_532_stpq;
  vec3 _io_533_return;

  _io_509_return = _sn_180_truncateVec(_io_510_v);
  _io_511_return = _sn_181_map2DData(_io_509_return);
  _io_513_return = _sn_182_sample2D(_io_511_return);
  _io_515_return = _sn_183_swizzle(_io_513_return);
  _io_520_stpq = _io_519_stpq;
  _io_517_return = _sn_184_getPolarPosition(_io_515_return, _io_520_stpq);
  _io_528_stpq = _io_520_stpq;
  _io_527_return = _pg_99_(_io_517_return, _io_528_stpq);
  _io_532_stpq = _io_528_stpq;
  _io_529_return = _sn_186_getViewPosition(_io_527_return, _io_532_stpq);
  _io_533_return = _sn_187_getRootPosition(_io_529_return, _io_532_stpq);
  return _io_533_return;
}
uniform vec4 _sn_190_geometryResolution;

#ifdef POSITION_STPQ
varying vec4 vSTPQ;
#endif
#ifdef POSITION_U
varying float vU;
#endif
#ifdef POSITION_UV
varying vec2 vUV;
#endif
#ifdef POSITION_UVW
varying vec3 vUVW;
#endif
#ifdef POSITION_UVWO
varying vec4 vUVWO;
#endif


vec3 _sn_190_getMeshPosition(vec4 xyzw, float canonical) {
  vec4 stpq = xyzw * _sn_190_geometryResolution;
  vec3 xyz = _sn_190_getPosition(xyzw, stpq);

  #ifdef POSITION_MAP
  if (canonical > 0.5) {
    #ifdef POSITION_STPQ
    vSTPQ = stpq;
    #endif
    #ifdef POSITION_U
    vU = stpq.x;
    #endif
    #ifdef POSITION_UV
    vUV = stpq.xy;
    #endif
    #ifdef POSITION_UVW
    vUVW = stpq.xyz;
    #endif
    #ifdef POSITION_UVWO
    vUVWO = stpq;
    #endif
  }
  #endif
  return xyz;
}

uniform float _sn_188_transitionEnter;
uniform float _sn_188_transitionExit;
uniform vec4  _sn_188_transitionScale;
uniform vec4  _sn_188_transitionBias;
uniform float _sn_188_transitionSkew;
uniform float _sn_188_transitionActive;

float _sn_188_getTransitionSDFMask(vec4 stpq) {
  if (_sn_188_transitionActive &lt; 0.5) return 1.0;

  float enter   = _sn_188_transitionEnter;
  float exit    = _sn_188_transitionExit;
  float skew    = _sn_188_transitionSkew;
  vec4  scale   = _sn_188_transitionScale;
  vec4  bias    = _sn_188_transitionBias;

  float factor  = 1.0 + skew;
  float offset  = dot(vec4(1.0), stpq * scale + bias);

  vec2 d = vec2(enter, exit) * factor + vec2(-offset, offset - skew);
  if (exit  == 1.0) return d.x;
  if (enter == 1.0) return d.y;
  return min(d.x, d.y);
}
uniform float _sn_191_worldUnit;
uniform float _sn_191_lineWidth;
uniform float _sn_191_lineDepth;
uniform float _sn_191_focusDepth;

uniform vec4 _sn_191_geometryClip;
attribute vec2 line;
attribute vec4 position4;

#ifdef LINE_PROXIMITY
uniform float _sn_191_lineProximity;
varying float vClipProximity;
#endif

#ifdef LINE_STROKE
varying float vClipStrokeWidth;
varying float vClipStrokeIndex;
varying vec3  vClipStrokeEven;
varying vec3  vClipStrokeOdd;
varying vec3  vClipStrokePosition;
#endif


#ifdef LINE_CLIP
uniform float _sn_191_clipRange;
uniform vec2  _sn_191_clipStyle;
uniform float _sn_191_clipSpace;

attribute vec2 strip;

varying vec2 vClipEnds;

void _sn_191_clipEnds(vec4 xyzw, vec3 center, vec3 pos) {

  
  vec4 xyzwE = vec4(strip.y, xyzw.yzw);
  vec3 end   = _sn_191_getPosition(xyzwE, 0.0);

  
  vec4 xyzwS = vec4(strip.x, xyzw.yzw);
  vec3 start = _sn_191_getPosition(xyzwS, 0.0);

  
  vec3 diff = end - start;
  float l = length(diff) * _sn_191_clipSpace;

  
  float arrowSize = 1.25 * _sn_191_clipRange * _sn_191_lineWidth * _sn_191_worldUnit;

  vClipEnds = vec2(1.0);

  if (_sn_191_clipStyle.y > 0.0) {
    
    float depth = _sn_191_focusDepth;
    if (_sn_191_lineDepth &lt; 1.0) {
      float z = max(0.00001, -end.z);
      depth = mix(z, _sn_191_focusDepth, _sn_191_lineDepth);
    }
    
    
    float size = arrowSize * depth;

    
    
    float mini = clamp(1.0 - l / size * .333, 0.0, 1.0);
    float scale = 1.0 - mini * mini * mini; 
    float invrange = 1.0 / (size * scale);
  
    
    diff = normalize(end - center);
    float d = dot(end - pos, diff);
    vClipEnds.x = d * invrange - 1.0;
  }

  if (_sn_191_clipStyle.x > 0.0) {
    
    float depth = _sn_191_focusDepth;
    if (_sn_191_lineDepth &lt; 1.0) {
      float z = max(0.00001, -start.z);
      depth = mix(z, _sn_191_focusDepth, _sn_191_lineDepth);
    }
    
    
    float size = arrowSize * depth;

    
    
    float mini = clamp(1.0 - l / size * .333, 0.0, 1.0);
    float scale = 1.0 - mini * mini * mini; 
    float invrange = 1.0 / (size * scale);
  
    
    diff = normalize(center - start);
    float d = dot(pos - start, diff);
    vClipEnds.y = d * invrange - 1.0;
  }


}
#endif

const float _sn_191_epsilon = 1e-5;
void _sn_191_fixCenter(vec3 left, inout vec3 center, vec3 right) {
  if (center.z >= 0.0) {
    if (left.z &lt; 0.0) {
      float d = (center.z - _sn_191_epsilon) / (center.z - left.z);
      center = mix(center, left, d);
    }
    else if (right.z &lt; 0.0) {
      float d = (center.z - _sn_191_epsilon) / (center.z - right.z);
      center = mix(center, right, d);
    }
  }
}


void _sn_191_getLineGeometry(vec4 xyzw, float edge, out vec3 left, out vec3 center, out vec3 right) {
  vec4 delta = vec4(1.0, 0.0, 0.0, 0.0);

  center =                 _sn_191_getPosition(xyzw, 1.0);
  left   = (edge > -0.5) ? _sn_191_getPosition(xyzw - delta, 0.0) : center;
  right  = (edge &lt; 0.5)  ? _sn_191_getPosition(xyzw + delta, 0.0) : center;
}

vec3 _sn_191_getLineJoin(float edge, bool odd, vec3 left, vec3 center, vec3 right, float width) {
  vec2 join = vec2(1.0, 0.0);

  _sn_191_fixCenter(left, center, right);

  vec4 a = vec4(left.xy, right.xy);
  vec4 b = a / vec4(left.zz, right.zz);

  vec2 l = b.xy;
  vec2 r = b.zw;
  vec2 c = center.xy / center.z;

  vec4 d = vec4(l, c) - vec4(c, r);
  float l1 = dot(d.xy, d.xy);
  float l2 = dot(d.zw, d.zw);

  if (l1 + l2 > 0.0) {
    
    if (edge > 0.5 || l2 == 0.0) {
      vec2 nl = normalize(d.xy);
      vec2 tl = vec2(nl.y, -nl.x);

#ifdef LINE_PROXIMITY
      vClipProximity = 1.0;
#endif

#ifdef LINE_STROKE
      vClipStrokeEven = vClipStrokeOdd = normalize(left - center);
#endif
      join = tl;
    }
    else if (edge &lt; -0.5 || l1 == 0.0) {
      vec2 nr = normalize(d.zw);
      vec2 tr = vec2(nr.y, -nr.x);

#ifdef LINE_PROXIMITY
      vClipProximity = 1.0;
#endif

#ifdef LINE_STROKE
      vClipStrokeEven = vClipStrokeOdd = normalize(center - right);
#endif
      join = tr;
    }
    else {
      
      float lmin2 = min(l1, l2) / (width * width);

      
#ifdef LINE_PROXIMITY
      float lr     = l1 / l2;
      float rl     = l2 / l1;
      float ratio  = max(lr, rl);
      float thresh = _sn_191_lineProximity + 1.0;
      vClipProximity = (ratio > thresh * thresh) ? 1.0 : 0.0;
#endif
      
      
      vec2 nl = normalize(d.xy);
      vec2 nr = normalize(d.zw);

      vec2 tl = vec2(nl.y, -nl.x);
      vec2 tr = vec2(nr.y, -nr.x);

#ifdef LINE_PROXIMITY
      
      vec2 tc = normalize(mix(tl, tr, l1/(l1+l2)));
#else
      
      vec2 tc = normalize(tl + tr);
#endif
    
      float cosA   = dot(nl, tc);
      float sinA   = max(0.1, abs(dot(tl, tc)));
      float factor = cosA / sinA;
      float scale  = sqrt(1.0 + min(lmin2, factor * factor));

#ifdef LINE_STROKE
      vec3 stroke1 = normalize(left - center);
      vec3 stroke2 = normalize(center - right);

      if (odd) {
        vClipStrokeEven = stroke1;
        vClipStrokeOdd  = stroke2;
      }
      else {
        vClipStrokeEven = stroke2;
        vClipStrokeOdd  = stroke1;
      }
#endif
      join = tc * scale;
    }
    return vec3(join, 0.0);
  }
  else {
    return vec3(0.0);
  }

}

vec3 _sn_191_getLinePosition() {
  vec3 left, center, right, join;

  float edge = line.x;
  float offset = line.y;

  vec4 p = min(_sn_191_geometryClip, position4);
  edge += max(0.0, position4.x - _sn_191_geometryClip.x);

  
  _sn_191_getLineGeometry(p, edge, left, center, right);

#ifdef LINE_STROKE
  
  vClipStrokePosition = center;
  vClipStrokeIndex = p.x;
  bool odd = mod(p.x, 2.0) >= 1.0;
#else
  bool odd = true;
#endif

  
  float width = _sn_191_lineWidth * 0.5;

  float depth = _sn_191_focusDepth;
  if (_sn_191_lineDepth &lt; 1.0) {
    
    float z = max(0.00001, -center.z);
    depth = mix(z, _sn_191_focusDepth, _sn_191_lineDepth);
  }
  width *= depth;

  
  width *= _sn_191_worldUnit;

  join = _sn_191_getLineJoin(edge, odd, left, center, right, width);

#ifdef LINE_STROKE
  vClipStrokeWidth = width;
#endif
  
  vec3 pos = center + join * offset * width;

#ifdef LINE_CLIP
  _sn_191_clipEnds(p, center, pos);
#endif

  return pos;
}

uniform vec4 _sn_189_geometryResolution;
uniform vec4 _sn_189_geometryClip;
varying float vMask;


void _sn_189_maskLevel() {
  vec4 p = min(_sn_189_geometryClip, position4);
  vMask = _sn_189_getSample(p * _sn_189_geometryResolution);
}

uniform float _sn_192_styleZBias;
uniform float _sn_192_styleZIndex;

void _sn_192_setPosition(vec3 position) {
  vec4 pos = projectionMatrix * vec4(position, 1.0);

  
  float bias  = (1.0 - _sn_192_styleZBias / 32768.0);
  pos.z *= bias;
  
  
  if (_sn_192_styleZIndex > 0.0) {
    float z = pos.z / pos.w;
    pos.z = ((z + 1.0) / (_sn_192_styleZIndex + 1.0) - 1.0) * pos.w;
  }
  
  gl_Position = pos;
}
void main() {
  vec3 _io_546_return;

  _io_546_return = _sn_191_getLinePosition();
  _sn_192_setPosition(_io_546_return);
  _pg_101_();
}
</code></pre>

</div></div>

<div class="g8 i2"><div class="pad">

<p>It still does guarded regex manipulation of code too, but those manipulations are now derived from a proper syntax tree. GLSL doesn't have strings and its scope is simple, so this is unusually safe. I'm sure you can still trip it up somehow, but it's worth it for speed. I'm seeing assembly times of ~10-30ms cold, 2-4ms warm, but it depends entirely on the particular shaders.</p>

<p>The assembly process is now properly recursive. Unassembled shaders can be used in factory form, standing in for snippets. Completed graphs form stand-alone programs with no open inputs or outputs. The result can be turned straight into a Three.js <code>ShaderMaterial</code>, but there is no strict Three dependency. It's just a dictionary with code and a list of uniforms, attributes and varyings. Unlike before, building a combined vertex/fragment program is now merely syntactic sugar for a pair of separate graphs.</p>

<p>As it's run-time, you can slot in user-defined or code-generated GLSL just the same. Shaders are fetched by name or passed as inline code, mixed freely as needed. You supply the dictionary or lookup method. You could bundle your GLSL into JS with a build step or include embedded <code>&lt;script&gt;</code> tags.</p>

</div></div>

<div class="c"></div>

<div class="wide full">

  <div class="iframe c">
    <iframe src="/files/mathbox2/iframe-shadergraph-6.html?22e389c1" class="mathbox janky" height="700" style="width: 100%;"></iframe>
  </div>

</div>

<div class="c"></div>

<div class="g8 i2"><div class="pad">

  <p><em>This is the fragment shader that implements the partial differential equation for this ripple effect (<code>getFramesSample</code>). It samples from a volumetric N×N×2 array, feeding back into itself.</em></p>
  
<h2>Paging Dr. Hickey</h2>

<p>ShaderGraph 2 drives the entirety of MathBox 2. Its shaders are specialized for particular types and dimensions, generating procedural data, clipping geometry, resampling transformed data on the fly, …. The composibility comes out naturally. To do so, I pass a partially built <code>factory</code> by interested parties. This way I build graphs for position, color, normal, mask and more. These are injected as callbacks into a final shader. Shader factories enable ad-hoc contracts, sandwiched between the inner and outer retained layers of Three.js and MathBox, but disappearing entirely in the end result.</p>

<p>Of course, all of this is meta-programming of GLSL, done through a stateful JS lasagna and a ghetto compiler, instead of an idiomatic language. I know this, it's an inner platform effect bathing luxuriously in Turing tar like a rhino in mud. I didn't really see a way around it, given the constraints at play.</p>

<p>While the factory API is designed for making graphs on the spot and then tossing them, you could keep graphs around. There's a full data model underneath. You can always skip the factory entirely.</p>

<p>Plenty of caveats of course. There is no built-in preprocessor, so you can't <code>#define</code> or <code>#ifdef</code> uniforms or attributes and have it make sense. But then the point of ShaderGraph is to formalize exactly that sort of ad-hoc fiddling. Preprocessor directives will just pass through. <code>glsl-parser</code> has gaps too, and it is also exceedingly picky with reserved variable names, so watch out for that.</p>

<p>I did sometimes feel the need for more powerful metaprogramming, but you can work around it. It is easy to dynamically make GLSL one-liner snippets and feed them in. String manipulation of code is always still an option, you just don't need to do it at the macro-level anymore.</p>

<p>ShaderGraph 2 has been in active use now for months, it does the job I need it to very well. In a perfect world, this would be solved at the GPU driver level. Until SPIR-V or WebVulkan gets here, imma stick to my regexes. Don't try this at home, kids.</p>

<p class="mt1"><em>For docs and more, see the <a href="https://github.com/unconed/shadergraph">Git repository</a>.</em></p>

<ul class="mt3">
  <li><a href="/blog/mathbox2/">MathBox² - PowerPoint Must Die</a></li>
  <li><a href="/blog/a-dom-for-robots/">A DOM for Robots - Modelling Live Data</a></li>
  <li><a href="/blog/yak-shading/">Yak Shading - Data Driven Geometry</a></li>
  <li><em>ShaderGraph 2 - Functional GLSL</em></li>
</ul>

</div></div>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[MathBox²]]></title>
    <link href="http://acko.net/blog/mathbox2-pt2/"/>
    <updated>2015-09-27T00:00:00+02:00</updated>
    <id>http://acko.net/blog/mathbox2-pt2</id>
    <content type="html"><![CDATA[<script src="/files/katex/katex.min.js"></script>
<script src="/files/katex/contrib/auto-render.min.js"></script>
<link rel="stylesheet" type="text/css" href="/files/katex/katex.min.css" />

<div class="g8 i2 first"><div class="pad">

<h2 class="sub">Part 2</h2>

</div></div>

<div class="c"></div>

<div class="g8 i2"><div class="pad">

<a href="/blog/mathbox2/">Continued from Part 1.</a>

<h2>I-Can't-Believe-It's-Not-React</h2>

<p>Underneath sits a large codebase driving it, 200+ files in JS/CS alone, not including dependencies that aren't my own. Much of it is infrastructure necessary to pull off certain tricks consistently: you can draw 2.5D lines with grace, render arbitrary Unicode text in GL, sync HTML to GPU-computed geometry, and do all this with GLSL code composed on the fly, including your own. Nobody needs all of this.</p>

</div></div>

<div class="wide full">

  <div class="iframe c">
    <iframe src="/files/mathbox2/iframe-labels.html?22e389c1" class="mathbox autosize janky" height="320"></iframe>
  </div>

</div>

<aside class="g8 i2"><div class="pad">

    <ul>
    <li><span style="color: rgb(48,192,255)">LaTeX HTML with KaTeX.</span></li>
    <li><span style="color: rgb(48,144,255)">Plain HTML DOM.</span></li>
    <li><span style="color: rgb(192,64,0)">Virtual HTML with DOM diff.</span></li>
    <li>Live Signed Distance Fields for GL.</li>
    </ul>
  
</div></aside>

<div class="g8 i2"><div class="pad">

<p>These wildly different strategies are actually all abstracted into the DOM path or the GL path, with a quacks-like-React component as the glue in the DOM path.</p>

</div></div>

<div class="g5"><div class="pad">

<pre><code class="language-jsx wrap small"><span style="color:rgb(128,0,128)">&lt;root&gt;
  <span style="color:rgb(0,70,156)">&lt;!-- Place the camera --&gt;</span>
  &lt;camera /&gt;
  <span style="color:rgb(0,70,156)">&lt;!-- 3D Polar view --&gt;</span>
  &lt;polar&gt;
      <span style="color:rgb(0,70,156)">&lt;!-- Sample an interval --&gt;</span>
      &lt;interval /&gt;
      <span style="color:rgb(0,70,156)">&lt;!-- Draw a line --&gt;</span>
      &lt;line /&gt;
      <span style="color:rgb(0,70,156)">&lt;!-- Resample along length --&gt;</span>
      &lt;resample /&gt;
      <span style="color:rgb(0,70,156)">&lt;!-- Draw a set of points --&gt;</span>
      &lt;point /&gt;
      <span style="color:rgb(0,70,156)">&lt;!-- Make HTML labels --&gt;</span>
      &lt;html /&gt;
      <span style="color:rgb(0,70,156)">&lt;!-- Draw HTML sprites --&gt;</span>
      &lt;dom /&gt;

      <span style="color:rgb(0,70,156)">...</span>      

      <span style="color:rgb(0,70,156)">&lt;!-- Sample an interval --&gt;</span>
      &lt;interval /&gt;
      <span style="color:rgb(0,70,156)">&lt;!-- Draw a line --&gt;</span>
      &lt;line /&gt;
      <span style="color:rgb(0,70,156)">&lt;!-- Resample along length --&gt;</span>
      &lt;resample /&gt;
      <span style="color:rgb(0,70,156)">&lt;!-- Draw a set of points --&gt;</span>
      &lt;point /&gt;
      <span style="color:rgb(0,70,156)">&lt;!-- Make GL text --&gt;</span>
      &lt;text /&gt;
      <span style="color:rgb(0,70,156)">&lt;!-- Generate colors --&gt;</span>
      &lt;array /&gt;
      <span style="color:rgb(0,70,156)">&lt;!-- Draw GL sprites --&gt;</span>
      &lt;label /&gt;

    &lt;/vertex&gt;
  &lt;/cartesian&gt;
&lt;/root&gt;</span></code></pre>

</div></div>

<aside class="g7"><div class="pad">

<pre><code class="language-javascript wrap small">// Define VDOM handler to clone real DOM elements
var clone = MathBox.DOM.createClass({
  render: function (el, props, children) {
    var element = children.cloneNode(true);
    return element;
  },
});

// Define VDOM handler to format 'latex' into an HTML span
var latex = MathBox.DOM.createClass({
  render: function (el) {
    this.props.innerHTML = katex.renderToString(this.children);
    return el('span', this.props);
  }
});</code></pre>

<div class="c"></div>

<p class="tc"><em>The interface with the HTML DOM.</em></p>

<p class="tc"><em>Appearances deceive however,<br>as MathBox's own DOM is an entirely different beast.</em></p>

</div></aside>

<div class="g8 i2"><div class="pad">

<p>Most of the code is for initialization only, building up a reactive machine by combining components. Once assembled it lets the GPU do most of the crunching, while relying on the JS VM to inline and optimize the chewy outside.</p>

<p>At the top level, MathBox is plain old JavaScript, used like this:</p>

</div></div>

<div class="g8"><div class="pad">
  <pre><code class="language-javascript" tabindex="0">/* Easy Mode */

// Bootstrap MathBox and Three.js
var mathbox = mathBox();
if (mathbox.fallback) { throw Error("No WebGL support.") };

// Make MathBox primitives
var view =
  mathbox
  .set({
    scale: null,
  })
  .camera({
    proxy: true,
    position: [0, 0, 3]
  })
  .polar({
    range: [[-2, 2], [-1, 1], [-1, 1]],
    scale: [2, 1, 1],
    bend: .25
  });

view.interval({
  width: 48,
  expr: function (emit, x, i, t) {
    // Emit sine wave
    y = Math.sin(x + t / 4) * .5 + .75;
    emit(x, y);
  },
  channels: 2,
})
.line({
  color: 0x30C0FF,
  width: 16,
})
.resample({
  width: 8,
})
.point({
  color: 0x30C0FF,
  size: 60,
})
.html({
  width:  8,
  expr: function (emit, el, i, j, k, l, t) {
    // Emit random latex
    var color = ['#30D0FF','#30A0FF'][i%2];
    var a = Math.round(t + i) % symbols.length;
    var b = Math.round(Math.sqrt(t * t + Math.sin(t + i * i) + 5));
    emit(el(latex, {style: {color: color}},
      '\\sqrt{\\text{LaTeX} + '+(i + b)+' \\pi^{'+symbols[a]+'}}'));
  },
})
.dom({
  snap: false,
  offset: [0, 32],
  depth: 0,
  zoom: 1,
  outline: 2,
  size: 20,
});

// ...
</code></pre>

</div></div>

<aside class="g4 m1">

  <div class="iframe c">
    <iframe src="/files/mathbox2/iframe-splash.html?22e389c1" class="mathbox janky" height="250" style="background: #fff; width: 100%;"></iframe>

    <p class="tc">CSS 3D rims included.</p>
  </div>

</aside>

<aside class="g4">

  <div class="iframe c">
    <iframe src="/files/mathbox2/iframe-fallback.html?22e389c1" class="mathbox janky" height="250" style="background: #fff; width: 100%;"></iframe>

    <p class="tc">The default fallback message.</p>
  </div>

</aside>

<div class="g8 i2"><div class="pad">
  
<p>The WebGL Canvas bootstrapper is a separate piece though, it's wrapping <a href="https://github.com/unconed/threestrap" target="_blank">Threestrap</a>, the little non-framework that could. It lets you spawn a fully functioning GL canvas in one exceedingly configurable call. It takes care of browser support, resizing with retina, CSS alignment, warm-up and more.</p>

<p>If you prefer instead, you can spawn a bare MathBox context in a Three.js scene of your choosing, but you'll have to babysit it:</p>

</div></div>

<div class="g12"><div class="pad">
  <pre><code class="language-javascript" tabindex="1">/* Simple Mode */

// Vanilla Three.js
var renderer = new THREE.WebGLRenderer();
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(60, WIDTH / HEIGHT, .01, 1000);

// Insert into document
document.body.appendChild(renderer.domElement);

// MathBox context
var context = new MathBox.Context(renderer, scene, camera).init();
var mathbox = context.api;

// Set size
renderer.setSize(WIDTH, HEIGHT);
context.resize({ viewWidth: WIDTH, viewHeight: HEIGHT });

// Place camera and set background
camera.position.set(0, 0, 3);
renderer.setClearColor(new THREE.Color(0xFFFFFF), 1.0);

// MathBox elements
view = mathbox
.set({
  focus: 3,
})
.cartesian({
  range: [[-2, 2], [-1, 1], [-1, 1]],
  scale: [2, 1, 1],
});
// ...

// Render frames
var frame = function () {
  requestAnimationFrame(frame);
  context.frame();
  renderer.render(scene, camera);
};
requestAnimationFrame(frame);</code></pre>
</div></div>

<div class="g8 i2 m1"><div class="pad">

<p>Despite looking like a monolith, it really isn't, it was merely a matter of convenience and sanity to not decouple it more until its shape had stabilized. Minimal builds are, for now, left as an exercise to the reader. I've split up the thinking and design into several articles, mirroring the architecture. However, you don't need to know all this to <em>use</em> MathBox 2, they are for people who want to know the how and why... the <a href="/blog/a-dom-for-robots/">document model</a>, the <a href="/blog/yak-shading/">geometry core</a> and the <a href="/blog/shadergraph-2/">shader assembly</a>.</p>

<p>In putting it all together, the devil's in the details of course. Depending on your imagination, it's either much more or much less powerful than you want. There's still far too much to cover: slideshows, keyframe tracks, fov-calibrated units, z-indexes, atlas retexting, … Most of this is unsurprising in that it all works. You can define a keyframe interpolation between two value or emitter expressions, and watch the smoothly lerped data go. Animation tracks are tied to triggers like clocks and slides, which lets them fit naturally in presentations.</p>

</div></div>

<div class="c"></div>

<div class="g10 i1"><div class="pad">

<pre><code class="language-jsx wrap small"><span style="color:rgb(128,0,128)">&lt;root&gt;
  <span style="color:rgb(0,70,156)">&lt;!-- Present slides --&gt;</span>
  &lt;present <span style="color:rgb(144,64,0)">index</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{1}</span>&gt;
    <span style="color:rgb(0,70,156)">&lt;!-- Slide 1 --&gt;</span>
    &lt;slide <span style="color:rgb(144,64,0)">steps</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{2}</span>&gt;
      <span style="color:rgb(0,70,156)">&lt;!-- Transition effect --&gt;</span>
      &lt;reveal&gt;
        <span style="color:rgb(0,70,156)">&lt;!-- Sample an interval --&gt;</span>
        &lt;interval /&gt;
        <span style="color:rgb(0,70,156)">&lt;!-- Draw a line --&gt;</span>
        &lt;line /&gt;
        <span style="color:rgb(0,70,156)">&lt;!-- Step through keyframes --&gt;</span>
        &lt;step <span style="color:rgb(144,64,0)">script</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{[
            {props: {color: 'red'}},
            {props: {color: 'blue'}},
          ]}</span> &gt;
      &lt;/reveal&gt;
    &lt;/slide&gt;
    <span style="color:rgb(0,70,156)">&lt;!-- Slide 2 --&gt;</span>
    &lt;slide&gt;
      <span style="color:rgb(0,70,156)">&lt;!-- Transition effect --&gt;</span>
      &lt;move&gt;
        <span style="color:rgb(0,70,156)">&lt;!-- Sample an interval --&gt;</span>
        &lt;interval /&gt;
        <span style="color:rgb(0,70,156)">&lt;!-- Play through keyframes --&gt;</span>
        &lt;play <span style="color:rgb(144,64,0)">script</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{[
                       {props: {expr: (emit, x) => emit(x, Math.sin(x))}},
                       {props: {expr: (emit, x) => emit(x, Math.tan(x))}},
                      ]}</span> /&gt;
        <span style="color:rgb(0,70,156)">&lt;!-- Draw a line --&gt;</span>
        &lt;line /&gt;

        <span style="color:rgb(0,70,156)">&lt;!-- Slide 2.1 --&gt;</span>
        &lt;slide&gt;
        &lt;/slide&gt;
      &lt;/move&gt;
    &lt;/slide&gt;
    <span style="color:rgb(0,70,156)">&lt;!-- Slide 3 --&gt;</span>
    &lt;slide&gt;
      <span style="color:rgb(0,70,156)">&lt;!-- Transition effect --&gt;</span>
      &lt;reveal&gt;
        <span style="color:rgb(0,70,156)">&lt;!-- Sample an interval --&gt;</span>
        &lt;interval /&gt;
        <span style="color:rgb(0,70,156)">&lt;!-- Draw a line --&gt;</span>
        &lt;line /&gt;
      &lt;/reveal&gt;
    &lt;/slide&gt;
  &lt;/present&gt;
&lt;/root&gt;</span></code></pre>

</div></div>

<div class="g8 i2"><div class="pad">

<h2>One More Thing…</h2>

<p class="tc"><img src="/files/mathbox2/fridge.png" style="max-width: 30%; margin: 0 auto;" class="flat" alt="Fridge is cool" title="Fridge is cool" /></p>

<p>Images are data. So is audio. That means MathBox 2 is Winamp AVS, Milkdrop and the mythical Fridge all rolled into one. You can replicate your everyday trippy music visualizer with two operators: render-to-texture (<code>rtt</code>) and <code>compose</code>. It acts as an embedded scene, rendering all of its children to an off-screen image, while Compose renders a full-screen pass. This is where the model's expressiveness shines.</p>

<p>Milkdrop equals <code>mathbox.rtt(…).compose(…).….end().compose(…)</code>, that is, an image feeding back into itself, but also rendered to the screen. The necessary double buffering and swaps are abstracted away. Drop in shapes and shaders, add transforms, nest as you like. RTTs have a <code>history</code> parameter like arrays, so Turing patterns, self-propagating hypno spirals, and other cool partial diffy eqs are a shader away.</p>

</div></div>

<div class="c"></div>

<div class="wide full m2">

  <div class="iframe c">
    <iframe src="/files/mathbox2/iframe-rtt-history.html?22e389c1" class="mathbox autosize janky" height="320"></iframe>
  </div>

</div>

<div class="c"></div>

<div class="g12"><div class="pad">
  <!--rtt history-->

<pre><code class="language-jsx wrap small"><span></span><span style="color:rgb(128,0,128)">&lt;root </span><span style="color:rgb(144,64,0)">id</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">1</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">scale</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{720}</span><span style="color:rgb(128,0,128)"></span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;rtt </span><span style="color:rgb(144,64,0)">id</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">render</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">minFilter</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">nearest</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">magFilter</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">nearest</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">type</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">unsignedByte</span><span style="color:rgb(128,0,128)">&quot;</span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;camera </span><span style="color:rgb(144,64,0)">id</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">3</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">lookAt</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{[0, 0, 0]}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">position</span><span style="color:rgb(128,0,128)">=></span><span style="color:rgb(0,70,156)">{(t) => { return [Math.cos(t) * 3, 0, Math.sin(t) * 3] }}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">/</span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;cartesian </span><span style="color:rgb(144,64,0)">id</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">4</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">range</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{[[-2, 2], [-1, 1], [-1, 1]]}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">scale</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{[2, 1, 1]}</span><span style="color:rgb(128,0,128)"></span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;transform </span><span style="color:rgb(144,64,0)">id</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">5</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">scale</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{[7/10, 7/10, 7/10]}</span><span style="color:rgb(128,0,128)"></span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;grid </span><span style="color:rgb(144,64,0)">id</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">6</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">width</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{5}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">divideX</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{2}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">divideY</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{2}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">zBias</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{10}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">opacity</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{1/4}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">color</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{16768992}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">/</span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;/transform</span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;/cartesian</span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;/rtt</span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;rtt </span><span style="color:rgb(144,64,0)">id</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">rtt1</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">history</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{4}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">type</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">unsignedByte</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">minFilter</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">linear</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">magFilter</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">linear</span><span style="color:rgb(128,0,128)">&quot;</span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;shader </span><span style="color:rgb(144,64,0)">id</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">8</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">code</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">
uniform vec3 dataResolution;
uniform vec3 dataSize;
uniform float cosine;
uniform float sine;
vec4 getSample(vec3 xyz);
vec4 getFramesSample(vec3 xyz) {
  vec2 pos = xyz.xy * dataResolution.xy - .5;
  pos = ((pos * dataSize.xy) * mat2(cosine, sine, -sine, cosine) * .999) / dataSize.xy;
  xyz.xy = (pos + .5) * dataSize.xy;
  vec4 c = getSample(xyz + vec3( 0.0, 0.0, 1.0));
  vec3 t = getSample(xyz + vec3( 0.0, 1.5, 0.0)).xyz;
  vec3 b = getSample(xyz + vec3( 0.0,-1.5, 0.0)).xyz;
  vec3 l = getSample(xyz + vec3(-1.5, 0.0, 0.0)).xyz;
  vec3 r = getSample(xyz + vec3( 1.5, 0.0, 0.0)).xyz;
  return vec4((t + b + l + r) / 2.0 - c.xyz, c.w);
}</span><span style="color:rgb(128,0,128)">&quot;
 </span><span style="color:rgb(144,64,0)">cosine</span><span style="color:rgb(128,0,128)">=></span><span style="color:rgb(0,70,156)">{(t) => Math.cos(Math.sin(t * .2) * .005)}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">sine</span><span style="color:rgb(128,0,128)">=></span><span style="color:rgb(0,70,156)">{(t) => Math.sin(Math.sin(t * .2) * .005)}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">/</span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;resample </span><span style="color:rgb(144,64,0)">id</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">resample1</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">indices</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{3}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">channels</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{4}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">/</span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;compose </span><span style="color:rgb(144,64,0)">id</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">10</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">/</span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;compose </span><span style="color:rgb(144,64,0)">id</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">11</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">source</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">#render</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">blending</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">add</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">/</span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;/rtt</span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;rtt </span><span style="color:rgb(144,64,0)">id</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">12</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">minFilter</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">linear</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">magFilter</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">linear</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">type</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">unsignedByte</span><span style="color:rgb(128,0,128)">&quot;</span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;shader </span><span style="color:rgb(144,64,0)">id</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">colormap</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">code</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">
uniform float modulate1;
uniform float modulate2;
uniform float modulate3;
uniform float modulate4;
vec4 getSample(vec3 xyz);
vec4 getFramesSample(vec3 xyz) {
  vec4 color = (
    getSample(xyz) +
    getSample(xyz + vec3(0.0, 0.0, 1.0)) +
    getSample(xyz + vec3(0.0, 0.0, 2.0)) +
    getSample(xyz + vec3(0.0, 0.0, 3.0))
  ) / 4.0;
  color = color * color * color * 1.15;
  float v = color.x + color.y + color.z;
  vec3 c = vec3(v*v + color.x * .2, v*v, v*v*v + color.z) * .333;
  c = mix(c, mix(sqrt(c.yzx * c), c.zxy, modulate1), modulate2);
  c = mix(c, mix(c.yzx, c.zxy, modulate1), modulate2);
  c = mix(c, mix(abs(sin(c.yxz * 2.0)), c.zyx, modulate3), modulate4);
  return vec4(c, 1.0);
}</span><span style="color:rgb(128,0,128)">&quot;
 </span><span style="color:rgb(144,64,0)">modulate1</span><span style="color:rgb(128,0,128)">=></span><span style="color:rgb(0,70,156)">{(t) => Math.cos(t * .417) * .5 + .5}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">modulate2</span><span style="color:rgb(128,0,128)">=></span><span style="color:rgb(0,70,156)">{(t) => Math.cos(t * .617 + Math.sin(t * .133)) * .5 + .5}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">modulate3</span><span style="color:rgb(128,0,128)">=></span><span style="color:rgb(0,70,156)">{(t) => Math.cos(t * .217 + 2.0) * .5 + .5}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">modulate4</span><span style="color:rgb(128,0,128)">=></span><span style="color:rgb(0,70,156)">{(t) => Math.cos(t * .117 + 3.0 + Math.sin(t * .133)) * .5 + .5}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">/</span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;resample </span><span style="color:rgb(144,64,0)">id</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">resample2</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">source</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">#rtt1</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">indices</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{3}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">channels</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{4}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">/</span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;compose </span><span style="color:rgb(144,64,0)">id</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">15</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">/</span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;/rtt</span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;shader </span><span style="color:rgb(144,64,0)">id</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">16</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">code</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">
vec4 getSample(vec2 xy);
vec4 getFramesSample(vec2 xy) {
  return getSample(xy + vec2(0.5, 0.5));
}</span><span style="color:rgb(128,0,128)">&quot;
 </span><span style="color:rgb(144,64,0)">/</span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;resample </span><span style="color:rgb(144,64,0)">id</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">resample3</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">indices</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{2}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">channels</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{4}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">/</span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;compose </span><span style="color:rgb(144,64,0)">id</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">18</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">source</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">#resample2</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">/</span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
</span><span style="color:rgb(128,0,128)">&lt;/root</span><span style="color:rgb(128,0,128)">></span><span style="color:inherit"></span></code></pre>

</div></div>

<div class="c"></div>

<div class="g8 i2 m2"><div class="pad">

<p>The difference compared to AVS is that the <code>.rtt()</code> is inert to its container by default. Until you add on a <code>.compose()</code> pass, it's just a dangling data source. Meanwhile the <code>.compose()</code> op offers the necessary  GL blend modes, opacity and color tints through style properties. Document order defines drawing order, so the decomposition into render passes is direct. On top, <code>zOrder</code> can be overridden (drawing order), as can <code>zIndex</code> (2D stacking order) and <code>zBias</code> (3D stacking order).</p>

<p>You can nest effects and compose shaders to create recursive visualizations, sampling from themselves or each other:</p>

</div></div>

<div class="c"></div>

<div class="wide full m2">

  <div class="iframe c">
    <iframe src="/files/mathbox2/iframe-readyornot.html?22e389c1" class="mathbox autosize janky" height="320" allowfullscreen="allowfullscreen"></iframe>
  </div>
  
  <div class="c"></div>
  
  <p class="tc"><a href="http://acko.net/files/mathbox2/iframe-readyornot.html?1" target="_blank">Open in New Window</a><br><em>(Burn that fillrate, baby.)</em></p>
  
  <p class="tc m2">Bonus: <a href="http://fm.acko.net/" target="_blank">Endless Visualizer</a>.</p>
</div>

<div class="g12"><div class="pad">


  <!--vertex feedback-->
<pre><code class="language-jsx wrap small"><span></span><span style="color:rgb(128,0,128)">&lt;root </span><span style="color:rgb(144,64,0)">id</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">1</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">scale</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{720}</span><span style="color:rgb(128,0,128)"></span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;camera </span><span style="color:rgb(144,64,0)">id</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">2</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">proxy</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{true}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">position</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{[3/10, 1/10, 2]}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">/</span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;group </span><span style="color:rgb(144,64,0)">id</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">3</span><span style="color:rgb(128,0,128)">&quot;</span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;array </span><span style="color:rgb(144,64,0)">id</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">audioTime</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">data</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{[]}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">width</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{1024}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">channels</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{1}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">/</span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;array </span><span style="color:rgb(144,64,0)">id</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">audioFreq</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">data</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{[]}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">width</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{512}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">channels</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{1}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">/</span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;/group</span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;rtt </span><span style="color:rgb(144,64,0)">id</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">render</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">width</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{256}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">height</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{144}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">type</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">unsignedByte</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">minFilter</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">nearest</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">magFilter</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">nearest</span><span style="color:rgb(128,0,128)">&quot;</span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;camera </span><span style="color:rgb(144,64,0)">id</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">7</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">position</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{[0, 0, 5/2]}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">/</span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;group </span><span style="color:rgb(144,64,0)">id</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">8</span><span style="color:rgb(128,0,128)">&quot;</span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;swizzle </span><span style="color:rgb(144,64,0)">id</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">9</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">source</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">#audioTime</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">order</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">yx</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">/</span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;spread </span><span style="color:rgb(144,64,0)">id</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">10</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">width</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{[861/250, 0, 0, 0]}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">/</span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;shader </span><span style="color:rgb(144,64,0)">id</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">11</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">code</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">
vec4 getSample(vec4 xyzw);
vec4 getColor(vec4 xyzw) {
  float h = getSample(xyzw).y;
  return vec4(vec3(h), 1.0);
}</span><span style="color:rgb(128,0,128)">&quot;
 </span><span style="color:rgb(144,64,0)">/</span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;resample </span><span style="color:rgb(144,64,0)">id</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">12</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">/</span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;transform </span><span style="color:rgb(144,64,0)">id</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">13</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">scale</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{[1, 3/4, 1]}</span><span style="color:rgb(128,0,128)"></span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;line </span><span style="color:rgb(144,64,0)">id</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">14</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">points</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">&lt;&lt;</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">colors</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">&lt;</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">width</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{5}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">color</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{16777215}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">opacity</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{2/5}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">blending</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">add</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">/</span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;/transform</span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;/group</span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;cartesian </span><span style="color:rgb(144,64,0)">id</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">15</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">range</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{[[-2, 2], [-1, 1], [-1, 1]]}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">scale</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{[1/2, 1/4, 1/4]}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">quaternion</span><span style="color:rgb(128,0,128)">=></span><span style="color:rgb(0,70,156)">{(t) => {
          c = Math.cos(t / 3);
          s = Math.sin(t / 3);
          c2 = Math.cos(t / 8.71);
          s2 = Math.sin(t / 8.71);
          return [s * s2, s * c2, .2, c];
        }}</span><span style="color:rgb(128,0,128)"></span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;grid </span><span style="color:rgb(144,64,0)">id</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">16</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">divideX</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{4}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">divideY</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{4}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">zBias</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{10}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">opacity</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{1/10}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">color</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{16768992}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">width</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{6}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">/</span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;/cartesian</span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;/rtt</span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;rtt </span><span style="color:rgb(144,64,0)">id</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">rtt1</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">history</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{4}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">width</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{256}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">height</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{144}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">type</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">unsignedByte</span><span style="color:rgb(128,0,128)">&quot;</span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;shader </span><span style="color:rgb(144,64,0)">id</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">18</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">code</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">#map-rotate</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">/</span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;resample </span><span style="color:rgb(144,64,0)">id</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">resample1</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">indices</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{3}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">channels</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{4}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">/</span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;compose </span><span style="color:rgb(144,64,0)">id</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">20</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">color</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">#ffffff</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">zWrite</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{false}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">/</span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;compose </span><span style="color:rgb(144,64,0)">id</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">21</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">source</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">#render</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">blending</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">add</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">color</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">#ffffff</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">zWrite</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{false}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">/</span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;/rtt</span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;rtt </span><span style="color:rgb(144,64,0)">id</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">rtt2</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">width</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{256}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">height</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{144}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">type</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">float</span><span style="color:rgb(128,0,128)">&quot;</span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;camera </span><span style="color:rgb(144,64,0)">id</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">23</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">position</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{[0, 0, 5/2]}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">/</span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;clock </span><span style="color:rgb(144,64,0)">id</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">24</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">seek</span><span style="color:rgb(128,0,128)">=></span><span style="color:rgb(0,70,156)">{(t) => audio ? audio.currentTime : t}</span><span style="color:rgb(128,0,128)"></span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;shader </span><span style="color:rgb(144,64,0)">id</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">25</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">code</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">#map-temporal-blur</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">time</span><span style="color:rgb(128,0,128)">=></span><span style="color:rgb(0,70,156)">{(t) => t * 16.0}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">modulate</span><span style="color:rgb(128,0,128)">=></span><span style="color:rgb(0,70,156)">{(t) => {
            var bang = ((t > 69.229311)  &amp;&amp; (t &lt; 88.922656)) ||
                       ((t > 88.922656)  &amp;&amp; (t &lt; 148.9143)) ||
                       ((t > 148.9143)   &amp;&amp; (t &lt; 158.2)) ||
                       ((t > 168.284427) &amp;&amp; (t &lt; 188.00)) ? 1 : 0;
            if ((t > 88.922656)  &amp;&amp; (t &lt; 148.9143)) {
              bang *= .5 + .45 * Math.cos(t / 3);
            }
            if ((t > 168)) {
              bang *= .85 + .15 * Math.cos(t);
            }
            modulate = modulate + (bang - modulate) * .1;
            return modulate;
          }}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">pattern</span><span style="color:rgb(128,0,128)">=></span><span style="color:rgb(0,70,156)">{(t) => {
            var bang = ((t > 88.922656) &amp;&amp; (t &lt; 148.9143));
            pattern = pattern + (bang - pattern) * .1;
            if ((t > 168)) {
              pattern = .5 + .4 * Math.cos(t * 2.311);
            }
            return pattern;
          }}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">warp</span><span style="color:rgb(128,0,128)">=></span><span style="color:rgb(0,70,156)">{(t) => {
            var bang = (t > 148.9143);
            if ((t > 168)) {
              warp *= 1 + .5 * Math.cos(t * .556);
            }
            if ((t > 148.2) &amp;&amp; (t &lt; 158.2)) warp = warp + .75 + .25 * Math.cos((t - 158.2));
            warp = warp + (bang - warp) * .1;
            return warp;
          }}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">shift</span><span style="color:rgb(128,0,128)">=></span><span style="color:rgb(0,70,156)">{(t) => {
            var bang = (t > 168) ? Math.max(0, Math.min(1, .1 * (t - 168))) : 0;
            bang *= .75 + .25 * Math.cos(t * .731);
            warp = warp + (bang - warp) * .1;
            return warp;
          }}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">/</span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;resample </span><span style="color:rgb(144,64,0)">id</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">resample2</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">source</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">#rtt1</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">indices</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{3}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">channels</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{4}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">/</span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;compose </span><span style="color:rgb(144,64,0)">id</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">27</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">color</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">#fff</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">zWrite</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{false}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">/</span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;/clock</span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;transform </span><span style="color:rgb(144,64,0)">id</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">28</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">scale</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{[1, 1/4, 1]}</span><span style="color:rgb(128,0,128)"></span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;swizzle </span><span style="color:rgb(144,64,0)">id</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">29</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">source</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">#audioTime</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">order</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">yx</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">/</span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;spread </span><span style="color:rgb(144,64,0)">id</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">30</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">width</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{[861/250, 0, 0, 0]}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">/</span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;shader </span><span style="color:rgb(144,64,0)">id</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">31</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">code</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">
vec4 getSample(vec4 xyzw);
vec4 getColor(vec4 xyzw) {
  float h = getSample(xyzw).y;
  return vec4(vec3(h) * .2, 1.0);
}</span><span style="color:rgb(128,0,128)">&quot;
 </span><span style="color:rgb(144,64,0)">/</span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;resample </span><span style="color:rgb(144,64,0)">id</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">32</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">/</span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;line </span><span style="color:rgb(144,64,0)">id</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">33</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">points</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">&lt;&lt;</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">colors</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">&lt;</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">width</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{50}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">color</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{16777215}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">opacity</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{1}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">blending</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">add</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">/</span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;/transform</span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;/rtt</span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;resample </span><span style="color:rgb(144,64,0)">id</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">34</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">width</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{129}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">height</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{73}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">/</span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;repeat </span><span style="color:rgb(144,64,0)">id</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">lerp</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">depth</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{2}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">/</span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;shader </span><span style="color:rgb(144,64,0)">id</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">36</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">code</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">#map-xy-to-xyz</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">/</span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;resample </span><span style="color:rgb(144,64,0)">id</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">37</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">indices</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{3}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">channels</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{3}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">/</span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;transpose </span><span style="color:rgb(144,64,0)">id</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">transpose</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">order</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">xywz</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">/</span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;transpose </span><span style="color:rgb(144,64,0)">id</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">color</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">source</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">#lerp</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">order</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">xywz</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">/</span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;clock </span><span style="color:rgb(144,64,0)">id</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">40</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">seek</span><span style="color:rgb(128,0,128)">=></span><span style="color:rgb(0,70,156)">{(t) => audio ? audio.currentTime : t}</span><span style="color:rgb(128,0,128)"></span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;clock </span><span style="color:rgb(144,64,0)">id</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">disco</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">speed</span><span style="color:rgb(128,0,128)">=></span><span style="color:rgb(0,70,156)">{(t) => {
        var bang = ((t > 69.329311)  &amp;&amp; (t &lt; 89.122656)) ||
                   ((t > 148.9143)   &amp;&amp; (t &lt; 158.0)) ||
                   ((t > 168.284427) &amp;&amp; (t &lt; 188.077772));
        return bang ? 1 : .2;
      }}</span><span style="color:rgb(128,0,128)"></span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;shader </span><span style="color:rgb(144,64,0)">id</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">42</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">code</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">#map-z-to-color</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">modulate1</span><span style="color:rgb(128,0,128)">=></span><span style="color:rgb(0,70,156)">{(t) => Math.cos((t + 1) * .417) * .5 + .5}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">modulate2</span><span style="color:rgb(128,0,128)">=></span><span style="color:rgb(0,70,156)">{(t) => Math.cos((t + 1) * .617 + Math.sin(t * .133)) * .5 + .5}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">modulate3</span><span style="color:rgb(128,0,128)">=></span><span style="color:rgb(0,70,156)">{(t) => Math.cos((t + 1) * .217 + 2.0) * .5 + .5}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">modulate4</span><span style="color:rgb(128,0,128)">=></span><span style="color:rgb(0,70,156)">{(t) => Math.cos((t + 1) * .117 + 3.0 + Math.sin(t * .133)) * .5 + .5}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">/</span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;resample </span><span style="color:rgb(144,64,0)">id</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">color1</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">source</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">#lerp</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">indices</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{2}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">channels</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{4}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">/</span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;shader </span><span style="color:rgb(144,64,0)">id</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">44</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">code</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">#map-z-to-color-2</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">modulate1</span><span style="color:rgb(128,0,128)">=></span><span style="color:rgb(0,70,156)">{(t) => Math.cos((t + 1) * .417) * .5 + .5}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">modulate2</span><span style="color:rgb(128,0,128)">=></span><span style="color:rgb(0,70,156)">{(t) => Math.cos((t + 1) * .617 + Math.sin(t * .133)) * .5 + .5}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">modulate3</span><span style="color:rgb(128,0,128)">=></span><span style="color:rgb(0,70,156)">{(t) => Math.cos((t + 1) * .217 + 2.0) * .5 + .5}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">modulate4</span><span style="color:rgb(128,0,128)">=></span><span style="color:rgb(0,70,156)">{(t) => Math.cos((t + 1) * .117 + 3.0 + Math.sin(t * .133)) * .5 + .5}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">/</span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;resample </span><span style="color:rgb(144,64,0)">id</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">color2</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">source</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">#lerp</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">indices</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{2}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">channels</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{4}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">/</span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;/clock</span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;cartesian </span><span style="color:rgb(144,64,0)">id</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">46</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">range</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{[[-1.7788, 1.7788], [-1, 1], [-1, 1]]}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">scale</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{[16/9, 1, 1]}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">quaternion</span><span style="color:rgb(128,0,128)">=></span><span style="color:rgb(0,70,156)">{(t) => {
        t = t / 3;
        c = Math.cos(t / 4);
        s = Math.sin(t / 4);
        c2 = Math.cos(t / 11.71) * 1.71;
        s2 = Math.sin(t / 11.71) * 1.71;
        return [s * s2, s * c2, -.2, c];
      }}</span><span style="color:rgb(128,0,128)"></span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;lerp </span><span style="color:rgb(144,64,0)">id</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">47</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">source</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">#transpose</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">width</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{33}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">height</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{19}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">/</span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;lerp </span><span style="color:rgb(144,64,0)">id</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">48</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">source</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">#color2</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">width</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{33}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">height</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{19}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">/</span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;transform </span><span style="color:rgb(144,64,0)">id</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">49</span><span style="color:rgb(128,0,128)">&quot;</span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;line </span><span style="color:rgb(144,64,0)">id</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">50</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">points</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">&lt;&lt;</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">colors</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">&lt;</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">color</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">#ffffff</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">width</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{2}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">zBias</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{5}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">/</span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;/transform</span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;play </span><span style="color:rgb(144,64,0)">id</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">51</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">script</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{<span></span>{19: {position: [0, 0, 0]}, 39: {position: [0, 0, 2]}, 57: {position: [0, 0, 0]}}}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">/</span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;transpose </span><span style="color:rgb(144,64,0)">id</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">52</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">source</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">&lt;&lt;</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">order</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">yxzw</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">/</span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;transpose </span><span style="color:rgb(144,64,0)">id</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">53</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">source</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">&lt;&lt;</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">order</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">yxzw</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">/</span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;transform </span><span style="color:rgb(144,64,0)">id</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">54</span><span style="color:rgb(128,0,128)">&quot;</span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;line </span><span style="color:rgb(144,64,0)">id</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">55</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">points</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">&lt;&lt;</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">colors</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">&lt;</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">color</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">#ffffff</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">width</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{2}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">zBias</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{5}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">/</span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;/transform</span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;play </span><span style="color:rgb(144,64,0)">id</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">56</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">script</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{<span></span>{19: {position: [0, 0, 0]}, 39: {position: [0, 0, -2]}, 57: {position: [0, 0, 0]}}}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">/</span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;transform </span><span style="color:rgb(144,64,0)">id</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">57</span><span style="color:rgb(128,0,128)">&quot;</span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;point </span><span style="color:rgb(144,64,0)">id</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">58</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">points</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">&lt;&lt;</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">colors</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">&lt;</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">color</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">#ffffff</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">size</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{10}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">zBias</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{5}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">zOrder</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{1}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">blending</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">add</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">zWrite</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{false}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">/</span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;/transform</span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;play </span><span style="color:rgb(144,64,0)">id</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">59</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">script</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{<span></span>{19: {position: [0, 0, 0]}, 39: {position: [0, 0, -1]}, 57: {position: [0, 0, 0]}}}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">/</span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;transform </span><span style="color:rgb(144,64,0)">id</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">60</span><span style="color:rgb(128,0,128)">&quot;</span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;point </span><span style="color:rgb(144,64,0)">id</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">61</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">points</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">#transpose</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">colors</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">#color2</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">color</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">#ffffff</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">size</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{5}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">zBias</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{5}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">zOrder</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{1}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">blending</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">add</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">zWrite</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{false}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">/</span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;/transform</span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;play </span><span style="color:rgb(144,64,0)">id</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">62</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">script</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{<span></span>{9: {position: [0, 0, 0]}, 39: {position: [0, 0, 1]}, 57: {position: [0, 0, 0]}}}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">/</span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;vector </span><span style="color:rgb(144,64,0)">id</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">63</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">points</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">#transpose</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">colors</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">#color1</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">color</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">#ffffff</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">start</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{false}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">end</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{false}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">width</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{40}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">opacity</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{3/100}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">blending</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">add</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">zWrite</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{false}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">zOrder</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{-2}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">/</span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;/cartesian</span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;/clock</span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
</span><span style="color:rgb(128,0,128)">&lt;/root</span><span style="color:rgb(128,0,128)">></span><span style="color:inherit"></span></code></pre>
  
</div></div>

<div class="c"></div>

<div class="g8 i2 m2"><div class="pad">

<h2>Full Stack</h2>

<p>What's left is basically kicking the tires and fixing the blind spots. As such this is not MathBox 2.0, this is MathBox 2 Alpha 1. It's still rough in the compatibility department, easily letting you exceed GL limits satisfied by only 70-80% of WebGL implementations in the wild, without warning. My own goal for public release was to be able to make another one of <em>those</em> presentations with it, only this time, 100% idiosyncratic MathBox. Result, <a href="http://acko.net/tv/pixelfactory/" target="_blank">The Pixel Factory</a>.</p>

<p>Some people have assumed this talk was another tour-de-force of multi-week autism, but in fact, rebuilding my old slides for v2 was easy and obvious. The RGBA subpixels and their labels are animated lambdas and GLSL. The multi-samples, the depth buffer columns, the tangents and normals, same thing. JavaScript twiddles the knobs while the GPU visualizes the visualizer, and in doing so, itself.</p>

<p><a href="https://gitgud.io/unconed/mathbox" target="_blank">Here the code be</a>.</p>

<p><em>To give it a whirl in your browser, open the <a target="_blank" href="http://jsbin.com/hasuhaw/edit?html,output">JSBin Sandbox</a>. There is a <a target="_blank" href="https://gitgud.io/unconed/mathbox/blob/master/docs/intro.md">quick start introduction</a> and a <a target="_blank" href="https://gitgud.io/unconed/mathbox/blob/master/docs/primitives.md">list of legos</a>.</em></p>

<ul>
  <li><em>MathBox² - PowerPoint Must Die</em></li>
  <li><a href="/blog/a-dom-for-robots/">A DOM for Robots - Modelling Live Data</a></li>
  <li><a href="/blog/yak-shading/">Yak Shading - Data Driven Geometry</a></li>
  <li><a href="/blog/shadergraph-2/">ShaderGraph 2 - Functional GLSL</a></li>
</ul>


</div></div>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[MathBox²]]></title>
    <link href="http://acko.net/blog/mathbox2/"/>
    <updated>2015-09-27T00:00:00+02:00</updated>
    <id>http://acko.net/blog/mathbox2</id>
    <content type="html"><![CDATA[<script src="/files/katex/katex.min.js"></script>
<script src="/files/katex/contrib/auto-render.min.js"></script>
<link rel="stylesheet" type="text/css" href="/files/katex/katex.min.css" />

<script type="text/javascript">
Acko.queue(function () {
  renderMathInElement(document.querySelector('article'), {delimiters: [
    {left: "$$", right: "$$", display: true},
    {left: "($", right: "$)", display: false},
  ]});
});
</script>

<div class="g8 i2 first"><div class="pad">

<h2 class="sub">PowerPoint Must Die</h2>

</div></div>

<div class="c"></div>

<div class="g12"><div class="pad">

<blockquote class="m2">
  <em class="big">"<strong>I think a lot of mathematics is really about how you understand things in your head.</strong> It's people that did mathematics, we're not just general purpose machines, we're people. We see things, we feel things, we think of things. A lot of what I have done in my mathematical career has had to do with finding new ways to build models, to see things, do computations. Really get a feel for stuff.<br><br>
It may seem unimportant, but when I started out people drew pictures of 3-manifolds one way and I started drawing them a different way. People drew pictures of surfaces one way and I started drawing them a different way. <strong>There's something significant about how the representation in your head profoundly changes how you think.</strong><br><br>
It's very hard to do a brain dump. Very hard to do that. But I'm still going to try to do something to give a feel for 3-manifolds. Words are one thing, we can talk about geometric structures. There are many precise mathematical words that could be used, but they don't automatically convey a feeling for it. <strong>I probably can't convey a feeling for it either, but I want to try.</strong>"</em>
<div class="tr m1">– <a href="https://duckduckgo.com/?q=William+Thurston" target="_blank">William Thurston</a>, The Mystery of 3-Manifolds (<a href="https://www.youtube.com/watch?v=4jdmkUQDWtQ&amp;t=168s" target="_blank">Video</a>)</div>
</blockquote>

</div></div>

<div class="g8 i2 m2"><div class="pad">

<p>How do you convince web developers—heck, people in general—to care about math? This was the challenge underlying <a href="/tv/wdcode/">Making Things With Maths</a>, a talk I gave three years ago. I didn't know either, I just knew why I liked this stuff: demoscene, games, simulation, physics, VR, … It had little to do with what passed for mathematics in my own engineering education. There we were served only eyesore PowerPoints or handwritten overhead transparencies, with simplified graphs, abstract flowcharts and rote formulas, available on black and white photocopies.</p>

<p>Smart people who were supposed to teach us about technology seemed unable to teach us <em>with</em> technology. Fixing this felt like a huge challenge where I'd have to start from scratch. This is why the focus was entirely on showing rather than telling, and why MathBox 1 was born. It's how this stuff looks and feels in my head, and how I got my degree: by translating formulas into mental pictures, which I could replay and reason about on demand.</p>

<h2>PowerPoint Syndrome</h2>

<p>Initially I used MathBox like an embedded image or video: compact diagrams, each a point or two in a presentation. My style quickly shifted though. I kept on finding ways to transform from one visualization to another. Not for show, but to reveal the similarities and relationships underneath. MathBox encouraged me to animate things correctly, leveraging the actual models themselves, instead of doing a visual morph from A to B. Each animation became a continuous stream of valid examples, a quality both captivating and revealing.</p>

</div></div>

<div class="c"></div>

<div class="wide mt2">
    <a target="_blank" href="/blog/how-to-fold-a-julia-fractal/"><img src="/files/mathbox2/julia-fractal.jpg" alt="How to Fold a Julia Fractal"></a>
</div>

<div class="g8 i2 m2"><div class="pad">

<p>For instance, <a href="/blog/how-to-fold-a-julia-fractal/">How to Fold a Julia Fractal</a> is filled with animations of complex exponentials, right from the get go. This way I avoid the scare that ($ e^{i\pi} $) is a meaningful expression; symbology and tau-tology never have a chance to obscure geometrical workings. Instead a web page that casually demonstrates conformal mapping and complex differential equations got 340,000 visits. Despite spotty web browser support and excluding all mobile phones for years.</p>

</div></div>

<aside class="g4 m3 ti tc muted"><div class="pad">
  <img src="/files/mathbox2/elsevier.png" style="max-width: 75%; margin: 0 auto;" alt="Elsevier $42 per PDF paywall" />
  <p>Meanwhile academics voluntarily published their writings behind a&nbsp;<a href="http://www.elsevier.com" rel="nofollow">$42&nbsp;per&nbsp;PDF</a> paywall, the&nbsp;<em>colossal&nbsp;idiots</em>.</p>
</div></aside>

<div class="g7 m1"><div class="pad">

<p>The next talk, <a href="/tv/webglmath/">Making WebGL Dance</a>, contained elaborate <em>long takes</em> worthy of an Alfonso Cuarón film, with only 3 separate shots for the bulk of a 30 minute talk. The lesson seemed obvious: the slides shouldn't have graphics in them, rather, <em>the graphics should have slides in them</em>. The diagnosis of PowerPoint syndrome is then the constant trashing of context from one slide to the next. A traditional blackboard doesn't have this problem: you build up diagrams slowly, by hand, across a large surface, erasing selectively and only when you run out of space.</p>

</div></div>

<div class="c"></div>

<div class="g8 i2 m2"><div class="pad">

<p>It's not just about permanence and progression though, it's also about leveraging our natural understanding of shape, scale, color and motion. Think of how a toddler learns to interact with the world: poke, grab, chew, spit, smash. Which evolves into run, jump, fall, get back up again. Humans are naturals at taking multiple cases of "If I do this, that will happen" and turning it into a consistent, functional model of how things work. We learn language by bootstrapping random jibberish into situational meaning, converging on a shared protocol.</p>

<p>That said, I find the usual descriptions of how people experience language and thought foreign. Instead, when <a href="http://grandin.com" target="_blank">Temple Grandin</a> speaks about visual thinking, I nod vigorously. Thought to me is analog concepts and sensory memories, remixed with visual and other simulations. It builds off the quantities and qualities present in spatial and temporal notions, which appear built-in to us.</p>

</div></div>

<div class="g8 m1"><div class="pad">

<p>Speech and writing is then a program designed to reconstruct particular thoughts in a compatible brain. There are a multitude of evolving languages, they can be used elegantly, bluntly, incomprehensibly, but the desired output remains the same. In my talks, armed with weapons-grade <a href="https://duckduckgo.com/?q=c2+continuous" target="_blank">C2-continuous</a> animations, it is easy to transcode my film reel into words, because the slides run themselves. The string of concepts already hangs in the air, I only add the missing grammar that links them up. This is a puzzle our brains are so good at solving, we usually do it <em>without</em> thinking.</p>

<p>Language is the ability of thoughts to compute their own source code.</p>

<p>(It's not proof, I just supply pudding.)</p>

</div></div>

<aside class="g4 m2"><div class="pad">
  <img src="/files/mathbox2/remote.jpg" alt="powerpoint remote" style="max-width: 50%; margin: 0 auto" />
  <p class="tc">Tip: Powerpoint remotes are 4-key USB keyboards with <kbd>PageUp</kbd>/<kbd>PageDown</kbd>, <kbd>F5</kbd> and <kbd>.</kbd> keys.<br><br>Comes with dongle.</p>
</div></aside>

<div class="c"></div>

<div class="g6 m3"><div class="pad">
  <img src="/files/mathbox2/sketch1.jpg" alt="mathbox presentation slide sketches" />
</div></div>
<div class="g6 m2"><div class="pad">
  <img src="/files/mathbox2/sketch2.jpg" alt="mathbox presentation slide sketches" />
</div></div>

<aside class="g12"><div class="pad">
  <p class="tc">I sketch rough thumbnails, then start animating until I hit a dead end. Then start another one. Titles and overlays always come last.</p>
</div></aside>

<div class="g8 i2 m2"><div class="pad">

<h2>Manifold Dreams</h2>

<p>I don't say all this to up my Rain Man cred, but to lay to rest the recurring question of where my work comes from. I translate the pictures in my head to HD, in order to learn from and refine the view. As I did with <a href="/blog/animate-your-way-to-glory-pt2/#quaternions">quaternions</a>: I struggled to grok the hypersphere, it wouldn't fit together right. So I wrote the code to trace out geodesics in color and fly around in it, and suddenly the twisting made sense. Hence my entire tutorial was built to replicate the same discovery process I went through myself.</p>

</div></div>

<div class="c"></div>

<aside class="wide full">

  <div class="iframe c m1">
    <iframe src="/files/mathbox2/iframe-quat.html?22e389c1" class="mathbox autosize janky" height="320"></iframe>

    <p class="tc">For visualizing the 4D hypersphere, quaternions are a natural fit.<br>They reveal their underlying cyclic symmetry under 4D stereographic projection.</p>
  </div>

</aside>

<div class="g8 i2"><div class="pad">

<p>There was one big problem: scenes now consisted of <em>diagrams of diagrams</em>, which meant working around MathBox more than with it. Performance issues arose as complexity grew. Above all there was a total lack of composability in the components. None of this could be fixed without ripping out significant pieces, so doing it incrementally seemed futile. I started from scratch and set off to reinvent all the wheels.</p>

<p class="tc">$$ \text{MathBox}^2 = \int_1^2 \text{code}(v) dv $$</p>

<p>MathBox 2 was inevitably going to suffer <em>second-system syndrome</em>, parts would be overengineered. Rather than fight it, I embraced it and effectively wrote a strange vector GPU driver in CoffeeScript. (Such is life, this is a blueprint meant to be simplified and made obsolete over time, not expanded upon.) It's a freight train straight to the heart of a graphics card, combining low-level and high-level in a way that feels novel <span style="font-size:0;opacity:0;width:0;position:absolute;left:-10000px;">🐴</span> when you use it, squeezing <span style="font-size:0;opacity:0;width:0;position:absolute;left:-10000px;">🐴</span> through a very small opening.</p>

<p>What was tedious before, now falls out naturally. If I format the scene above as XML/JSX, it becomes:</p>

</div></div>

<div class="g8 i2"><div class="pad">
  <!--first-->

<pre><code class="language-jsx wrap small"><span style="color:rgb(128,0,128)">&lt;root&gt;
  <span style="color:rgb(0,70,156)">&lt;!-- Place the camera --&gt;</span>
  &lt;camera /&gt;
  <span style="color:rgb(0,70,156)">&lt;!-- Change clock speed --&gt;</span>
  &lt;clock&gt;
    <span style="color:rgb(0,70,156)">&lt;!-- 4D Stereographic projection --&gt;</span>
    &lt;stereographic4&gt;
      <span style="color:rgb(0,70,156)">&lt;!-- Custom 4D rotation shader --&gt;</span>
      &lt;shader /&gt;
      <span style="color:rgb(0,70,156)">&lt;!-- Move vertices --&gt;</span>
      &lt;vertex&gt;
        <span style="color:rgb(0,70,156)">&lt;!-- Sample an area --&gt;</span>
        <span style="color:rgb(0,70,156)">&lt;!-- Draw a set of lines --&gt;</span>
        <span style="color:rgb(32,128,240)">&lt;area /&gt;</span>
        <span style="color:rgb(32,128,240)">&lt;line /&gt;</span>

        <span style="color:rgb(0,70,156)">&lt;!-- Sample an area --&gt;</span>
        <span style="color:rgb(0,70,156)">&lt;!-- Draw a set of lines --&gt;</span>
        <span style="color:rgb(32,140,0)">&lt;area /&gt;</span>
        <span style="color:rgb(32,140,0)">&lt;line /&gt;</span>

        <span style="color:rgb(0,70,156)">&lt;!-- Sample an area --&gt;</span>
        <span style="color:rgb(0,70,156)">&lt;!-- Draw a set of lines --&gt;</span>
        <span style="color:rgb(203,32,0)">&lt;area /&gt;</span>
        <span style="color:rgb(203,32,0)">&lt;line /&gt;</span>
      &lt;/vertex&gt;
    &lt;/stereographic4&gt;
  &lt;/clock&gt;
&lt;/root&gt;</span></code></pre>

</div></div>

<div class="c"></div>

<div class="g12 m1"><div class="pad">
  
<p>In order to make these pieces behave, a bunch of additional attributes are applied, most of which are strings or values, some of which are functions/code, either JavaScript or GLSL:</p>

</div></div>

<div class="g12"><div class="pad">
  <!--second-->

<pre><code class="language-jsx wrap small"><span></span><span style="color:rgb(128,0,128)">&lt;root </span><span style="color:rgb(144,64,0)">id</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">1</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">scale</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{300}</span><span style="color:rgb(128,0,128)"></span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;camera </span><span style="color:rgb(144,64,0)">id</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">2</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">proxy</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{true}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">position</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{[0, 0, 3]}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">/</span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;clock </span><span style="color:rgb(144,64,0)">id</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">3</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">speed</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{1/4}</span><span style="color:rgb(128,0,128)"></span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;stereographic4 </span><span style="color:rgb(144,64,0)">id</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">4</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">bend</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{1}</span><span style="color:rgb(128,0,128)"></span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;shader </span><span style="color:rgb(144,64,0)">id</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">5</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">code</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">
uniform float cos1;
uniform float sin1;
uniform float cos2;
uniform float sin2;
uniform float cos3;
uniform float sin3;
uniform float cos4;
uniform float sin4;

vec4 getRotate4D(vec4 xyzw, inout vec4 stpq) {
  xyzw.xy = xyzw.xy * mat2(cos1, sin1, -sin1, cos1);
  xyzw.zw = xyzw.zw * mat2(cos2, sin2, -sin2, cos2);
  xyzw.xz = xyzw.xz * mat2(cos3, sin3, -sin3, cos3);
  xyzw.yw = xyzw.yw * mat2(cos4, sin4, -sin4, cos4);

  return xyzw;
}</span><span style="color:rgb(128,0,128)">&quot;
 </span><span style="color:rgb(144,64,0)">cos1</span><span style="color:rgb(128,0,128)">=></span><span style="color:rgb(0,70,156)">{(t) => Math.cos(t * .111)}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">sin1</span><span style="color:rgb(128,0,128)">=></span><span style="color:rgb(0,70,156)">{(t) => Math.sin(t * .111)}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">cos2</span><span style="color:rgb(128,0,128)">=></span><span style="color:rgb(0,70,156)">{(t) => Math.cos(t * .151 + 1)}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">sin2</span><span style="color:rgb(128,0,128)">=></span><span style="color:rgb(0,70,156)">{(t) => Math.sin(t * .151 + 1)}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">cos3</span><span style="color:rgb(128,0,128)">=></span><span style="color:rgb(0,70,156)">{(t) => Math.cos(t * .071 + Math.sin(t * .081))}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">sin3</span><span style="color:rgb(128,0,128)">=></span><span style="color:rgb(0,70,156)">{(t) => Math.sin(t * .071 + Math.sin(t * .081))}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">cos4</span><span style="color:rgb(128,0,128)">=></span><span style="color:rgb(0,70,156)">{(t) => Math.cos(t * .053 + Math.sin(t * .066) + 1)}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">sin4</span><span style="color:rgb(128,0,128)">=></span><span style="color:rgb(0,70,156)">{(t) => Math.sin(t * .053 + Math.sin(t * .066) + 1)}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">/</span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;vertex </span><span style="color:rgb(144,64,0)">id</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">6</span><span style="color:rgb(128,0,128)">&quot;</span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;area </span><span style="color:rgb(144,64,0)">id</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">7</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">rangeX</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{[-π/2, π/2]}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">rangeY</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{[0, τ]}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">width</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{129}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">height</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{65}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">expr</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{(emit, θ, ϕ, i, j) => {
        q1.set(0, 0, Math.sin(θ), Math.cos(θ));
        q2.set(0, Math.sin(ϕ), 0, Math.cos(ϕ));
        q1.multiply(q2);
        emit(q1.x, q1.y, q1.z, q1.w);
      }}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">live</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{false}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">channels</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{4}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">/</span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;line </span><span style="color:rgb(144,64,0)">id</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">8</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">color</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(32,128,240)">#3090FF</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">/</span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;area </span><span style="color:rgb(144,64,0)">id</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">9</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">rangeX</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{[-π/2, π/2]}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">rangeY</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{[0, τ]}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">width</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{129}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">height</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{65}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">expr</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{(emit, θ, ϕ, i, j) => {
        q1.set(0, Math.sin(θ), 0, Math.cos(θ));
        q2.set(Math.sin(ϕ), 0, 0, Math.cos(ϕ));
        q1.multiply(q2);
        emit(q1.x, q1.y, q1.z, q1.w);
      }}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">live</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{false}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">channels</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{4}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">/</span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;line </span><span style="color:rgb(144,64,0)">id</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">10</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">color</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(32,140,0)">#20A000</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">/</span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;area </span><span style="color:rgb(144,64,0)">id</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">11</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">rangeX</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{[-π/2, π/2]}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">rangeY</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{[0, τ]}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">width</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{129}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">height</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{65}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">expr</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{(emit, θ, ϕ, i, j) => {
        q1.set(Math.sin(θ), 0, 0, Math.cos(θ));
        q2.set(0, 0, Math.sin(ϕ), Math.cos(ϕ));
        q1.multiply(q2);
        emit(q1.x, q1.y, q1.z, q1.w);
      }}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">live</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{false}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">channels</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{4}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">/</span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;line </span><span style="color:rgb(144,64,0)">id</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">12</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">color</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(203,32,0)">#DF2000</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">/</span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;/vertex</span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;/stereographic4</span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;/clock</span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
</span><span style="color:rgb(128,0,128)">&lt;/root</span><span style="color:rgb(128,0,128)">></span><span style="color:inherit"></span></code></pre>

</div></div>

<div class="g8 i2"><div class="pad">

<p>Phew. That's how you make a 4D diagram with Hopf fibration as far as the eye can see. Except it's not actually JSX, that's just me and my pretty-printer pretending.</p>

<h2>Geometry Streaming</h2>

<p>The key is the data itself. It's an array of points mostly, but how that data is laid out and interpreted determines how useful it can be.</p>

<p>Most basic primitives come in fixed size chunks. Particles are single points, lines have two points, triangles have three points. Polygons and polylines have N points. So it made sense to have a <em>tuple of N points</em> be the basic logical unit. You can think in logical pieces of geometry, rather than raw points or individual triangles, unlike GL.</p>

<p>Each primitive maps over data in a standard way. Feed an <code>array</code> of points to a <code>line</code>, you get a polyline. Feed a <code>matrix</code> of points to a <code>surface</code> and you get a grid mesh. Simple. But feed a <code>voxel</code> to a <code>vector</code>, and you get a <em>3D vector field</em>. The general idea is that drawing 1 of something should be as easy as drawing 100×100×100.</p>

</div></div>

<div class="wide full">

  <div class="iframe c">
    <iframe src="/files/mathbox2/iframe-lineup.html?22e389c1" class="mathbox autosize janky" height="320"></iframe>
  </div>

</div>

<div class="c"></div>

<div class="g8 i2 m1"><div class="pad">

<p>This is particularly useful for custom data expressions, which stream in live or procedural data. They now receive an <code>emit(x, y, z, w)</code> function, for emitting a 4-vector like XYZW or RGBA. This is little more than an inlineable call to fill a <code>floatArray[i++] = x</code>, quite a lot faster than returning an array or object.</p>

</div></div>

<div class="c"></div>

<div class="g8 m2"><div class="pad">

  <div class="iframe c">
    <iframe src="/files/mathbox2/iframe-vector.html?22e389c1" class="mathbox janky" height="250" style="background: #fff; width: 100%;"></iframe>
  </div>

<pre><code class="language-javascript">mathbox
  .interval({
    expr: function (emit, x, i, t) {
      y = Math.sin(x + t);
      emit(x,  y);
      emit(x, -y);
    },
    width:   64,
    items:    2,
    channels: 2,
  })
  .vector({
    color: 0x3090FF,
    width: 3,
    start: true,
  });
</code></pre>

<div class="c"></div>

<p class="tc"><em>Emitting 64 2D vectors on an interval, 2 points each.</em></p>

<p>More importantly it lets you emit N points in one iteration, which makes the JS expressions themselves feel like geometry shaders. The result feeds into one or more styled drawing ops. The number of emit calls has to be constant, but you can always knock out or mask the excess geometry.</p>

</div></div>

<div class="g4 m3"><div class="pad">

  <pre><code class="language-javascript">emit = switch channels
  when 1 then (x) ->
    array[i++] = x
    ++j
    return

  when 2 then (x, y) ->
    array[i++] = x
    array[i++] = y
    ++j
    return

  when 3 then (x, y, z) ->
    array[i++] = x
    array[i++] = y
    array[i++] = z
    ++j
    return

  when 4 then (x, y, z, w) ->
    array[i++] = x
    array[i++] = y
    array[i++] = z
    array[i++] = w
    ++j
    return</code></pre>

  <div class="c"></div>
  
  <p><em>Both the <code>expr</code>ession and <code>emit</code>ter will be inlined into the stream's iteration loop.</em></p>

</div></div>

<div class="g12"><div class="pad">

  <pre><code class="language-javascript">consume = switch channels
  when 1 then (emit) ->
    emit array[i++]
    ++j
    return

  when 2 then (emit) ->
    emit array[i++], array[i++]
    ++j
    return

  when 3 then (emit) ->
    emit array[i++], array[i++], array[i++]
    ++j
    return

  when 4 then (emit) ->
    emit array[i++], array[i++], array[i++], array[i++]
    ++j
    return</code></pre>

  <div class="c"></div>
    
  <p class="tc m1"><em>Closures of Hanoi</em></p>

</div></div>

<div class="g8 i2"><div class="pad">

<h2>(4-in-1)²</h2>

<p>GPUs can operate on 4×1 vectors and 4×4 matrices, so working with 4D values is natural. Values can also be referenced by 4D indices. With one dimension reserved for the tuples, that leaves us 3 dimensions XYZ. Hence MathBox arrays are 3+1D. This is for <em>width</em>, <em>height</em>, <em>depth</em>, while the tuple dimension is called <em>items</em>. It does what it says on the tin, creating 1D W, 2D W×H and 3D W×H×D arrays of tuples. Each tuple is made of N vectors of up to 4 channels each.</p>

<p>Thanks to cyclic buffers and partial updates, history also comes baked in. You can use a spare dimension as a free time axis, retaining samples on the go. You can <code>.set('history', N)</code> to record a short log of a whole array over time, indefinitely.</p>

<p>All of this is modular: a data source is something that can be sampled by a 4D pointer from GLSL. Underneath, arrays end up packed into a regular 2D float texture, with "items × width" horizontally and "height × depth" vertically. Each 'pixel' holds a 1/2/3/4D point.</p>

<p>Mapping a 4D 'pointer' to the real 2D UV coordinates is just arithmetic, and so are operators like <code>transpose</code> and <code>repeat</code>. You just swap the XY indices and tell everyone downstream that it's now <em>this</em> big instead. They can't tell the difference.</p>

<p>You can create giant procedural arrays this way, including across rectangular texture size limits, as none of them actually exist except as transient values deep inside a GPU core. Until you materialize them by rendering to a texture using the <code>memo</code> primitive. Add in operators like interpolation and convolution and it's a pretty neat real-time finishing kit for data.</p>

</div></div>

<div class="wide full">

  <div class="iframe c">
    <iframe src="/files/mathbox2/iframe-volume.html?22e389c1" class="mathbox autosize janky" height="320"></iframe>
  </div>

</div>

<div class="c"></div>

<div class="g10 i1 m4"><div class="pad">

<p><img src="/files/mathbox2/too-many-webgls.png" alt="Too many WebGL contexts" /></p>

</div></div>

<div class="g8 i2"><div class="pad">

<p><em><big><a href="/blog/mathbox2-pt2/">Continued in Part 2.</a></big></em></p>

</div></div>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[A DOM for Robots]]></title>
    <link href="http://acko.net/blog/a-dom-for-robots/"/>
    <updated>2015-09-27T00:00:00+02:00</updated>
    <id>http://acko.net/blog/a-dom-for-robots</id>
    <content type="html"><![CDATA[<script src="/files/katex/katex.min.js"></script>
<script src="/files/katex/contrib/auto-render.min.js"></script>
<link rel="stylesheet" type="text/css" href="/files/katex/katex.min.css" />

<div class="g8 i2 first"><div class="pad">

<h2 class="sub">Modelling Live Data</h2>

<p>I want to render live 3D graphics based on a declarative data model. That means a choice of shapes and transforms, as well as data sources and formats. I also want to combine them and make live changes. Which sounds kind of <abbr title="Document Object Model">DOMmy</abbr>.</p>

</div></div>

<div class="g8 i2 first"><div class="pad">
  <a href="http://threejs.org/editor/"><img src="/files/mathbox2/three.js-editor.png" alt="Three.js Editor" title="Three.js Editor"></a>
</div></div>

<div class="g8 i2"><div class="pad">

<p>3D engines don't have <em>Document Object Models</em> though, they have <em>scene graphs</em> and <em>render trees</em>: minimal data structures optimized for rendering output. In Three.js, each tree node is a JS object with properties and children like itself. Composition only exists in a limited form, with a parent's matrix and visibility combining with that of its children. There is no fancy data binding: the renderer loops over the visible tree leaves every frame, passing in values directly to GL calls. Any geometry is uploaded once to GPU memory and cached. If you put in new parameters or data, it will be used to produce the next frame automatically, aside from a <em>needsUpdate</em> bit here and there for performance reasons.</p>

<p>So Three.js is a thin retained mode layer on top of an immediate mode API. It makes it trivial to draw the same thing over and over again in various configurations. That won't do, I want to draw <em>dynamic</em> things with the same ease. I need a richer model, which means wrapping another retained mode layer around it. That could mean observables, data binding, tree diffing, immutable data, and all the other fun stuff nobody can agree on.</p>

<p>However I mostly feed data <em>in</em> and many parameters will end up as shader properties. These are passed to Three as a dictionary of <code>{ type: '…', value: x }</code> objects, each holding a single parameter. Any code that holds a reference to the dictionary will see the same value, as such it acts as a <em>register</em>: you can share it, transparently binding one value to N shaders. This way a single <code>.set('color', 'blue')</code> call on the fringes can instantly affect data structures deep inside the <code>WebGLRenderer</code>, without actually cascading through.</p>

</div></div>

<div class="wide full">

  <div class="iframe c">
    <iframe src="/files/mathbox2/iframe-vertex.html?22e389c1" class="mathbox autosize janky" height="320"></iframe>
  </div>

</div>

<div class="g8 i2"><div class="pad">
  <img src="/files/mathbox2/mathbox-scene.png" alt="MathBox Three Scene Object" title="MathBox Three Scene">
</div></div>

<div class="g8 i2"><div class="pad">

<p>I applied this to build a view tree which retains this property, storing all attributes as shareable registers. The Three.js scene graph is reduced to a single layer of <code>THREE.Mesh</code> objects, flattening the hierarchy. Rather than clumsy CSS3D divs which encode matrices as strings, there's binary arrays, GLSL shaders, and highly optimizable JS lambdas.</p>

<p>As long as you don't go overboard with the numbers, it runs fine even on mobile.</p>

</div></div>

<div class="g12 first"><div class="pad">
  <!--<img src="/files/mathbox2/mathbox-dom-vertex.png" alt="MathBox 2 DOM" title="MathBox 2 DOM">-->

<pre><code class="language-jsx wrap small"><span></span><span style="color:rgb(128,0,128)">&lt;root </span><span style="color:rgb(144,64,0)">id</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">1</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">scale</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{600}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">focus</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{3}</span><span style="color:rgb(128,0,128)"></span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;camera </span><span style="color:rgb(144,64,0)">id</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">2</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">proxy</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{true}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">position</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{[0, 0, 3]}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">/</span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;shader </span><span style="color:rgb(144,64,0)">id</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">3</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">code</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">
uniform float time;
uniform float intensity;

vec4 warpVertex(vec4 xyzw, inout vec4 stpq) {
  xyzw +=   0.2 * intensity * (sin(xyzw.yzwx * 1.91 + time + sin(xyzw.wxyz * 1.74 + time)));
  xyzw +=   0.1 * intensity * (sin(xyzw.yzwx * 4.03 + time + sin(xyzw.wxyz * 2.74 + time)));
  xyzw +=  0.05 * intensity * (sin(xyzw.yzwx * 8.39 + time + sin(xyzw.wxyz * 4.18 + time)));
  xyzw += 0.025 * intensity * (sin(xyzw.yzwx * 15.1 + time + sin(xyzw.wxyz * 9.18 + time)));

  return xyzw;
}</span><span style="color:rgb(128,0,128)">&quot;
 </span><span style="color:rgb(144,64,0)">time</span><span style="color:rgb(128,0,128)">=></span><span style="color:rgb(0,70,156)">{(t) => t / 4}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">intensity</span><span style="color:rgb(128,0,128)">=></span><span style="color:rgb(0,70,156)">{(t) => {
        t = t / 4;
        intensity = .5 + .5 * Math.cos(t / 3);
        intensity = 1.0 - Math.pow(intensity, 4);
        return intensity * 2.5;
      }}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">/</span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;reveal </span><span style="color:rgb(144,64,0)">id</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">4</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">stagger</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{[10, 0, 0, 0]}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">enter</span><span style="color:rgb(128,0,128)">=></span><span style="color:rgb(0,70,156)">{(t) => 1.0 - Math.pow(1.0 - Math.min(1,  (1 + pingpong(t))*2), 2)}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">exit</span><span style="color:rgb(128,0,128)">=></span><span style="color:rgb(0,70,156)">{(t) => 1.0 - Math.pow(1.0 - Math.min(1,  (1 - pingpong(t))*2), 2)}</span><span style="color:rgb(128,0,128)"></span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;vertex </span><span style="color:rgb(144,64,0)">id</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">5</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">pass</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">view</span><span style="color:rgb(128,0,128)">&quot;</span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;polar </span><span style="color:rgb(144,64,0)">id</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">6</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">bend</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{1/4}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">range</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{[[-π, π], [0, 1], [-1, 1]]}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">scale</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{[2, 1, 1]}</span><span style="color:rgb(128,0,128)"></span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;transform </span><span style="color:rgb(144,64,0)">id</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">7</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">position</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{[0, 1/2, 0]}</span><span style="color:rgb(128,0,128)"></span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;axis </span><span style="color:rgb(144,64,0)">id</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">8</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">detail</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{512}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">/</span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;scale </span><span style="color:rgb(144,64,0)">id</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">9</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">divide</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{10}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">unit</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{π}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">base</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{2}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">/</span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;ticks </span><span style="color:rgb(144,64,0)">id</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">10</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">width</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{3}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">classes</span><span style="color:rgb(128,0,128)">=[&quot;</span><span style="color:rgb(0,0,192)">foo</span><span style="color:rgb(128,0,128)">&quot;, </span><span style="color:rgb(144,64,0)">&quot;</span><span style="color:rgb(0,0,192)">bar</span><span style="color:rgb(128,0,128)">&quot;] </span><span style="color:rgb(144,64,0)">/</span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;scale </span><span style="color:rgb(144,64,0)">id</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">11</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">divide</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{5}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">unit</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{π}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">base</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{2}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">/</span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;format </span><span style="color:rgb(144,64,0)">id</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">12</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">expr</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{(x) => {
        return x ? (x / π).toPrecision(2) + '</span><span style="color:rgb(144,64,0)">π</span><span style="color:rgb(0,70,156)">' : 0
      }}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">/</span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;label </span><span style="color:rgb(144,64,0)">id</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">13</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">depth</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{1/2}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">zIndex</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{1}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">/</span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;/transform</span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;axis </span><span style="color:rgb(144,64,0)">id</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">14</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">axis</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{2}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">detail</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{128}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">crossed</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{true}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">/</span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;transform </span><span style="color:rgb(144,64,0)">id</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">15</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">position</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{[π/2, 0, 0]}</span><span style="color:rgb(128,0,128)"></span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;axis </span><span style="color:rgb(144,64,0)">id</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">16</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">axis</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{2}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">detail</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{128}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">crossed</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{true}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">/</span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;/transform</span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;transform </span><span style="color:rgb(144,64,0)">id</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">17</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">position</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{[-π/2, 0, 0]}</span><span style="color:rgb(128,0,128)"></span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;axis </span><span style="color:rgb(144,64,0)">id</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">18</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">axis</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{2}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">detail</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{128}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">crossed</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{true}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">/</span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;/transform</span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;grid </span><span style="color:rgb(144,64,0)">id</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">19</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">divideX</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{40}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">detailX</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{512}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">divideY</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{20}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">detailY</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{128}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">width</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{1}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">opacity</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{1/2}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">unitX</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{π}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">baseX</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{2}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">zBias</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{-5}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">/</span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;interval </span><span style="color:rgb(144,64,0)">id</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">20</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">width</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{512}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">expr</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{(emit, x, i, t) => {
        emit(x, .5 + .25 * Math.sin(x + t) + .25 * Math.sin(x * 1.91 + t * 1.81));
      }}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">channels</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{2}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">/</span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;line </span><span style="color:rgb(144,64,0)">id</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">21</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">width</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{5}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">/</span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;play </span><span style="color:rgb(144,64,0)">id</span><span style="color:rgb(128,0,128)">=&quot;</span><span style="color:rgb(0,0,192)">22</span><span style="color:rgb(128,0,128)">&quot; </span><span style="color:rgb(144,64,0)">pace</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{10}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">loop</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{true}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">to</span><span style="color:rgb(128,0,128)">=</span><span style="color:rgb(0,70,156)">{3}</span><span style="color:rgb(128,0,128)"> </span><span style="color:rgb(144,64,0)">script</span><span style="color:rgb(128,0,128)">=[[</span><span style="color:rgb(0,70,156)">{color: &quot;</span><span style="color:rgb(144,64,0)">rgb(48, 144, 255)</span><span style="color:rgb(0,70,156)">&quot;}</span><span style="color:rgb(128,0,128)">], </span><span style="color:rgb(144,64,0)">[</span><span style="color:rgb(0,70,156)">{color: &quot;</span><span style="color:rgb(144,64,0)">rgb(100, 180, 60)</span><span style="color:rgb(0,70,156)">&quot;}</span><span style="color:rgb(128,0,128)">], </span><span style="color:rgb(144,64,0)">[</span><span style="color:rgb(0,70,156)">{color: &quot;</span><span style="color:rgb(144,64,0)">rgb(240, 20, 40)</span><span style="color:rgb(0,70,156)">&quot;}</span><span style="color:rgb(128,0,128)">], </span><span style="color:rgb(144,64,0)">[</span><span style="color:rgb(0,70,156)">{color: &quot;</span><span style="color:rgb(144,64,0)">rgb(48, 144, 255)</span><span style="color:rgb(0,70,156)">&quot;}</span><span style="color:rgb(128,0,128)">]] </span><span style="color:rgb(144,64,0)">/</span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;/polar</span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;/vertex</span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
 </span><span style="color:rgb(144,64,0)"> </span><span style="color:rgb(144,64,0)"></span><span style="color:rgb(128,0,128)">&lt;/reveal</span><span style="color:rgb(128,0,128)">></span><span style="color:inherit">
</span><span style="color:rgb(128,0,128)">&lt;/root</span><span style="color:rgb(128,0,128)">></span><span style="color:inherit"></span></code></pre>  
  
  <div class="c"></div>
    
  <p><em>Note: The JSX is a lie, you define nodes in pure JS.</em></p>
</div></div>


<div class="g8 i2"><div class="pad">

<h2>Keep it Simple</h2>

<p>From afar there's a tree of nodes, similar to SVG tags. This is the MathBox library of vector primitives. The basic shapes are all there: points, lines, faces, vectors, surfaces, etc. These nodes are placed inside a shallow hierarchy of views and transforms.</p>

<p>However none of the shapes draw anything by themselves. They only know how to draw <em>data</em> supplied by a linked source. Data can be an array (static or live), a procedural source, custom JS / GLSL code, etc. This is further augmented by data operators which can be sandwiched between source and shape, forming automatic pipelines between siblings.</p>

<p>The current set of components looks like this:</p>

</div></div>

<div class="c"></div>

<div class="g12 m1"><div class="pad">

<div class="columns-4">

  <div class="g3 section">
  <h3>Base</h3>
  <ul>
    <li>Group</li>
    <li>Inherit</li>
    <li>Root</li>
    <li>Unit</li>
  </ul>

  <h3>Camera</h3>
  <ul>
    <li>Camera</li>
  </ul>
  </div>

  <div class="g3 section">
  <h3>Draw</h3>
  <ul>
    <li>Axis</li>
    <li>Face</li>
    <li>Grid</li>
    <li>Line</li>
    <li>Point</li>
    <li>Strip</li>
    <li>Surface</li>
    <li>Ticks</li>
    <li>Vector</li>
  </ul>
  </div>

  <div class="g3 section">
  <h3>Data</h3>
  <ul>
    <li>Area</li>
    <li>Array</li>
    <li>Interval</li>
    <li>Matrix</li>
    <li>Scale</li>
    <li>Volume</li>
    <li>Voxel</li>
  </ul>
  </div>

  <div class="g3 section">
  <h3>Operator</h3>
  <ul>
    <li>Grow</li>
    <li>Join</li>
    <li>Lerp</li>
    <li>Memo</li>
    <li>Resample</li>
    <li>Repeat</li>
    <li>Slice</li>
    <li>Split</li>
    <li>Spread</li>
    <li>Swizzle</li>
    <li>Transpose</li>
  </ul>
  </div>

  <div class="c"></div>

  <div class="g3 section">
  <h3>Overlay</h3>
  <ul>
    <li>DOM</li>
    <li>HTML</li>
  </ul>

  <h3>Present</h3>
  <ul>
    <li>Move</li>
    <li>Play</li>
    <li>Present</li>
    <li>Reveal</li>
    <li>Slide</li>
    <li>Step</li>
  </ul>
  </div>

  <div class="g3 section">
  <h3>RTT</h3>
  <ul>
    <li>Compose</li>
    <li>RTT</li>
  </ul>

  <h3>Shader</h3>
  <ul>
    <li>Shader</li>
  </ul>
  </div>

  <div class="g3 section">
  <h3>Text</h3>
  <ul>
    <li>Format</li>
    <li>Label</li>
    <li>Text</li>
    <li>Retext</li>
  </ul>

  <h3>Time</h3>
  <ul>
    <li>Clock</li>
    <li>Now</li>
  </ul>
  </div>

  <div class="g3 section">
  <h3>Transform</h3>
  <ul>
    <li>Fragment</li>
    <li>Layer</li>
    <li>Transform</li>
    <li>Transform4</li>
    <li>Vertex</li>
  </ul>
  </div>

  <div class="g3 section">
  <h3>View</h3>
  <ul>
    <li>Cartesian</li>
    <li>Cartesian4</li>
    <li>Polar</li>
    <li>Spherical</li>
    <li>Stereographic</li>
    <li>Stereographic4</li>
    <li>View</li>
  </ul>
  </div>

</div>

</div></div>

<div class="c"></div>

<div class="g8 i2 m1"><div class="pad">

<p>To make you feel at home, nodes have an <code>id</code> and <code>classes</code>, and you can use CSS selectors to identify them. Nodes link up with preceding siblings and parents by default, but you can select any node in the tree. This allows for arbitrary graphs, including feedback loops. However all of this is optional: you can also pass in direct node objects or MathBox's own jQuery-like selections. What it doesn't have is a notion of detached document fragments: nodes are immediately inserted on creation.</p>

<p>A node's attributes can be <code>.get()</code> and <code>.set()</code>, though there is also a read-only <code>.props</code> dictionary for fashionable reasons. The values are strongly typed as Three.js colors, vectors, matrices, … but accept e.g. CSS colors and ordinary arrays too. The values are normalized for immediate use, the original values are preserved on the side for printing and serialization.</p>

<p><img src="/files/mathbox2/api.png" alt="MathBox Node API" /></p>

<p>What's unique is the emphasis on time. First, properties can be directly bound to time-dependent expressions, on creation or afterwards. Second, clocks are primitives on their own. This allows for nested timelines, on-demand bullet time, fast forwards and more. It even supports limited time travel, evaluating an expression several frames in the past. This can be used to ensure consistent 60 fps data logging through janky updates, useful for all sorts of things. It's exposed publicly as <code>.bind(key, expr)</code> and <code>.evaluate(key, time)</code> per node. It's also dogfood for declarative animation tracks. The primitives <code>clock</code>/<code>now</code> provide timing, while <code>step</code> and <code>play</code> handle keyframes on tracks.</p>

<p>This is definitely <em>a</em> DOM, but it has only basic features in common with <em>the</em> HTML DOM and does much less. Most of the magic comes from the components themselves. There's no cascade of styles to inherit. Children compose <em>with</em> a parent, they do not inherit <em>from</em> it, only caring about their own attributes. The namespace is clean, with no weird combo styles à la CSS. As much as possible, attributes are unique orthogonal knobs you can turn freely.</p>

<h2>Model-View-Projection</h2>

<p>On the inside I separate the generic data model from the type-specific View Controller attached to it. The controller's job is to create and manage Three.js objects to display the node (if any). Because a data source and a visible shape have very little in common, the nodes and their controllers are blank slates built and organized around named <em>traits</em>. Each trait is a data mix-in, with associated attributes and helpers for common behavior. Primitives with the same traits can be expected to work the same, as their public facing models are identical.</p>

<p>Controllers can traverse the graph to find each other by matching traits, listening for events and making calls in response. This way only specific <em>events</em> will cascade through cause and effect, often skipping large parts of the hierarchy. The only way to do a "global style recalculation" would be to send a forced change event to every single controller, and there's never a reason to do so.</p>

<p>The controller lifecycle is deliberately kept simple: <code>make()</code>, <code>made()</code>, <code>change(…)</code>, <code>unmake()</code>, <code>unmade()</code>. When a model changes, its controller either updates in place, or rebuilds itself, doing an unmake/make cycle. The change handler is invoked on creation as well, to encourage stateless updates. It affords live editing of anything, without having to micro-optimize every possible change scenario. Controllers can also watch bound selectors, retargeting if their matched set changes. This lets primitives link up with elements that have yet to be inserted.</p>

<p>Unlike HTML, the DOM is not forced to contain a render tree as well. Only some of the leaf nodes have styles and create renderables. Siblings and parents are called upon to help, but the effects don't have to be strictly hierarchical. For example, a visual effect can wrap a single leaf but still be applied after all its parents, as transformations are collected and composed in passes.</p>


<h2>It'll Do</h2>

<p>The result is not so much a <em>document model</em> as it is a <em>computational model</em> inside a <em>presentational model</em>. You can feed it finalized data and draw it directly… or you can build new models within it and reproject them live. Memoization enables feedback and meta-visualization. The line between data viz and demo scene is rarely this blurry.</p>

<p>Here, the notion of a <em>computed style</em> has little meaning. Any value will end up being transformed and processed in arbitrary ways down the pipe. As I've <a href="/blog/shadow-dom">tried to explain before</a>, the kinds of things people do with <code>getComputedStyle()</code> and <code>getClientBoundingRect()</code> are better achieved by having an extensible layout model, one that affords custom constraints and composition on an equal footing. To do otherwise is to admit defeat and embrace a leaky abstraction by design.</p>

<p>The shallow hierarchy with composition between siblings is particularly appealing to me, even if I realize it introduces non-traditional semantics more reminiscent of a command-line. It acts as both a jQuery-style chainable API, and a minimal document model. If it offends your sensibilities, you could always defuse the magic by explicitly wiring up every relationship. In case of confusion, <code>.inspect()</code> will log syntax highlighted JSX, while <code>.debug()</code> will draw the underlying shader graphs.</p>

<p>I've defined a good set of basic primitives and iterated on them a few times. But how to implement it, when WebGL doesn't even fully cover OpenGL ES 2?</p>


<ul>
  <li><a href="/blog/mathbox2/">MathBox² - PowerPoint Must Die</a></li>
  <li><em>A DOM for Robots - Modelling Live Data</em></li>
  <li><a href="/blog/yak-shading/">Yak Shading - Data Driven Geometry</a></li>
  <li><a href="/blog/shadergraph-2/">ShaderGraph 2 - Functional GLSL</a></li>
</ul>

</div></div>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[The Cargo Cult of Game Mechanics]]></title>
    <link href="http://acko.net/blog/the-cargo-cult-of-game-mechanics/"/>
    <updated>2014-09-21T00:00:00+02:00</updated>
    <id>http://acko.net/blog/the-cargo-cult-of-game-mechanics</id>
    <content type="html"><![CDATA[<div class="g8 i2 first"><div class="pad">

<h2 class="sub">Form without Function</h2>

<p>There's been a lot of fuss about gaming and gaming culture lately, in particular the nature of gaming journalism. Don't worry, I'm <em>so</em> not sticking my face into that particular beehive. However, I do agree the conversation around gaming is crap, so instead I'm posting the kind of opinion piece I wish I'd see on credible gaming sites, as someone who actually knows how the sausage is made.</p>

</div></div>

<div class="c"></div>

<div class="wide mt2">
    <a target="_blank" href="http://dear-esther.com"><img src="https://acko.net/files/cargocult/dear-esther.jpg" alt="Dear Esther"></a>
</div>
<div class="g8 i2 mb0">
    <a target="_blank" class="credit" href="http://dear-esther.com">Dear Esther</a>
</div>

<div class="g8 i2 mt1"><div class="pad">

<h2>But is it Art?</h2>

<p>Gamers like to talk—or argue—about graphics, frame rates, physics, hours of play time, item variety, models, textures, <a target="_blank" href="https://www.youtube.com/watch?v=rTYpKlIc_fg">downloadable content and microtransactions</a>, and so on. There is a reason the <em>Glorious PC Master Race</em> and the <em>Console Wars</em> are memes. If games are art, if it's a grown up medium, why do we fuss about trivialities so much? You don't debate high literature by critiquing the paper stock or chapter length.</p>

<p>Well because production values are important for immersion. Details and performance really matter. However when we treat games just as mechanical live pictures, we're missing the point entirely. It's confusing form with function. In The Dark Knight, Heath Ledger's Joker should look the part, but he'll be 10x scarier and more interesting once you understand how he operates and thinks. This seems obvious in film, yet not in gaming.</p>

<p>Even "artistic games" like <a target="_blank" href="http://dear-esther.com">Dear Esther</a> are often criticized for superficial mechanics (or lack thereof), not for what they set out to do. The question isn't whether Dear Esther is just a walking simulator. It's whether it's anywhere near as engaging as walking around a real place, like a park or a museum. If it fails, it's not because there aren't any puzzles. The Anne Frank House in Amsterdam does not require puzzles. It does have a secret passage but the only achievement you get for finding it is sadness.</p>

<p>…</p>

<p>Yup, that awkward pause is where the "gaming as a serious medium" debate usually hangs, and it leaves the conversation severely deadlocked. Trying to add gamified elements for the heck of it, to make a gamier game, rings hollow and does not get us any closer to credibility.</p>

</div></div>

<div class="c"></div>

<div class="wide mt2">
    <a target="_blank" href="http://en.wikipedia.org/wiki/Heavy_Rain"><img src="https://acko.net/files/cargocult/heavy-rain.jpg" alt="Heavy Rain"></a>
</div>
<div class="g8 i2 mb0">
    <a target="_blank" class="credit" href="http://en.wikipedia.org/wiki/Heavy_Rain">Heavy Rain</a>
</div>

<div class="g8 i2 mt2"><div class="pad">

<p>The popular alternative is to simply adopt the current forms of Serious Media. To make a game more like a movie or a book, whether blockbuster <em>or</em> arthouse. It generally involves taking away choice, using scripts instead of simulations, with mini-games and quick-time events thrown in to amuse your hindbrain. It's tacitly saying that real storytelling, real human comedy or tragedy, can't happen while a player is in control. It's non-sense of course, plenty of games have done so before.</p>

<p>Somehow though we've forgotten how to do it, and I don't think I'm alone in thinking this. This existential crisis was perfectly embodied in indie gem <a href="http://www.stanleyparable.com">The Stanley Parable</a>, a post-modern tale of choice. It's a game about playing a game, constantly breaking the fourth wall. There's recursive gags, self-parodying achievements, 'victory' conditions that require you to quit the game, and other surgical strikes at typical gaming habits. It garnered critical praise from gamers and journalists alike, playing like a love-hate letter to its audience: at times cooperative and happy, other times sardonic and sadistic.</p>

</div></div>

<div class="c"></div>

<div class="wide mt2">
    <a target="_blank" href="http://stanleyparable.com"><img src="https://acko.net/files/cargocult/stanley-parable.jpg" alt="The Stanley Parable"></a>
</div>
<div class="g8 i2 mb0">
    <a target="_blank" class="credit" href="http://stanleyparable.com">The Stanley Parable</a>
</div>

<div class="g8 i2 mt1"><div class="pad">

<p>I'm pretty sure The Stanley Parable is Art. There's just one thing bothering me. It doesn't actually offer you any choice. The game is an admission of defeat.</p>

<p>Choice is of course a tricky concept, that was the whole point, so let me be more specific. You could feasibly make a 100% Let's Play of Stanley Parable, covering all the branching paths, and turn it into a sort of <a target="_blank" href="https://en.wikipedia.org/wiki/Dragon%27s_Lair">Dragon's Lair</a> on Laserdisc. It would lose little in translation, most of the gags would still work. It's not a game about <em>your</em> choices, it's still just about watching theirs.</p>


<h2 class="mt3">Live in Your World, Play in Ours</h2>

<p>If you're looking for someone to blame (you know, in general), it's easy to point to the incestuous industry. Games are big business and cost a ton to produce. The primary purpose of talking about games is to sell things to gamers, in a market that moves very fast, saturated with product. Hence brands and franchises compete over the attention of customers, preferably through lock-in. It goes beyond ordinary sales, and includes pre-orders, season passes, virtual marketplaces and other monetary aids. Be sure to use a condom.</p>

<p>For several years now though, there has been a counterpoint: the wave of DRM-free indies, Humble Bundles and the wild success of Kickstarter. Notably, industry veterans Tim Schafer and Brian Fargo, known for beloved classics like Monkey Island and Wasteland, each held out their hats and promised to bring back the glory days of old. Gamers rewarded them in spades. Budgets ballooned from a few hundred thousand to several million, spawning further spinoffs. Chris Roberts of Wing Commander fame did even better. He kickstarted Star Citizen to the tune of a few million, but continues to raise funds today with virtual goods and perks for the future game. It now exceeds $50 million in backer funding.</p>

</div></div>

<div class="c"></div>

<div class="wide mt2">
    <img src="https://acko.net/files/cargocult/pre-order.jpg" alt="Typical game ad">
</div>
<div class="g8 i2 mb0">
    <a class="credit">Destiny vs Star Citizen</a>
</div>

<div class="g8 i2 mt2"><div class="pad">

<p>If I were cynical, which I am, I would say a bunch of people have spent hundreds of dollars each on virtual assets with no guarantee they'll ever work as promised. This is the power of nostalgia mixed with in-engine mockups, and it's clearly very good business. Don't get me wrong, I've funded a few games on Kickstarter too, <em>below</em> retail. But what comes out of these projects is raising some eyebrows, with hype, delays and cancellations galore. I think it points to a deeper issue altogether, driven by games but not limited to gaming.</p>

<p>On the surface these developers are giving their fans exactly what they want. Something they already love, modernized and expanded, with early access and feedback. You cannot fault the creators for this. Rather, I think the problem is that gaming fans don't know what they want. It's a <em>know it when I see it</em> kind of affair. So they just ask for more of the same instead, again confusing form with function.</p>

<p>There's an elephant in the room. Everybody does it to some degree, but it's somehow shameful.</p>

<p>Compulsion.</p>

<p>It's even more obvious when you consider that the easy money in gaming isn't actually to bankroll a $200 million console blockbuster, half of which is probably marketing. Rather, it's to put a carefully tuned slot machine under the noses of as many people as possible, like say, a <a target="_blank" href="http://venturebeat.com/2014/02/21/report-finds-free-to-play-microtransactions-make-up-79-of-u-s-app-store-revenues/">free-to-play smartphone game</a>. With lots of push notifications and time locks, using fictional hooks to create personal investment and a sense of false scarcity. People pull their phones out in elevators and on the toilet, multiple times a day. It's guaranteed brain share if you get in, so much easier than convincing everyone to fork out $50 once, let alone monthly.</p>

<p>The real target audience is a small minority of <em>whales</em>—compulsive users—to buy the virtual currency and goods you mint at will. They subsidize the free users, who in turn provide word of mouth on social media. It's gambling and addiction, by any other name, only now people are betting real money against fake money, so it's legal.</p>

</div></div>

<div class="c"></div>

<div class="wide mt2">
    <img src="https://acko.net/files/cargocult/freetoplay.jpg" alt="Free to play">
</div>

<div class="g8 i2 mt2"><div class="pad">
    
<p>Most gamers are familiar with the "one more turn" itch of strategy or puzzle games, the desire to open every chest and read every log, the zombiefied stares at LAN parties. It's a common trope to be obsessive, but gamers are generally self-aware about it. We don't mind wasting time if it's fun, that's the point, and it gives the Youtubers something to do.</p>

<p>But the <a target="_blank" href="http://psychology.wikia.com/wiki/Skinner_Box">Skinner box</a> is still real. Too often we see products that seem to consist mainly of compulsive triggers. Where the developers built a guided theme park ride with only the promise of cake at the end. They set out a generic progression tree and loom a nebulous threat overhead that can only be beaten by a fully armed and operational Level 80 Battlemage. Between you and the end stand a thousand foes and a bunch of fetch/build/shoot/escort quests. Everything will be perfectly scaled to offer the permanent illusion of a challenge you can barely win, and are constantly forced to work for.</p>

<p>I think this kind of game design stems from a fundamental misunderstanding, willful or not, of how games are supposed to work. It's cargo culting the patterns of games and game mechanics, without considering what they're for. Which is the point I'd like to get to.</p>
  
<p>But first, there's still the elephant.</p>

</div></div>

<div class="c"></div>

<div class="wide mt2">
    <a href="https://robertsspaceindustries.com"><img src="https://acko.net/files/cargocult/crowdfunding.jpg" alt="Crowdfunding"></a>
</div>
<div class="g8 i2 mb0">
    <a target="_blank" class="credit" href="https://robertsspaceindustries.com">Star Citizen</a>
</div>

<div class="wide mt1">
    <a href="https://www.kickstarter.com/projects/doublefine/double-fine-adventure"><img src="https://acko.net/files/cargocult/doublefine.jpg" alt="Double Fine Adventure Kickstarter"></a>
</div>
<div class="g8 i2 mb0">
    <a target="_blank" class="credit" href="https://www.kickstarter.com/projects/doublefine/double-fine-adventure">Double Fine Adventure</a>
</div>

<div class="g8 i2 mt2"><div class="pad">

<p>See, the way these shady free-to-play games work... if we're honest, it kinda matches how Kickstarter plays out. Dramatic concept art. A beloved NPC in need. An XP bar to fill. Stretch goals to level up. Massive online multiplayer with social media tie ins, rally your friends. Plus of course, unlimited alpha and beta testing until release, bankrolled by you, with additional paid perks along the way.</p>

<p>With the risk of stating the obvious, but it's more on point than ever: these things are run by game designers, for gamers. No, put away the tin foil hat. I simply want to suggest that what draws people <em>into</em> these projects bears little relation to what comes <em>out</em> at the end, a release which is merely a coda to a multi-year event. That it is no more about game development than Mario is about saving princesses. That maybe Kickstarter is a sequel to Twitter, the world's <a target="_blank" href="https://en.wikipedia.org/wiki/How_Videogames_Changed_the_World">#1 video game</a>.</p>

<p>It shows in the lack of polish and sophistication in the games that do manage a release, which reviewers and fans consistently gloss over or forgive. Yes I'm getting into taste territory here, but let's look at it objectively. Repetitive shoot em ups that merely consist of dice rolls and numbers going up. RPGs with fenced off wax-museum towns. Meticulously painted backdrops that belie the lack of depth. Or alternatively, pixel art and chiptunes.</p>

</div></div>

<div class="c"></div>

<div class="wide mt2">
    <img src="https://acko.net/files/cargocult/indies.jpg" alt="Indies">
</div>
<div class="g8 i2 mb0">
    <a class="credit">Wasteland 2, Shadowrun Returns, Broken Age, Superbrothers: Sword &amp; Sworcery EP</a>
</div>
<div class="g8 i2 mt2"><div class="pad">

<p>On the surface these games have all the trappings of the classic gaming age, remade in widescreen HD or quirky indie glory, but they lack lasting power once you <em>stop</em> playing. Far from evolving the real classics, of which there are admittedly not actually <em>that</em> many, we've regressed and turned them into caricatures of themselves, mistaking technical limitations for a lack of ambition.</p>

<h2 class="mt3">The Carrot and the Stick</h2>

<p>If at this point you think I'm wearing rose-tinted glasses so fabulous I'm farting rainbows, allow me to convince you otherwise. I'm not pretending that classic DOS or NES games with giant clunky controls were the height of interaction design, or that early 3D wasn't butt-ugly in retrospect. Features like hint systems and autosaves are nice. Rather, there's a reason people continue to cite the same few classics.</p>

<p>Fallout, Freespace, Outcast, Master of Orion, Rollercoaster Tycoon, System Shock, Thief and Torment are still high points in gaming, and it isn't because they were/weren't Art, or are/aren't crappy by modern standards.</p>

<p>To this day, each of those games presents an understandable, flexible sandbox. They offer you a world with consistent rules, letting you figure out the mechanics to face the game's challenges your way. You explore environments at your own pace, build at your leisure, and you're driven forward because you want to, not because you have to. Compulsion is a side-effect of existing motivations, which naturally result from actively participating in the game world.</p>

</div></div>

<div class="c"></div>

<div class="wide mt2">
    <img src="https://acko.net/files/cargocult/classics.jpg" alt="Classics">
</div>
<div class="g8 i2 mb0">
    <a class="credit">Fallout, Outcast, Freespace 2, System Shock 2, Thief 2: The Metal Age</a>
</div>

<div class="g8 i2 mt2"><div class="pad">

<p>If I go through an airlock in System Shock 2, it's because <em>I need</em> what's on the other side of it, and I hope to return alive from it. The game presents a choice and then dares me to take it.</p>

<p>If I go through an airlock in Mass Effect 3, it closes permanently because everything looks the same and too many players got turned around in testing. There is never a reason to go back. The game presents a mistaken illusion of freedom and has to clamp down to fix it.</p>

<p>Corridor shooters with random chest high barriers, indestructible plot armor, keys hanging next to locks, breadcrumbed objective markers, one-way quick travel or chutes, rock-paper-scissors busywork, teleporting AI... these are all just symptoms of a broken game world, which needs dramatic patch jobs to make basic gameplay not fall apart. If a level designer locks a door with the Red Key, they're just putting a meaningless fetch quest in your path to keep you busy. If they put two elite guards and an alarm there instead, now you have the opportunity for improvisation and consequence. That can only happen when there's options beyond "Use Shotgun on Face" <em>and</em> you've been given space and time to get confident about&nbsp;it.</p>

<p>Instead, many games are explicitly structured in a linear, inflationary manner. What you do at level 50 is mostly the same as level 5, only now the numbers are 10× larger, and you shoot blue instead of green.</p>

</div></div>

<div class="c"></div>

<div class="wide mt2">
    <a target="_blank" href="http://www.elderscrolls.com/oblivion"><img src="https://acko.net/files/cargocult/oblivion.jpg" alt="Classics"></a>
</div>
<div class="g8 i2 mb0">
    <a target="_blank" href="http://www.elderscrolls.com/oblivion" class="credit">The Elder Scrolls IV: Oblivion</a>
</div>


<div class="g8 i2 mt2"><div class="pad">

<p>The role of game mechanics should not be the oppressive tyrant telling you to fetch and grind and be thankful for your crumbs of XP and DPS as the scenery blazes past. It should be an à-la-carte menu of options which is opened up for your benefit and at your direction. Slow enough that you can get familiar with each element in turn, but fast enough not to frustrate and limit. Unlockables and crafting should be a way to enable new abilities, not just busywork. Level ups should let you specialize in certain tactics, not just keep up with the Joneses who all bought new glass armor and plasma rifles overnight. Compulsion is just a stick, not the carrot.</p>

<p>Ironically I think it's the technical limitations of classic games that often played to their advantage and which modern remakes in particular are screwing up. The spartan graphics served to highlight the mechanics, instead of needing focus rings and prompts. The lack of voices and mocap forced the writing to carry the story. When you can't conjure up massive vistas at will, there's no point in making the player cross giant cities and wastelands. When the entire world is just isometric sprites, it's practical to let the player destroy all of them. For a while there was a really good match between the complexity of the game world and the way it was represented, and I don't think it's a coincidence that this window is where we find many beloved gaming classics.</p>

<p>What might now seem like broken mechanics often had significant effects on gameplay. An amnesiac guard that can't climb ladders has a similar effect as regenerating health: it makes it easier to run away. Except only one of those requires the player to learn their surroundings. Circle strafers had a surprising amount of non-linearity and involved much more acrobatics than FPSes today, and the passive AI of early RTSes acts similar to modern shooter enemies, which don't engage unless <em>you've</em> spotted <em>them</em>.</p>

</div></div>

<div class="c"></div>

<div class="wide mt1">
    <img src="https://acko.net/files/cargocult/classics2.jpg" alt="Classic games">
</div>
<div class="g8 i2 mb0">
    <a class="credit">Crusader: No Regret, Rollercoaster Tycoon, TIE Fighter, Carmageddon</a>
</div>

<div class="g8 i2 mt1"><div class="pad">

<p>Gaming is ultimately about forgetting the rules of reality and adopting a whole new set. Realism doesn't matter, whacky rules can be fun, as long as they're consistent and interact in interesting ways.</p>

<p>For modern games to evolve to match their now deceiving superstar looks, to move beyond progress bar quest and animated puppets with voice boxes, significant advances have to be made. We need real sandbox simulation, autonomous agents and language-capable AI, and it's not as easy to deliver as another sequel or reboot, mainstream or otherwise. It requires building a game that's meant to be played rather than just reacted to.</p>

<p>I just hope enough people remember what actually made the classics work.</p>

</div></div>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Shadow DOM]]></title>
    <link href="http://acko.net/blog/shadow-dom/"/>
    <updated>2014-03-24T00:00:00+01:00</updated>
    <id>http://acko.net/blog/shadow-dom</id>
    <content type="html"><![CDATA[<div class="g8 i2 first"><div class="pad">

<h2 class="sub">SVG, CSS, React and Angular</h2>

</div></div>

<div class="c"></div>

<div class="g8 i2"><div class="pad">

<p>For a while now I've been working on MathBox 2. I want to have an environment where you take a bunch of mathematical legos, bind them to data models, draw them, and modify them interactively at scale. Preferably in a web browser.</p>

<p>Unfortunately HTML is crufty, CSS is annoying and the DOM's unwieldy. Hence we now have libraries like <a href="http://facebook.github.io/react/">React</a>. It creates its own virtual DOM just to be able to manipulate the real one—the Agile Bureaucracy design pattern.</p>

<p>The more we can avoid the DOM, the better. But why? And can we fix it?</p>

</div></div>

<div class="c"></div>

<div class="wide mt2">
  <img src="https://acko.net/files/shadowdom/banner.jpg" alt="Netscape">
</div>

<div class="c"></div>

<aside class="g3 mt2"><div class="pad">
  <p><img src="https://acko.net/files/shadowdom/star.svg" alt="Star" class="flat"></p>
</div></aside>

<div class="g8 mt3"><div class="pad">

<pre><code class="language-html" tabindex="0">&lt;?xml version="1.0" encoding="utf-8"?&gt;
&lt;!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"&gt;
&lt;svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
  width="400px" height="400px" viewBox="0 0 400 400" enable-background="new 0 0 400 400" xml:space="preserve"&gt;
  &lt;polygon fill="#FDBD10" stroke="#BE1E2D" stroke-width="3" stroke-miterlimit="10" points="357.803,105.593 276.508,202.82 
    343.855,310.18 226.266,262.91 144.973,360.139 153.592,233.697 36.002,186.426 158.918,155.551 167.538,29.109 234.885,136.469 "/&gt;
  &lt;polygon fill="#FDEB10" points="326.982,114.932 259.695,195.408 315.441,284.271 218.109,245.146 150.821,325.623 157.955,220.966 
    60.625,181.838 162.364,156.283 169.499,51.625 225.242,140.488 "/&gt;
&lt;/svg&gt;
</code></pre>
<div class="c"></div>

</div></div>

<div class="g8 i1 cl"><div class="pad">
  
<h2>Dangling Nodes</h2>

<p>Take SVG. Each XML tag is a graphical shape or instruction. Like all XML, the data has to be serialized into tags with attributes made of strings. Large data sets turn into long string attributes to be parsed. Large collections of stuff turn into many separate tags to be iterated over. Neither is really desirable.</p>

<p>It only represents basic operations, so all serious prep work has to be done by the user up front. This is what D3 is used for, generating and managing more complex mappings for you.</p>

<p>When you put SVG into HTML, each element becomes a full DOM node. A simple <code>&lt;tag&gt;</code> with attributes is now a colossal binding between HTML, JS, CSS and native. It's a JavaScript object that pretends to be an XML tag, embedded inside a layout model that takes years to understand fully.</p>

<p>Its namespace mixes metadata with page layout, getters and setters with plain properties, native methods with JS, string shorthands with nested objects, and so on. Guess how many properties the DOM Node Object actually has in total? We'll be generous and count <code>style</code> as one.</p>

<p>A hundred is not even close. A plain <code>&lt;div&gt;</code> doesn't fare much better. Just serializing a chunk of DOM back into its constituent XML is a tricky task once you get into fun stuff like namespaces. Nothing in the DOM is as simple as <code>JSON.stringify</code>. Why does my polygon have a base URI?</p>

<p>We have all these awesome dev tools now, yet we're using them to teach a terrible model to people who don't know any better.</p>

</div></div>

<aside class="g3 mt2"><div class="pad">
  <p><img src="https://acko.net/files/shadowdom/svg-in-dom.png" alt="SVG in DOM" class="s70"></p>
</div></aside>

<div class="g8 i2"><div class="pad">

<h2>DOM Shader</h2>

<p>In contrast, there's <a href="http://angularjs.org/">Angular</a>. I like it because they've pulled off a very neat trick: convincing people to adopt a whole new DOM by disguising it as HTML.</p>


<pre><code class="language-html" tabindex="1">&lt;body ng-controller="PhoneListCtrl"&gt;
  &lt;ul&gt;
    &lt;li ng-repeat="phone in phones"&gt;
      {{phone.name}}
      &lt;p&gt;{{phone.snippet}}&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ul&gt;
&lt;/body&gt;
</code></pre>
<div class="c"></div>


<p>When you use <code>&lt;input ng-model="foo"&gt;</code> or <code>&lt;my-directive&gt;</code>, you're creating a controller and a scope, entirely separate from the DOM, with their own rules and chain of inheritance. The pseudo-HTML in the source code is merely an initial definition, most of it inert to the browser. Angular parses it out and replaces much of it.</p>

<p>Like React, the browser's live DOM is subsumed and used as a sort of <em>render tree</em>, a generic canvas to be cleverly manipulated to match a given set of views. The real <em>view tree</em> hides in the shadows of JS, where controllers operate on scopes. They only use the DOM to find each other on creation, and then communicate directly. The DOM is mostly there to trigger events, do layout and look pretty. Form controls are the one exception.</p>

</div></div>

<aside class="g3 i1"><div class="pad">
  <p><img src="https://acko.net/files/shadowdom/svg-in-css.png" alt="SVG in CSS"></p>
</div></aside>

<div class="g8"><div class="pad">

<p>It's a bad fit because the DOM was built for text markup and there's tons of baggage in the form of inline spans, floats, alignment, indentation, etc. Most of these are layout systems disguised as typography, of which CSS now has several.</p>

<p>The whole idea of cascading styles is suspect. In reality, most styles don't actually cascade: paddings and backgrounds are set on individual elements. The inherited ones are almost all about typography: font styles, text justification, writing direction, word wrap, etc.</p>

<p>Think of it this way: why should a table have a font size? Only the <em>text</em> inside the table can have a font size, the table is just a box with layout that contains other <em>boxes</em>. Why don't we write <code>table text { size: 16px; }</code> instead of <code>table { font-size: 16px; }</code>? Text nodes exist today.</p>

<p>Well because that's how HTML's <code>&lt;font&gt;</code> tag worked. Instead of just making a selector for text nodes, they gave all the other elements font properties. They didn't <em>get rid</em> of font tags, they made them invisible and put one inside each DOM node.</p>

</div></div>

<div class="g8 i2 cl"><div class="pad">

<pre><code class="language-html" tabindex="2">&lt;html&gt;&lt;font&gt;
  &lt;body&gt;&lt;font&gt;
    &lt;h1&gt;&lt;font&gt;Hello World&lt;/font&gt;&lt;/h1&gt;
    &lt;p&gt;&lt;font&gt;Welcome to the future.&lt;/font&gt;&lt;/p&gt;
  &lt;/font&gt;&lt;/body&gt;
&lt;/font&gt;&lt;/html&gt;
</code></pre>
<div class="c"></div>

<h2>Unreasonable Behavior</h2>

<p>It was decided the world would be made of <code>block</code> and <code>inline</code> elements—divs and spans—and they saw that it was good, until someone came along and said, hey, so what about my table?</p>

<pre><code class="language-html" tabindex="3">&lt;table&gt;
  &lt;tr&gt;
    &lt;td&gt;Forever&lt;/td&gt;
    &lt;td&gt;Alone&lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;
</code></pre>
<div class="c"></div>

<p>This <code>&lt;table&gt;</code> can't be replicated with CSS 1. Tables require a particular arrangement of children and apply their own box model. It's a directive posing amongst generic markup, just like Angular.</p>

<p>CSS has never been able to deliver on the promise of turning semantic HTML into arbitrary layout. We've always been forced to add extra divs or classes. These are really just attachment points for independent behaviors.</p>

<p>Purists see these as a taint upon otherwise pristine HTML, even though I've never seen someone close a website because the markup was messy. Not all HTML should be semantic. Rather, HTML stripped of its non-semantic parts should remain meaningful to <em>robots</em>.</p>

<p>CSS 2's solution was instead to make <code>&lt;table&gt;</code> invisible too, to go with the invisible <code>&lt;float&gt;</code>, <code>&lt;layer&gt;</code>, <code>&lt;clear&gt;</code> and <code>&lt;frame&gt;</code> tags which we pretended we didn't have. Watch:</p>

</div></div>

<div class="g7 i1 cl"><div class="pad">

<div class="embed"><blockquote>
  <strong>17.2.1 Anonymous table objects</strong><br><br>

  […] Any table element will automatically generate necessary anonymous table objects around itself, consisting of at least three nested objects corresponding to a 'table'/'inline-table' element, a 'table-row' element, and a 'table-cell' element. Missing elements generate anonymous objects (e.g., anonymous boxes in visual table layout) according to the following rules […]
</blockquote></div>

</div></div>

<div class="g3"><div class="pad">

  <pre><code class="language-css" tabindex="4">.grid {
  display: table;
}
.grid > ul {
  display: table-row;
}
.grid > ul > li {
  display: table-cell;
}
</code></pre>
<div class="c"></div>

<p class="tc"><em>This is called Not Using Tables.</em></p>

</div></div>

<div class="g8 i2 cl"><div class="pad"> 
  
<p>Without typographical styles, <code>block</code> elements start to look very different. They're styled boxes with implied layout constraints. They stack vertically, expand horizontally and shrink wrap vertically. Floated blocks are boxes that stack horizontally, and shrink wrap both ways. Tables are grids of boxes that are locked together.</p>


<p>Just think how much simpler CSS would be if boxes had box styles and text had text styles, instead of all of them having both. Besides, block margins and paddings don't even work the same on inline elements, there's a whole new layout behavior there.</p>

<p>So we do have two kinds of objects, <em>text and boxes</em>, but several different ways of combining them into layout: inline, stacked, nested, absolute, relative, fixed, floated, flex or table. We have optional behaviors like scrollable, draggable, clipped or overflowing.</p>

<p>They're spread across <code>display</code>, <code>position</code>, <code>float</code> and more, only meaningful in some combinations. And presence is mixed in there too. As a result, you can't unhide an element without knowing its display model. This is a giant red flag.</p>

<h2>Thinking with Portals</h2>
  
<p>It should further raise eyebrows that the binary world of <code>inline</code> and <code>block</code> now also includes a hybrid called <code>inline-block</code>.</p>

<p><img src="https://acko.net/files/shadowdom/medium.png" alt="Medium share thing"></p>

<p>You generally don't need to embed a contact form–or all of Gmail—in the middle of mixed English/Hebrew poetry shaped like a bird. You just don't. To attach something to flowing text, you should insert an anchor point instead and add floating constraints. Links are called <em>anchor tags</em> for a reason. Why did we forget this?</p>

<p>Don't shove your entire widget right between the words. You'd inherit styles, match new selectors and bubble all your events up through the text just for the sake of binding a pair of (x, y) coordinates.</p>

<p>Heck, pointer events, cursors, hover states... these are for interactive elements only. Why isn't that optional, so mouse events wouldn't need to bubble up through inert markup? This would completely avoid the <code>mouseover</code> vs <code>mouseenter</code> problem. What is the point of putting a resize cursor on something that is dead without JavaScript? Pointer events shouldn't fire on inert children, and inert parents shouldn't care about interactive children. It's about boundaries, not hierarchy.</p>

</div></div>

<div class="g8"><div class="pad">

<p>Things like SVG are better used as image tags instead of embedded trees, just slotting into place while ignoring their surroundings. They do need their own tree structure, but there is little reason to graft it onto HTML/CSS, inheriting original sin. The nodes have too little in common. At most you can share the models, not the controllers.</p>

<p>We should be able to manipulate them from the outside, like a <code>&lt;canvas&gt;</code>, but define and load them declaratively, like an image tag.</p>

<p>For that matter, MathML should really be a single inline text tag, optimized for math, not a bunch of tags. Regular text spans are not just "plain text". They are trimmed, joined, bidirectionalized, word wrapped and ellipsified before display. It's a separate embedded layout model that makes up the true, invisible <code>&lt;p&gt;</code> tag. A tag that HTML1 actually sort of got right: as an <em>operator</em>.</p>

<p>We create JavaScript with code, not as <em>abstract syntax trees</em>. Why should I build articles and embedded languages out of enormously nested trees, instead of just typing them out and adding some <em>anchor</em> tags around specific interesting parts? The DOM already inserts invisible text nodes everywhere. We didn't need to wrap all our words in <code>&lt;text&gt;</code> tags by hand just to embiggen one of them. The mutant tree on the right could just look like this:</p>
  
<pre><code class="language-html" tabindex="5">&lt;math&gt;x = (-b &amp;pm; &amp;Sqrt;(b^2 - 4 a c)) / 2a&lt;/math&gt;</code></pre>
<div class="c"></div>

<pre><code class="language-html" tabindex="6">&lt;math&gt;x = (-b &pm; &Sqrt;(b^2 - 4 a c)) / 2a&lt;/math&gt;</code></pre>
<div class="c"></div>

<p class="tc"><em>Wasn't HTML5 supposed to match how people write it? LaTeX exists.</em></p>

<p>And which is easier: defining a hairy new category of pseudo-elements like <code>:first-letter</code> and <code>:first-line</code>… or just telling people to wrap their first letter in a span if they really want to make it giant? It was ridiculous to have this feature in a spec that didn't include tables.</p>

<p>The <code>:first-line</code> problem should be solved differently: you define two <em>separate</em> blocks inside a directive, to spread markup across two children with a content binding. It's no different from flowing text across lines and columns.</p>

</div></div>

<div class="g4 mt1"><div class="pad">

  <pre><code class="language-html" tabindex="7">&lt;mrow&gt;
  &lt;mi&gt;x&lt;/mi&gt;
  &lt;mo&gt;=&lt;/mo&gt;
  &lt;mfrac&gt;
    &lt;mrow&gt;
      &lt;mrow&gt;
        &lt;mo&gt;-&lt;/mo&gt;
        &lt;mi&gt;b&lt;/mi&gt;
      &lt;/mrow&gt;
      &lt;mo&gt;&amp;#xB1;&lt;!--PLUS-MINUS SIGN--&gt;&lt;/mo&gt;
      &lt;msqrt&gt;
        &lt;mrow&gt;
          &lt;msup&gt;
            &lt;mi&gt;b&lt;/mi&gt;
            &lt;mn&gt;2&lt;/mn&gt;
          &lt;/msup&gt;
          &lt;mo&gt;-&lt;/mo&gt;
          &lt;mrow&gt;
            &lt;mn&gt;4&lt;/mn&gt;
            &lt;mo&gt;&amp;#x2062;&lt;!--INVISIBLE TIMES--&gt;&lt;/mo&gt;
            &lt;mi&gt;a&lt;/mi&gt;
            &lt;mo&gt;&amp;#x2062;&lt;!--INVISIBLE TIMES--&gt;&lt;/mo&gt;
            &lt;mi&gt;c&lt;/mi&gt;
          &lt;/mrow&gt;
        &lt;/mrow&gt;
      &lt;/msqrt&gt;
    &lt;/mrow&gt;
    &lt;mrow&gt;
      &lt;mn&gt;2&lt;/mn&gt;
      &lt;mo&gt;&amp;#x2062;&lt;!--INVISIBLE TIMES--&gt;&lt;/mo&gt;
      &lt;mi&gt;a&lt;/mi&gt;
    &lt;/mrow&gt;
  &lt;/mfrac&gt;
&lt;/mrow&gt;
</code></pre>
<div class="c"></div>

<p class="tc"><em>This is the first example in the MathML spec. Really. "Invisible times".</em></p>
</div></div>

<div class="g6 i3"><div class="pad">

<pre><code class="language-html" tabindex="8">&lt;join&gt;
  &lt;box class="first-line"&gt;&lt;/box&gt;
  &lt;box&gt;&lt;/box&gt;
  &lt;content&gt;Hello New World&lt;/content&gt;
&lt;/join&gt;
</code></pre>
<div class="c"></div>

<p class="tc"><em>Would this really be insane?</em></p>

</div></div>

<div class="g8 i2"><div class="pad">

<h2>The Boxed Model</h2>

<p>CSS got it wrong and we're now suffering the consequences. The HTML feature that was ignored in CSS 1 was the thing they should've focused on: tables, which were directives that generated layout. It set us on a path of trying to fake them by piggybacking on supposedly semantic elements, like lipstick on a div. Really we were pigeonholing non-linear layout as a nested styling problem.</p>

<p>Semantic content was a false spectre on the document level. Making our menus out of <code>&lt;ul&gt;</code> and <code>&lt;li&gt;</code> tags did not help impaired users skip to the main article. Adding roman numerals for lists did not help us number our headers and chapters automatically.</p>

<p>View and render trees are supposed to be simple and transparent data structures, the <em>model for</em> and <em>result of</em> layout. This is why absolute positioning is a performance win for mobile: it avoids creating invisible dynamic constraints between things that rarely change. Styles are orthogonal to that, they merely define the shape, not where it goes.</p>

<p>Flash had its flaws, but it worked 15 years ago. Shoving raw SVG or MathML into the DOM—or god forbid XML3D–is a terrible idea. It's like there's an entire class of developers who've now forgotten how fast computers actually are and how memory is supposed to work. A <a href="http://c2.com/cgi/wiki?StringlyTyped">stringly typed</a> kitchen sink is not it.</p>

<p>So I frown when I see people excited about SVG in the browser in the year 2014, making polygons out of CSS 3D or driving divs with React. Yes I know, it's fun and it does work. And Angular shows the web component approach has merit. But we need a way out.</p>

<p>CSS should be limited to style and typography. We can define a real layout system next to it rather than on top of it. The two can combine in something that still includes semantic HTML fragments, but wraps layout as a first class citizen. We shouldn't be afraid to embrace a modular web page made of isolated sections, connected by reference instead of hierarchy.</p>

<p>Not my problem though, I can make better SVGs with WebGL in the meantime. But one can dream.</p>

</div></div>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[On Asm.js]]></title>
    <link href="http://acko.net/blog/on-asmjs/"/>
    <updated>2013-11-27T00:00:00+01:00</updated>
    <id>http://acko.net/blog/on-asmjs</id>
    <content type="html"><![CDATA[<div class="g8 i2"><div class="pad">

<h2 class="sub">Ending The Ice Age of JavaScript</h2>

<p>The demo is striking: Unreal Engine, <a href="https://blog.mozilla.org/futurereleases/2013/05/02/epic-citadel-demo-shows-the-power-of-the-web-as-a-platform-for-gaming/">running live in a browser</a>, powered by Mozilla's <em>Asm.js</em>, offering near native performance with hardware accelerated graphics. A gasp here, a wow there, everyone is surely impressed. And look ma', no plug-ins: just JavaScript, Web Audio and WebGL! Just don't mind the 10 second hang on load as it's <em>Preparing JavaScript</em>.</p>

<p>When I heard of it, it sounded great: a highly optimizable subset of JS, that can be dropped seamlessly into any existing code. It reminded me of Winamp's charming <a href="https://gitorious.org/xbmc/xbmc/source/add3bf0839626430245a3248b229caae4aed5d70:xbmc/visualizations/Milkdrop/vis_milkdrop/evallib/Compiler.c">evallib</a> compiler, used in <a href="/blog/avs">AVS</a> and <a href="http://blog.nihilogic.dk/2009/03/javascript-canvas-sm2-milkdrop.html">Milkdrop</a>, which only did floating point expressions. It spawned a whole subculture of visual hackery based on little more than a dozen or so math functions and some clever graphics routines. It showed the power of being able to turn scripts into optimal machine code on the fly, and having a multimedia platform at your disposal while doing so.</p>

</div></div>

<div class="c"></div>

<div class="wide mt1">
    <a target="_blank" href="http://www.unrealengine.com/html5/"><img src="https://acko.net/files/asmjs/epiccitadel.jpg" alt="Epic Citadel"></a>
</div>

<div class="g8 i2 mt1"><div class="pad">

<p>But that's not what Asm.js is for at all. It's not for people, it's a compiler target, a way of converting non-JS code into a form that browsers handle well. Its design is based on how JavaScript handles numbers: as 64-bit <em>doubles</em>, onto which you can perform select 32-bit integer operations. As such, Asm.js seems like a sweet hack similar to UTF-8, an elegant way of encoding something complicated under strong legacy constraints: typed arithmetic despite a single number type. Yet the part of me that remembers pushing pixels with MMX, that watched this web thing for more than a few years, can't help but ask exactly what it is we're trying to do here.</p>

<p>Asm.js deserves closer inspection for two reasons. First, it's the one "native browser VM" that doesn't massively reinvent wheels. Second, it's the only time a browser vendor's "next-gen JS" attempts have actually gotten everybody else to <a href="https://blog.mozilla.org/futurereleases/2013/11/26/chrome-and-opera-optimize-for-mozilla-pioneered-asm-js/">pay attention</a>. But what are we transitioning into exactly?</p>

<h2>LLVM to Asm</h2>

<p>To understand Asm.js, you have to understand <a href="http://llvm.org">LLVM</a>. Contrary to its name, it's not really a Virtual Machine but rather a compiler. More precisely, it's the modular core of a compiler, along with assorted tools and libraries. You plug in different front-ends to parse different languages (e.g. C or C++), and different back-ends to target different machines (e.g. Intel or ARM). LLVM can do common tasks like optimization in a platform and language agnostic way, and as such should probably be considered an open-source treasure.</p>

<p class="tc"><a href="http://www.aosabook.org/en/llvm.html"><img src="https://acko.net/files/asmjs/llvm.png" alt="llvm"></a></p>

<p class="tc"><em>LLVM architecture</em> (<a href="http://www.aosabook.org/en/llvm.html">source</a>)</p>

<p>Per <a href="http://www.codinghorror.com/blog/2007/07/the-principle-of-least-power.html">Atwood's law</a>, it was inevitable that someone decided the back-end should be JavaScript. Thus was born <a href="https://github.com/kripken/emscripten">emscripten</a>, turning C into JS—or indeed, <em>anything</em> native into JS. Because the output is tailored to how JS VMs work, this already gets you pretty far. The trick is that native code manages its own memory, creating a <a href="http://stackoverflow.com/questions/79923/what-and-where-are-the-stack-and-heap">stack and heap</a>. Hence you can output JS that just manipulates pre-allocated typed arrays as much as possible, and minimizes use of the garbage collector.</p>

<p>This works particularly well because JavaScript VMs already cheat when it comes to numbers and number types. They receive special treatment compared to other data. You can find a good overview in <a href="http://wingolog.org/archives/2011/05/18/value-representation-in-javascript-implementations">value representation in JavaScript implementations</a>. The gist is that JS VMs handle floating point and integer arithmetic separately and efficiently, with lots of low level trickery to speed up computation. Modern VMs will furthermore try to identify code that uses one type only, and emit highly optimized type-specific code with only minimal checks at the boundaries. It's this kind of code that emscripten can emit a lot of, e.g. translating the clang compiler into <a href="http://kripken.github.io/clangor/clang.js">48MB of JS</a>.</p>

</div></div>

<aside class="g5 c">
<p class="codeblock">
<code>function DiagModule(stdlib, foreign, heap) {
  "use asm";

  // Variable Declarations
  var sqrt = stdlib.Math.sqrt;

  // Function Declarations
  function square(x) {
      x = +x;
      return +(x*x);
  }

  function diag(x, y) {
      x = +x;
      y = +y;
      return +sqrt(square(x) + square(y));
  }

  return { diag: diag };
}
</code>
</p><p>Example Asm.js code from the spec.</p>
</aside>

<div class="g7"><div class="pad">

<p>Which brings us to the bittersweet hack of Asm.js. Once you realize that C can run 'well' as JavaScript even when the VM has to guess and juggle, imagine how much faster it could be when the VM is in on it.</p>

<p>What this means in practice is a directive "use asm" in a block of tailored code, along with implicit type annotations for arguments, casts and return values. Type casts are <code>x|0</code> for <code>int</code>, <code>+x</code> for <code>double</code>. These annotations are parsed and validated, and optimized code is emitted for the entire code block. This doesn't look bad at all. However, it also looks nothing like real Asm.js code in the wild.</p>

<p>Benchmarks show roughly a 1–2× slowdown compared to native code, significantly faster than normal JS. Hooray, JavaScript wins again, the web is awesome. Because of LLVM, an enormous piece of <em>external, non-web infrastructure</em>. Wait what?</p>

</div></div>

<div class="g8 i2"><div class="pad">

<h2>Impostor Syndrome</h2>

<p>When something bugs me that I can't put my finger on, there's usually a contradiction that I'm not seeing. After a few talks, articles and conversations, it seems pretty obvious: it puts JavaScript on a pedestal, even as it makes it irrelevant.</p>

<p>It makes JavaScript irrelevant because with LLVM's infrastructure or similar tools, practically anything can or will be compiled into JS.</p>

<p>But it also makes JavaScript more important, focusing the future optimization efforts of browser makers onto it, despite its ill-suited semantics.</p>

<p>It means JavaScript has nothing to do with it, it's just the poison we ended up with, the bitter pill we supposedly have to swallow. So when Brendan Eich <a href="http://www.youtube.com/watch?v=O83-d0t0Ldw">says with a smile</a> "Always bet on JavaScript", what he really means is "Always bet on legacy code" or perhaps "Always bet on politics". When you think about it, it's weird to tell JavaScript developers about Asm.js. It's not actually aimed at them.</p>
  
<p>Looking around, in the browser there's CoffeeScript, TypeScript and Dart. Outside, there's Python, Ruby, Go and Rust. Even CSS now has offspring. The future of the web is definitely multilingual and some people want to jump ship, they're just not sure which one will actually sail yet.</p>

<p>When faced with a legacy mechanism like UTF-8 or indeed Asm.js, we have to ask, is it actually necessary? In the case of UTF-8, it's a resounding yes: we need to assign unique names to things, and this name has to work with both modern and legacy software, passing through unharmed as much as possible. UTF-8 solves a bunch of problems while causing very few.</p>

<p>But with Asm.js, it's just a nice to have. All Asm.js code is new, there is no vault of legacy code that will stop working if we do it wrong. We can already generate functioning JS for legacy browsers, along with something new for alternative VMs. Having one .js file that does both is merely a convenience, and a dubious one at that.</p>

<p>Indeed, the unique appeal of Asm.js is for the browser maker who implements it first: it lets their JS VM race closer to that much desired Native line. It also turns any demo that uses Asm.js into an instant benchmark, which other vendors have to catch up with. It's a rational choice made in self interest, but also a tragedy of the commons.</p>

<p>Maybe that's a bit hyperbolic, but work with me here. There's a serious amount of defeatism and learned helplessness at work here, and again a contradiction. We seek to get ever closer to native performance, yet fall short by design, resigning ourselves to never quite reaching it. I can't be the only one who finds that completely bizarre, when there's laptops and phones running entirely on a web stack now?</p>

<p>If you look at the <a href="https://wiki.mozilla.org/Javascript:SpiderMonkey:OdinMonkey">possible future of Asm.js</a>, there's SIMD extensions, long ints with value objects, math extensions, support for specific JVM/CLR instructions and more. Asm.js is positioned not just as something that works today, but that leads into a bright future to boot. And yet, it all has to be shoehorned into something that is still 100% JavaScript, even as that target itself consists of moving goalposts.</p>

</div></div>

<div class="c"></div>

<div class="wide mt1">
  <p class="mt0"><img src="https://acko.net/files/asmjs/code.jpg" alt="Epic Citadel Code"></p>
  <p class="tc"><em>Part of Unreal Engine, JSified.</em></p>
</div>

<div class="g8 i2"><div class="pad">

<h2>History Repeating</h2>

<p>So fast forward a year or two. Firefox has completed its wishlist and Asm.js has filled its instruction set gaps. Meanwhile Chrome has continued to optimize V8. Will they have the same new language features that Firefox has? Will they officially support Asm.js? Or push Dart and PNaCl, expanding their influence through ChromeOS and Android? Your guess is as good as mine. As for IE and Safari, I'll just pencil in "behind" for now and leave it at that.</p>

<p>But a certain phrase comes to mind: embrace and extend. From multiple fronts.</p>

</div></div>

<aside class="g4"><div class="pad">
  
  <p><img src="https://acko.net/files/asmjs/asmjsinside.png" alt="asm.js inside"></p>

</div></aside>

<div class="g8"><div class="pad">

<p>It looks like a future where your best bet to get things running fast in a browser is to do decidedly non-web things. You compile something like C to the different flavors of web at your disposal, either papering over their strengths, or tailoring for each individually. That's not something I personally look forward to, as much as it might arouse Epic's executives and shareholders today.</p>

<p>Web developers wouldn't actually be working that differently. They might be using a multi-language framework like Angular, or dropping in a neat C physics library somebody cross compiled for them. I doubt they'll have a nice web-native way to run the same performance-critical code everywhere. You'll just waste some battery life because a computer pretended to be a JavaScript developer. For backwards compatibility with browsers that auto-update every few weeks. Eh?</p>

</div></div>

<div class="g8 i2"><div class="pad">

<p>I admit, I don't know what the post-JS low level future should look like either, but it should probably be closer to <a href="http://mbebenita.github.io/LLJS/">LLJS</a>'s nicely struct'ed and typed order, than either the featherweight of Asm.js <em>or</em> the monolithic Flash-replacement that is PNaCl.</p>

<p>The big problem with Asm.js isn't that it runs off script rather than bytecode, it's that the code is generated to match how JS engines work rather than how CPUs compute. At best it will be replaced with something more sensible later or just fizz out as an optimization fad. At worst it'll become the <a href="http://en.wikipedia.org/wiki/IA-32">IA-32</a> of the web, still pretending to be an 8086 if asked to.</p>

<p>Looking ahead, there's computation with WebCL, advanced GLSL shaders and more on the horizon. That's a whole set of problems that can become much simpler when "browser" is a language that everyone can speak, to and from, rather than a weird write-only dialect built on a tower of Babel. We don't just need a compilation target, we need a compilation source, as well as a universal intermediate representation.</p>

<p>And this is really the biggest contradiction of them all. Tons of people have invested countless hours to build these VMs, these new languages, these compilers, these optimizations. Yet somehow, they all seem to agree that it is impossible for them to sit down and define the most basic glue that binds their platforms, and implement a shared baseline for their many innovations.</p>

<p>We really should aim higher than a language frozen after 10 days, thawing slowly over 20 years.</p>

</div></div>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Animate Your Way to Glory - Part II]]></title>
    <link href="http://acko.net/blog/animate-your-way-to-glory-pt2/"/>
    <updated>2013-09-13T00:00:00+02:00</updated>
    <id>http://acko.net/blog/animate-your-way-to-glory-pt2</id>
    <content type="html"><![CDATA[<script type="text/x-mathjax-config">
MathJax.Hub.Config({
  "HTML-CSS": { availableFonts: ["TeX"] },
  extensions: ["tex2jax.js"],
  jax: ["input/TeX","output/HTML-CSS"],
  tex2jax: {inlineMath: [["$","$"],["\\(","\\)"]]},
});
</script>

<script type="text/javascript"
  src="https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS_HTML.js">
</script>

<script type="text/javascript">
// <!--
window.MathJax && MathJax.Hub.Queue(["Typeset",MathJax.Hub]);
Acko.queue(function () { Acko.Fallback.warnWebGL(); });
// -->
</script>

<div class="g8 i2"><div class="pad">

<h2 class="sub">Math and Physics in Motion</h2>

</div></div>

<div class="c"></div>

<div class="g8 i2"><div class="pad">

<h2 id="splines">Doctor… Who?</h2>

<p>All the models we've dealt with until now are programmatic. If we wish to run a sequence of animations, we have to schedule calls appropriately, perhaps using a queue. The proper tool for this job is a timeline. At first glance, it's just a series of keyframes on tracks: a set of coordinates over time, one for each property you're animating, with some easing in between. But it's hard to offer direct controls to a director or animator, without creating uneven or jarring motion, at least in 2D.</p>

<p>We must stop treating space and time as separate things, and chart a course in both at the same time.</p>

</div></div>

<div class="wide slideshow full">

  <div class="iframe c">
    <iframe src="/files/animating/mb-3-timeline.html" class="mathbox paged autosize" height="320"></iframe>
  </div>

  <div class="steps">

    <div class="step">
      <p>This a classic keyframe timeline: a set of frames, with values defined along the way. It could be a vertical or horizontal motion, the opacity of a shape, the volume of a sound, etc. Any one thing we want to animate precisely over a long time.</p>
    </div>

    <div class="step">
      <p>This is one second of a 60 fps animation and there's a keyframe every 10 frames. We can interpolate between the points with a <span class="blue">cosine ease</span>. But there's already a mistake.</p>
    </div>

    <div class="step">
      <p>By expressing animations as frames, we can only have animations that are multiples of the frame length. In this case, that's 16.6 ms. If we want to space keyframes at 125ms, we can't, because that's 7.5 frames. The closest we can manage is alternating <span class="orangered">7</span> and <span class="green">8</span> frame sections.</p>
    </div>

    <div class="step">
      <p>Just like with variable frame rates, we need to set keyframes in <span class="blue">absolute time</span>, not numbered frames. We use a global clock to produce arbitrary in-between frames. If we change our mind and wish to speed up or slow down part of our timeline, there's no snap-to-frame to get in our way. Note that Adobe Flash does <em>not</em> do this: you define your frame rate up front and are stuck with it.</p>
    </div>

    <div class="step">
      <p>There is a catch though, easy to overlook: by the time we notice the first animation has ended, the second one has already started. We need to account for this <em>slack</em>, and make sure we start partway in, <span class="orangered">not&nbsp;from the beginning</span>. Otherwise, this error accumulates with every keyframe, leading to noticeable lag.</p>
    </div>

    <div class="step">
      <p>This is also important for triggered actions like <span class="purple">sound</span>. Suppose there is a performance glitch right before it plays, and we lose 7 frames. Rare, but not impossible. If we don't account for slack, we'd have 7.5 frames of permanent lag on the audio, 125ms. More than enough to disrupt lip sync. Instead we should skip ahead to make up for it. To avoid an audible pop when skipping audio, we can apply a tiny fade in: a <em>microramp</em>.</p>
    </div>

    <div class="step">
      <p>With real-time dependencies like audio, it's better to be safe than sorry though. As the audio subsystem is generally independent, we can avoid this issue by pre-queuing all the sound with a delay. Here we begin playback 100ms earlier, but start each sound with an implied 100ms silence, minus the slack. Now, no audio will be lost in most situations. This too is animation: micromanaging time.</p>
    </div>

    <div class="step">
      <p>Let's focus our attention back on <span class="blue">this easing curve</span>. By treating it as a sequence of individual animations, we've created a smooth path. But it's not a very ideal path: it stops at every keyframe and then starts moving again, creating a curve with stairs. This is more obvious if we plot the <span class="green">velocity</span>.</p>
    </div>

    <div class="step">
      <p>We need to replace it with a <em>spline</em>, a <em>designable</em> curve. There's too many to name, but we'll stick to a common one: Catmull-Rom splines. It's entirely based on <span class="purple">one particular curve</span>. Looks suspiciously like an <em>impulse response</em>, doesn't it?</p>
    </div>

    <div class="step">

      <div class="extra top edge" data-delay="4.5" data-align-y=".95" ><span style="font-size: 80%">
        $$ 
        catmullRom(t) = \frac{1}{2} \cdot

        \left\{
        	\begin{array}{rcll}
            \class{purple}{p_1(t)} &amp; = &amp; t^3 + 5 t^2 + 8 t + 4  &amp; \mbox{if } -2 \leq t \lt -1 \\
            \class{royal}{p_2(t)} &amp; = &amp; -3 t^3 - 5 t^2 + 2      &amp; \mbox{if } -1 \leq t \lt 0 \\
            \class{purple}{p_3(t)} &amp; = &amp; 3 t^3 - 5 t^2 + 2     &amp; \mbox{if } 0 \leq t \lt 1 \\
            \class{royal}{p_4(t)} &amp; = &amp; -t^3 + 5 t^2 - 8 t + 4 &amp; \mbox{if } 1 \leq t \lt 2 \\
        	\end{array}
        \right.

           $$</span>
      </div>
      
      <p>But actually, it's not one curve, it's 4 separate <span class="purple">cubic</span> <span class="royal">curves</span> <span class="purple">glued</span> <span class="royal">together</span> into a symmetric pulse. They're designed so their <span class="green">velocities</span> meet up at the transition, thus creating a <span class="purple">single smooth path</span>. But if you look closely, you can see that the <span class="green">velocity</span> (scaled) has two minor kinks in it, one on each side.</p>
    </div>

    <div class="step">
      <p>There are two other important features. The first is that the curve <span class="purple">goes through 0</span> at all the keyframes <em>except the central one</em>. There, its <span class="purple">value is 1</span>. The keyframes are called the <em>knots</em> of the spline.</p>
    </div>

    <div class="step">
      <p>The other is that its <span class="green">slope</span> is 0 at all the knots <em>except the ones adjacent to the peak</em>. There, it's <span class="green">$ \frac{1}{2} $</span> and <span class="green">$ -\frac{1}{2} $</span> respectively. If we trace the slopes out to the center, we go <em>half as high</em> as the peak, to 0.5.</p>
    </div>

    <div class="step">
      <p>That means if we <span class="purple">scale down</span> this curve as a whole, very few things we're interested in actually change. All the horizontal slopes remain horizontal. All the knots at 0 remain at 0. Only the <em>peak shrinks</em>, and the <em>slopes at the adjacent knots go down</em>.</p>
    </div>

    <div class="step">
      <div class="extra top" data-align-y="1.2" data-delay="4.5">
        $$ 
          \class{blue}{p_i} \cdot catmullRom(t-i) 
        $$
      </div>
      <p>We can literally treat the curve as the <span class="purple">impulse response</span> of a filter, and the knots as a series of <span class="blue">impulses</span>. A filter outputs a copy of its impulse response for every impulse it encounters. As this is all theoretical, we don't care about filter lag.</p>
    </div>

    <div class="step">
      <div class="extra top" data-align-y="1.2"  data-delay="4">
        $$ 
          \class{blue}{spline(t)} = \sum\limits_{i=0}^n \class{blue}{p_i} \cdot catmullRom(t-i) 
        $$
      </div>
      <p>If we now add up all the curves, we get the <span class="blue">Catmull-Rom spline</span>. Despite the intricate interactions of the curves between the knots, the result is very predictable. The spline goes through every keyframe, because the <span class="blue">values at the knots</span> are all 0 except for the peak itself.</p>
    </div>

    <div class="step">
      <p>What's more, when we move a single value up and down, only two other things change: the two slopes at the <span class="purple">adjacent knots</span>. The <span class="green">slope at the knot itself</span> is still constant. This means we can control the initial and final slope of the spline just by adding an extra knot before and after: it won't affect anything else.</p>
    </div>

    <div class="step">
      <p>See, the slope at a knot is actually just the <span class="green">central difference</span> around that point. This is where the factor of $ \frac{1}{2} $ for the <span class="purple">adjacent slopes</span> came from earlier, and why their signs were opposite: it's a difference that spans two keyframes, so we divide by 2. This is the rule that determines how <span class="blue">Catmull-Rom splines</span> curve.</p>
    </div>

    <div class="step">
      <p>There's just one problem: all of this only works if the keyframes are equally spaced. If we change the spacing, our <span class="purple">base curve</span> is no longer smooth: there is a kink at the adjacent keyframes. This might not look like much, but it would be noticeable.</p>
    </div>

    <div class="step">
      <p>There's two ways to solve this. One is to try and come up with a <span class="purple">unique curve</span> for every knot. This curve has to be smooth and hit all the right values and slopes. This is the hard way, and can result in odd changes of curvature if done badly, like here.</p>
    </div>

    <div class="step">
      <p>But actually, you already know the other solution. By distorting the <span class="blue">Catmull-Rom spline</span> to fit our keyframes, it's like we've rendered it with a <em>variable speed clock</em>. But one that doesn't change smoothly. This is why the curves have developed kinks out of nowhere. If we can smooth out the passage of time, then we'll stretch the spline smoothly between the keyframes.</p>
    </div>

    <div class="step">
      <p>We can just create another Catmull-Rom spline to do so. Horizontally, we put <span class="cyan">equally spaced knots</span>. This dimension has no real meaning: it's just 'spline time' now, independent of real time.</p>
    </div>

    <div class="step">
      <p>We move the knots vertically to the keyframe time and make a <span class="cyan">spline</span>. In this case, I tweaked the start and end to be a diagonal rather than a horizontal slope. This curve hits all the keyframes at the right time and transitions smoothly between them. It's a variable clock that goes from <em>constant spline time to variable real time</em>.</p>
    </div>

    <div class="step">
      <p>To actually calculate the animation, we need to go the other way and invert the relationship: from <em>variable real time to constant spline time</em>. This can be done a few ways, but the easiest is to use a binary search, as the time curve always rises: it's like finding a value in an ordered list. This tells us how fast to <em>scrub through</em> the spline. </p>
    </div>

    <div class="step">
      <p>With this, we can warp the <span class="blue">Catmull-Rom spline</span> to hit all the keyframes at the right times. We'll still need to manually edit the keyframes to get a perfectly ripple-free path, but now we can move them anywhere, anytime we like.</p>
    </div>

    <div class="step">
      <p>What we just did was to chart a path through 1D space and 1D time, by combining two Catmull-Rom splines. Add <span class="blue">time travel</span>, and this is entirely equivalent to charting a random 2D spline through 2D space. To create such an animation, you create two parallel tracks, one for X and one for Y, with identical timings. By scrubbing through <em>spline time</em>, you move in both X and Y, and hence along the curve. However, doing so precisely turns out to be complicated.</p>
    </div>

    <div class="step">
      <p>In the 1D case, the distance between two keyframes is trivial: going from 0 to 1 means you moved 1 unit. In the 2D case, that's no longer true: the distance travelled depends on both X and Y simultaneously. What's worse is, splines generate <span class="blue">uneven paths</span>. If we divide them equally in <em>spline time</em>, we get unequal steps in <em>real time</em>. The apple slows down and then shoots off.</p>
    </div>
    
    <div class="step">
      <p>It might seem cool that the spline naturally has a tension in its motion, but it will only get in the way. If we move just a single X coordinate of a single knot, the entire path shifts, and the <span class="blue">distance between the steps</span> changes considerably. The easing of the Y coordinate needs to compensate for this. We can't maintain a controlled velocity this way: X and Y are dependent and have to be animated together.</p> 
    </div>

    <div class="step">
      <p>We can resolve this by doing for distance what we did for time: we have to make a map from <em>spline time</em> to <em>real distance</em>. We can step along the spline in small steps and measure the distance one line segment at a time. When we <span class="green">integrate</span>, adding up the pieces, we get a <span class="cyan">curve</span> that maps <em>spline time</em> onto <em>total distance along the curve</em>.</p>
    </div>

    <div class="step">
      <p>Again, we can invert the relationship to get a map from <span class="cyan"><em>distance</em> to <em>spline time</em></span>.</p>
    </div>

    <div class="step">
      <p>We can use it to divide the spline into segments of equal length and move an object along the path with a constant speed. This works for any spline, not just Catmull-Rom. We can always turn a curve into a neat set of <span class="blue">equally spaced points</span> of our choosing.</p>
    </div>

    <div class="step">
      <p>The distance map gives us a <span class="blue">natural parametrization</span>: a way to move along the curve by the arc length itself. This effectively flattens out the curve into a straight line, and we can treat it like a 1D animation. We can apply straightforward easing again, because distances are once again preserved.</p>
    </div>

    <div class="step">
      <p>To animate, we just define an easing curve for <span class="blue">distance over time</span>. If we want to move along the path at a constant speed, we line the keyframes up along a diagonal.</p>
    </div>

    <div class="step">
      <p>However, the knots don't have any special meaning anymore. When we move, we pass through them at just the same speed as any other point. That means we can control velocity completely independent of the path itself, using all the tricks from earlier. We can also apply a direct easing curve along the path, for example <span class="blue">cubic easing</span>.</p>
    </div>

    <div class="step">
      <p>To run the animation, we go the other way. The easing curve tells us the <span class="blue">desired distance along the path</span> at any moment in time. We have to use the <span class="cyan">inverse distance map</span> to convert this to <em>spline time</em> for the point in question.</p>
    </div>

    <div class="step">
      <p>Then we can use the <em>spline time</em> to look up the point on the <span class="purple">Catmull-Rom spline</span>. The <span class="blue">easing curve</span> makes us scrub smoothly along the <span class="cyan">distance map</span>. This in turn makes us move smoothly on the <span class="purple">spline</span>—albeit with a bit of whiplash.</p>
    </div> 

    <div class="step">
      <p>While that might seem like a lot of work, the good news is, it works in 3D too. We can find a <span class="cyan">distance map based on 3D distance</span>, and now have three simultaneous Catmull-Rom splines for the <span class="purple">X, Y and Z</span> coordinates.</p>
    </div>
    
    <div class="step">
      <p>In a way, path-based animation is <em>cheating</em>: it acts like there's an infinitely strong force keeping the object <span class="purple">on the track</span>, only we don't need physics to make it happen. If we <em>did</em> add other forces however, we'd get a miniature WipeOut-style racing game. This principle is applied in the demo at the top of this page: the velocity along the track is constant, but the camera and its target are being exponentially eased, creating lag and swings in corners, giving it a natural feel.</p>
    </div> 



  </div>
  
</div>

<div class="c"></div>

<div class="g8 i2"><div class="pad">

<p>Timelines and splines are both sides of the same coin: using piecewise sections to create smoothness. The combination of both gives us path-based animation, pretty close to being the holy grail of controlled animation. We can fit this neatly into any timeline model—provided we don't lose track of all the tiny extra bits of time—with any easing mechanism we want. The track and the motion on it are completely decoupled.</p>

<p>Aside from Catmull-Rom, there's the non-rational splines, the popular <a href="/tv/wdcode/">Bezier curves</a> as well as other recursive methods. As most of these allow you to control the slope directly, you get direct velocity control on any path in a timeline.</p>

<p>Path-based animations don't have to be restricted to positions either. You can animate RGB colors as XYZ triplets the same way. Or you could animate the parameters of a procedural generator, or a physics simulation. Or animate the volume levels of music in response to gameplay. Or move your robot. Timelines are excellent tools to manage change, but only if you can control the speed precisely at the same time.</p>

<p>Which leaves us only one thing: rotation.</p>
  
  

<h2 id="quaternions">Blowing up the Death Star</h2>

<p>How difficult can a few angles be? Very. In 2D, they don't cooperate with our linear models. Even just turning to face a particular direction requires care. In 3D, things get properly messed up. Rotations will turn the wrong way, wobble in place and generally not behave. If you're trying to animate a free-moving camera in 3D, fixing this is pretty important, unless you're making <em>Motion Sickness Tycoon</em> or <em>Cloverfield Returns</em>.</p>

<p>Defeating this particular Goliath will require a careful approach. We'll launch our squadrons of X, Y and Z-wings, use the Force, and attack the weak spot for maximum damage. It better not be a trap.</p>

</div></div>

<div class="wide slideshow full">

  <div class="iframe c">
    <iframe src="/files/animating/mb-4-quaternion.html" class="mathbox paged autosize" height="320"></iframe>
  </div>

  <div class="steps">

    <div class="step">
      <div class="extra left" data-align-x="1.25" data-delay="4">
        <big>$$ 
          \phi = 0°
        $$</big>
      </div>
      <p>What's wrong with angles? Let's ask our trusty friend, the apple. Sorry, I got hungry.</p>
    </div>

    <div class="step">
      <div class="extra left" data-align-x="1.4" data-delay="4">
        <big>$$ 
        	\begin{array}{rcl}
          \class{blue}{\phi} &amp; = &amp; 2.3 \cdot τ \\
          &amp; = &amp; 828°
          \end{array}
        $$</big>
      </div>
      <p>Well, they <em>wrap around</em>. Suppose we have an object that's been rotated a couple of times, for example as part of an interactive display. It completed 2.3 turns ($ τ = 2π $) around the circle. For now we'll use degrees, but eventually we'll switch to radians for the heavier stuff.</p>
    </div>

    <div class="step">
      <div class="extra left" data-align-x="1.4">
        <big>$$ 
          \class{purple}{\phi_T} = 0° 
        $$</big>
      </div>
      <p>If we animate the apple to a target angle $ \class{purple}{\phi_T} $ at 0°, it will spin all the way back. Our animation system doesn't know that it could stop earlier at 720° or 360°.</p>
    </div>

    <div class="step">
      <div class="extra left" data-align-x="1.4">
        <big>$$ 
          \class{purple}{\phi_T} = 315° \\
        $$</big>
      </div>
      <p>To fix this, we can't simply reduce all angles to the interval 0…360. If we animate from 0 to <span class="purple">315°</span>, we still go the long way around rather than just 45° in the other direction.</p>
    </div>

    <div class="step">
      <div class="extra left" data-align-x="1.4">
        <big>$$ 
        	\begin{array}{rcl}
          \class{blue}{\phi} &amp;=&amp; 315° \\
          \class{purple}{\phi_T} &amp;=&amp; 90° \\
          \end{array}
        $$</big>
      </div>
      <div class="extra right edge" data-delay="2">
        $$ 
        	\begin{array}{rcl}
          δ &amp;=&amp; \frac{\class{purple}{\phi_T} - \class{blue}{\phi}}{360°} \\[8pt]
          \class{green}{Δ\phi} &amp;=&amp; 360° \cdot (δ - ├\,δ\,┤) \\
          \end{array}
        $$
      </div>
      <p>We need to reduce the difference in angle $ \class{purple}{\phi_T} - \class{blue}{\phi} $ to less than 180° in either direction. This is a <em>circular difference</em>, easiest when counting whole turns $ δ $, so we can <em>round off</em> to $├\,δ\,┤$. The difference, e.g. $ 3.3 - 3 = 0.3 $ or $ 1.6 - 2 = -0.4 $ is never more than half a turn. If we now set the target to <span class="purple">90°</span>, it tells us to animate by $ \class{green}{+135°} $, that is, the short way around.</p>
    </div>
    
    <div class="step">
      <div class="extra left" data-align-x="1.4">
        <big>$$ 
          \class{purple}{\phi_T} = 90° \\
          \class{blue}{\phi} = 450° \\
        $$</big>
      </div>
      <p>Our angles are now still continuous, going <span class="blue">beyond 360°</span> in either direction, but we never rotate more than 180° at a time unless we actually want to. We can apply this correction whenever we interpolate between two angles, and always end up at an equivalent angle.</p>
    </div>

    <div class="step">
      <p>Here I use double exponential easing to chase a <span class="blue">rapidly changing angle</span>. The <span class="purple">once filtered angle</span> jerks whenever it gets lapped, as it suddenly needs to change direction. The <span class="green">twice filtered angle</span> moves smoothly however.</p>
    </div>
    
    <div class="step">
      <p>What about 3D? If we're restricting ourselves to a single axis of rotation, nothing really changes. We still control the angle the same way.</p>
    </div>

    <div class="step">
      <p>But orientation in 3D is a complicated thing. The easiest way to express it is with a <em>3×3 matrix</em>: this is a set of 3 vectors in 3D. They define a frame of reference in space, a <em>basis</em>: <span class="blue">right/left</span>, <span class="green">up/down</span> and <span class="red">forward/back</span>. When we rotate around the vertical axis <span class="green">$ \vec y $</span>, we rotate <span class="blue">$ \vec x $</span> and <span class="red">$ \vec z $</span> together.</p>
    </div>

    <div class="step">
      <p>For arbitrary orientations, <span class="blue">$ \vec x $</span>, <span class="green">$ \vec y $</span> and <span class="red">$ \vec z $</span> can turn in any direction, but always maintain a perfect 90° angle between themselves.</p>
    </div>

    <div class="step">
      <div class="extra right" data-align-x="1.15">
        <big><big>$$ 
          \begin{bmatrix}
            \class{blue}{a} &amp; \class{green}{d} &amp; \class{orangered}{g}  \\
           \class{blue}{b} &amp; \class{green}{e} &amp; \class{orangered}{h} \\
           \class{blue}{c} &amp; \class{green}{f} &amp; \class{orangered}{i}
          \end{bmatrix}
        $$</big></big>
      </div>
      
      <p>Each vector is a set of $ (x, y, z) $ coordinates. That means we can write down the matrix as a set of 3 triples of coordinates, one column <span class="blue">for</span> <span class="green">each</span> <span class="red">vector</span>. At first it would seem we need <em>9 numbers</em> to describe a 3D rotation. We can apply this <em>rotation matrix</em> to transform any point $ (x, y, z) $ by adding up proportional amounts of <span class="blue">$ \vec x $</span>, <span class="green">$ \vec y $</span> and <span class="red">$ \vec z $</span>. This is <em>linear</em> algebra.</p>
    </div>

    <div class="step">
      <div class="extra right" data-align-x="1.15">
        <big><big>$$ 
          \begin{bmatrix}
            \class{blue strike}{a} &amp; \class{green strike}{d} &amp; \class{orangered}{g}  \\
           \class{blue strike}{b} &amp; \class{green strike}{e} &amp; \class{orangered}{h} \\
           \class{blue strike}{c} &amp; \class{green strike}{f} &amp; \class{orangered}{i}
          \end{bmatrix}
        $$</big></big>
      </div>
      
      <p>But there's tons of redundancy here. Because the 3 vectors are perpendicular, <span class="red">$ \vec z $</span> can only be in one of two places. The difference between the two is called a <em>left handed</em> or <em>right handed</em> coordinate system: for <span class="blue">thumb</span>, <span class="green">index</span> and <span class="red">middle</span> finger, with your hand shaped like a gun and the middle finger sticking out.</p>
    </div>

    <div class="step">
      <div class="extra right" data-align-x="1.15" data-hold="1">
        <big><big>$$ 
          \begin{bmatrix}
            \class{blue}{a} &amp; \class{green}{d} &amp; \class{red gone}{g}  \\
           \class{blue}{b} &amp; \class{green}{e} &amp; \class{red gone}{h} \\
           \class{blue}{c} &amp; \class{green}{f} &amp; \class{red gone}{i}
          \end{bmatrix}
          \\[32pt]
          \class{blue}{\vec x} × \class{green}{\vec y} = \class{orangered}{\vec z}
        $$</big></big>
      </div>
      
      <p>So long as we agree on a common style of coordinate system, for example <em>right-handed</em>, we don't need to track <span class="red">$ \vec z $</span>. We can recover it from <span class="blue">$ \vec x $</span> and <span class="green">$ \vec y $</span> using something called the vector <em>cross product</em>. The vector that comes out will always be perpendicular to the two we pass in, decided by a left- or right-hand rule. This is by the way how you can aim a camera in 3D: all you need is a <span class="blue">target</span>, and an <span class="green">up</span> vector.</p>
    </div>

    <div class="step">
      <p>
        We're down to 6 numbers. But there's more. A rotation preserves length, so the basis must always stay the same size. All the vectors must have length 1—be <em>normalized</em>—and hence move on the surface of a sphere.
      </p>
    </div>

    <div class="step">
      <div class="extra right" data-align-x="1">
        <big><big>$$ 
          (\class{purple}{\phi}, \class{slate}{\theta})
        $$</big></big>
      </div>
      <p>
        Instead of 3 coordinates, we can remember <span class="blue">$ \vec x $</span> as two angles: <span class="purple">longitude $ \phi $</span> and <span class="slate">latitude $ \theta $</span>. First we rotate around the Y axis, then around the rotated Z axis. Did we uniquely determine <span class="green">$ \vec y $</span> as well?
      </p>
    </div>

    <div class="step">
      <div class="extra right" data-align-x="1">
      <big><big>$$ 
        (\class{purple}{\phi}, \class{slate}{\theta}, \class{cyan}{\gamma})
      $$</big></big>
      </div>
      <p>
        No, there is a <span class="cyan">third degree of freedom</span> we haven't been using so far. In order to account for all the places where <span class="green">$ \vec y $</span> can be, we need to allow rotation around <span class="blue">$ \vec x $</span>, by another angle <span class="cyan">$ \gamma $</span>. Now we can describe any orientation in 3D using just <em>three numbers</em>, the so called <em>Euler angles</em>.
      </p>
    </div>
    
    <div class="step">
      <p>
        This is a <em>YZX gyroscope</em>, after the order of rotations used. We can build one in real life by using concentric rings connected by axles. Make one large enough to put a chair in the middle, and you've got an amusement ride—or something to train pilots with. When we rotate the object inside, we rotate the rings, decomposing the rotation into 3 <em>separate perpendicular ones</em>.
      </p>
    </div>

    <div class="step">
      <p>
        If we animate the individual angles smoothly, like here, we seem to get a smooth orientation. What's the problem? Well, we need to study the gyroscope a bit more.
      </p>
    </div>

    <div class="step">
      <p>
        Let's go back to neutral, setting all angles to 0. You can see the <span class="green">Y</span><span class="red">Z</span><span class="blue">X</span> nature of the gyroscope, if you follow the axles from the outside in.
      </p>
    </div>

    <div class="step">
      <p>
        We rotate the <span class="purple">first ring</span> by 90° and look at the axles again. Now they go <span class="green">Y</span><span class="blue">X</span><span class="red">Z</span>. We've swapped the last two.
      </p>
    </div>

    <div class="step">
      <p>
        If we rotate the <span class="slate">second ring</span> by 90°, the axles change again. They've moved to <span class="green">Y</span><span class="blue">X</span><span class="green">Y</span>. This means changing the order or nature of the axles doesn't change the gyroscope, it just rotates all or part of it. That is, unless you make the very useless <span class="green">YYY</span> gyroscope. All <em>functional</em> gyroscopes are identical. Whatever we discover for one applies to all.
      </p>
    </div>

    <div class="step">
      <p>
        This configuration is special however. The axles for the <span class="purple">first</span> and <span class="cyan">third rings</span> are aligned. This is called <em>gimbal lock</em>, though no ring actually locks. If we apply an equal-but-opposite rotation to both, the apple doesn't move. From any of these configurations, we can only rotate <em>two ways</em>, not <em>three</em>. It shows Euler angles do not divide rotations equally.
      </p>
    </div>

    <div class="step">
      <p>
        If we now rotate the <span class="slate">inner ring</span> by 90°, all rings have been changed 90° from their initial position. Same for the apple: its final orientation happens to be rotated -90° around the <span class="red">Z</span> axis.
      </p>
    </div>

    <div class="step">
      <p>
        Which means if we rotate the <em>entire gyroscope</em> by 90° around <span class="red">Z</span>, the apple returns to its original orientation. This is what we'd <em>like</em> to see if we simultaneously rotated the three rings of the gyroscope back to zero.
      </p>
    </div>

    <div class="step">
      <p>
        That's not the case however. We try to hold the apple in place, by rotating back the gyroscope as we rotate back all three rings at the same time. The rotations don't cancel out cleanly and the apple wobbles. We'll need to create an <em>angle map</em>, similar to the <em>distance map</em> for splines before. Only now we need to equalize three numbers at the same time.
      </p>
    </div>

    <div class="step">
      <p>
        Another telling sign is when we rotate all rings by 180°: the start and end orientation is the same. Yet the apple performs a complicated pirouette in between. Just like with circular easing, we'll need a way to identify <em>equivalent orientations</em> and rotate to the nearest one.
      </p>
    </div>

    <div class="step">
      <p>
        To see why this is happening, we can rotate the apple around <span class="royal">a diagonal axis</span>. You can do this with a real gyroscope just by turning the object in the middle. The three rings—and hence the Euler angles—undergo a complicated dance. The two <span class="purple">outer</span> <span class="slate">rings</span> wobble back and forth rather than completing a full turn. Charting a straight course through <em>rotation space</em> is not obvious.
      </p>
    </div>

    <div class="step">
      <p>
        In summary: trying to decompose rotations is messy and leads to gimbal lock. We're going to build a different model altogether, using what we just learnt.
      </p>
    </div>

    <div class="step">
      <p>
        First, we make an arbitrary rotation matrix by doing a random <span class="blue">X</span> rotation followed by a (local) <span class="green">Y</span> rotation and a (local) <span class="red">Z</span> rotation. This is like using an <span class="blue">X</span><span class="green">Y</span><span class="red">Z</span> gyroscope.
      </p>
    </div>

    <div class="step">
      <p>
        We can apply the same rotations again, acting like a nested <span class="blue">X</span><span class="green">Y</span><span class="red">Z</span><span class="blue">X</span><span class="green">Y</span><span class="red">Z</span> gyroscope. Because the gyroscope is made of two equal parts in series, we've rotated twice as far.
      </p>
    </div>

    <div class="step">
      <p>
        Three points uniquely define a circle. So we can trace an arc for each of <span class="blue">the</span> <span class="green">basis</span> <span class="red">vectors</span>. These arcs are not part of the same circle, but they do lie parallel to each other. They turn around a common axis of rotation.
      </p>
    </div>

    <div class="step">
      <div class="extra left" data-align-x="1.4">
      <big>$$ 
        \class{cyan}{\vec a} = \class{blue}{\vec x_1} - \class{blue}{\vec x_0} \\
        \class{cyan}{\vec b} = \class{blue}{\vec x_2} - \class{blue}{\vec x_1} \\
        \class{purple}{\vec c} = \class{cyan}{\vec a} × \class{cyan}{\vec b} \\
        \class{slate}{\phi} = \arcsin \frac{|\class{purple}{\vec c}|}{|\class{cyan}{\vec a}| \cdot |\class{cyan}{\vec b}|}
      $$</big>
      </div>
      <p>
        We can find this <span class="purple">common axis</span> from any of the arcs. We take the <em>cross product</em> of the <span class="cyan">forward differences</span>. If we divide by the lengths of the differences, the <span class="purple">cross product</span>'s length tells us about the <span class="slate">angle of rotation</span>. We apply an arcsine to get an angle in <em>radians</em>. This is the <em>axis-angle representation</em> of rotations. Note that the axis is <em>oriented</em> to distinguish clockwise from counter-clockwise, here using the right hand rule.
      </p>
    </div>

    <div class="step">
      <p>
        We can do this for any rotation matrix, for any set of Euler angles. It tells us we can rotate from neutral to any orientation by doing <em>a single rotation around a specific axis</em>. Now we have three better numbers to describe orientations: $ \class{purple}{(x, y, z)} $. They don't privilege any particular rotation axis, as both their direction and length can change equally in all 3 dimensions. We can pre-apply the arcsine: we make the vector's length directly equal rotation angle, <em>linearizing</em> it.
      </p>
    </div>

    <div class="step">
      <p>
    We can also identify equivalent angles: if we rotate more than 180° one way, that's equivalent to rotating less than 180° the other way. The axis can <span class="purple">flip around</span> when its length reaches <em>$ π $ radians</em> (180°) without any disruption. We can restrict axis-angle to a <em>ball of radius $ π $</em>.
      </p>
    </div>

    <div class="step">
      <p>
        If we <span class="slate">interpolate linearly</span> to a different $ \class{purple}{(x, y, z)} $, we get a smooth animation, but there's some wobble. It also goes the long way through the sphere. There's a much shorter way.
      </p>
    </div>

    <div class="step">
      <p>
        We can flip $ \class{purple}{(x, y, z)} $ and then <span class="slate">interpolate back</span>, to get a more direct rotation. The wobble remains though: there's a subtle change in direction at the start and end. Hence, axis-angle cannot be used directly to rotate between any two orientations in a single smooth motion.
      </p>
    </div>

    <div class="step">
      <div class="extra left" data-delay="2" data-align-x="1.2">
        <big>$$
      \begin{bmatrix}
        \class{blue}{a_1} &amp; \class{green}{d_1} &amp; \class{orangered}{g_1}  \\
       \class{blue}{b_1} &amp; \class{green}{e_1} &amp; \class{orangered}{h_1} \\
       \class{blue}{c_1} &amp; \class{green}{f_1} &amp; \class{orangered}{i_1}
      \end{bmatrix}
      $$</big>
      </div>
      <div class="extra right" data-delay="2" data-align-x="1.2">
        <big>$$
      \begin{bmatrix}
        \class{slate}{a_2} &amp; \class{cyan}{d_2} &amp; \class{purple}{g_2}  \\
       \class{slate}{b_2} &amp; \class{cyan}{e_2} &amp; \class{purple}{h_2} \\
       \class{slate}{c_2} &amp; \class{cyan}{f_2} &amp; \class{purple}{i_2}
      \end{bmatrix}
      $$</big>
      </div>
      <p>
        If we have two random rotation matrices $ [\class{blue}{\vec x_1} \,\,\, \class{green}{\vec y_1} \,\,\, \class{orangered}{\vec z_1}] $ and $ [\class{slate}{\vec x_2} \,\,\, \class{cyan}{\vec y_2} \,\,\, \class{purple}{\vec z_2}] $, how can we find the axis-angle rotation that turns one directly onto the other?
      </p>
    </div>

    <div class="step">
      <div class="extra left" data-delay="2" data-align-x="1.2">
        <big>$$
      \begin{bmatrix}
        \class{blue}{a_1} &amp; \class{blue}{b_1} &amp; \class{blue}{c_1}  \\
        \class{green}{d_1}&amp; \class{green}{e_1} &amp;\class{green}{f_1}  \\
        \class{orangered}{g_1} &amp; \class{orangered}{h_1}  &amp; \class{orangered}{i_1}
      \end{bmatrix}
      $$</big>
      </div>
      <p>
        We have to <em>invert</em> the first matrix to turn the other way. We could convert it to axis-angle and then reverse the angle. But it turns out that's the same as swapping rows and columns. The latter is obviously a lot less work, but it only works because <span class="blue">the</span> <span class="green">three</span> <span class="red">vectors</span> are perpendicular and have length 1. We end up with a matrix that rotates the same amount around the same axis, but in the other direction. For other kinds of matrices, inversion is trickier.
      </p>
    </div>

    <div class="step">
      <div class="extra left edge" data-delay="2">
        $$
      \begin{bmatrix}
        \class{blue}{a_1} &amp; \class{blue}{b_1} &amp; \class{blue}{c_1}  \\
        \class{green}{d_1}&amp; \class{green}{e_1} &amp;\class{green}{f_1}  \\
        \class{orangered}{g_1} &amp; \class{orangered}{h_1}  &amp; \class{orangered}{i_1}
      \end{bmatrix}
      \cdot
      \begin{bmatrix}
       \class{blue}{a_1} &amp; \class{green}{d_1} &amp; \class{orangered}{g_1}  \\
      \class{blue}{b_1} &amp; \class{green}{e_1} &amp; \class{orangered}{h_1} \\
      \class{blue}{c_1} &amp; \class{green}{f_1} &amp; \class{orangered}{i_1}
      \end{bmatrix}
      $$
      </div>
      <div class="extra right edge" data-delay="2">
        $$
      \begin{bmatrix}
        \class{blue}{a_1} &amp; \class{blue}{b_1} &amp; \class{blue}{c_1}  \\
        \class{green}{d_1}&amp; \class{green}{e_1} &amp;\class{green}{f_1}  \\
        \class{orangered}{g_1} &amp; \class{orangered}{h_1}  &amp; \class{orangered}{i_1}
      \end{bmatrix}
      \cdot
      \begin{bmatrix}
        \class{slate}{a_2} &amp; \class{cyan}{d_2} &amp; \class{purple}{g_2}  \\
        \class{slate}{b_2} &amp; \class{cyan}{e_2} &amp; \class{purple}{h_2} \\
        \class{slate}{c_2} &amp; \class{cyan}{f_2} &amp; \class{purple}{i_2}
      \end{bmatrix}
      $$
      </div>
      <p>
        To apply the inverted rotation, we do a <em>matrix-matrix multiplication</em>, which is a fancy way of saying we use it to rotate the other matrix's  basis vectors $ [\class{slate}{\vec x_2} \,\,\, \class{cyan}{\vec y_2} \,\,\, \class{purple}{\vec z_2}] $ individually. When applied to the first basis $ [\class{blue}{\vec x_1} \,\,\, \class{green}{\vec y_1} \,\,\, \class{orangered}{\vec z_1}] $, it rotates back to neutral as expected, aligned with the XYZ axes. 
      </p>
    </div>

    <div class="step">
      <p>
        We can now convert the relative rotation matrix into <span class="royal">axis-angle</span> again. This is the rotation straight from A to B, without any wobble or variable speed. This method is quite involved, and hence is still just a stepping stone towards rotational bliss.
      </p>
    </div>

    <div class="step">
      <p>
        We go back to our <em>axis-angle sphere</em> and apply this rotation, while measuring the <span class="cyan">total axis-angle</span> every step along the way. We can see the cause of the earlier wobble: when moving straight through <em>rotation space</em>, we need to follow a <span class="cyan">curved arc</span> rather than a straight line. As <span class="purple">both rotations</span> are the same length, this arc follows the surface of the sphere.
      </p>
    </div>

    <div class="step">
      <p>
        To get a better feel for how this works, let's move <span class="purple">the other end</span> around. We change it to various rotations of $ \frac{π}{2} $ radians (90°). These are all the rotations on a sphere of radius $ \frac{π}{2} $. Both the <span class="cyan">arc</span> and <span class="royal">axis of rotation</span> change in mysterious ways. The arc snaps back and forth, crossing through the edge of the sphere if that's shorter. 
      </p>
    </div>
    
    <div class="step">
      <p>
        What this really means is that <em>angle space</em> itself is <em>curved</em>. Think of the <span class="blue">surface of the earth</span>: if we keep going long enough in any particular direction, we always get back to where we started. As a consequence, you can't flatten an orange peel without tearing it, and you can't make a flat map of the Earth without distorting the shapes and area unequally. Yet we can view such a <span class="blue">curved 2D space</span> easily in 3D: it's just the <em>surface of a sphere</em>.
        </p>
    </div>

    <div class="step">
      <p>
        The same applies here, except not just on the surface, but also <em>inside it</em>. Each <span class="cyan">curved arc</span> is actually straight as far as rotation is concerned, and each <span class="slate">straight interpolation</span> is actually curved. The inside of this ball is <span class="blue">curved 3D space</span>.
      </p>
    </div>

    <div class="step">
      <p>
        If we want to see curved 3D space without distorting it, we need to view it in <em>four dimensions</em>. This ball is the <em>hypersurface of a 4D hypersphere</em>. So 3D rotation is four dimensional. WTF?
      </p>
    </div>

    <div class="step">
      <p>
        Math is boring. Let's blow up the Death Star.
      </p>
    </div>

    <div class="step">
      <p>
        <img src="/files/animating/portrait1.jpg" width="140" height="140" class="r ml1 natural">
        The Emperor has made a critical error and the time for our attack has come. The data brought to us by the Bothan spies pinpoints the exact location of the Emperor's new battle station. We also know that the weapon systems of this Death Star are not yet operational. Many Bothans died to bring us this information. Admiral Ackbar, please.
      </p>
    </div>

    <div class="step">
      <p>
        <img src="/files/animating/portrait2.jpg" width="140" height="140" class="r ml1 natural">
        Although the weapon systems on this Death Star are not yet operational, the Death Star does have a strong defense mechanism. It is protected by an energy shield, which is generated from the nearby forest Moon of Endor. Once the shield is down, our cruisers will create a perimeter, while the fighters fly into the superstructure and attempt to knock out the main reactor.
      </p>
    </div>

    <div class="step">
      <p>
        <img src="/files/animating/portrait3.jpg" width="140" height="140" class="r ml1 natural">
        Sir, I'm getting a report from Endor! A pack of rabid teddy bears has attacked the generator, tearing the equipment to shreds. The shield is failing…
      </p>
    </div>    

    <div class="step">
      <p>
        <img src="/files/animating/portrait2.jpg" width="140" height="140" class="r ml1 natural">
        The Death Star is completely vulnerable! Report to your ships, we launch immediately. We'll relay your orders on the way.<br><br>
        Lieutenant Hamilton, show me the interior of the superstructure.<br>
        <em>(That's your cue.)</em>
      </p>
    </div>    

    <div class="step">
      <p>
        <img src="/files/animating/portrait2.jpg" width="140" height="140" class="r ml1 natural">
        <em>*Mic screech*</em><br>
        Red Wing, these are your orders. Of the <em>6 access points</em> to the interior, you will fly your X-wings through the <span class="blue">east</span> portal, closest to the superlaser.
      </p>
    </div>    

    <div class="step">
      <p>
        <img src="/files/animating/portrait2.jpg" width="140" height="140" class="r ml1 natural">
        <span>
        There are large passageways leading directly to the central chamber. As these shafts are heavily guarded by fighters, a direct assault is impossible. We will need to avoid patrols by navigating the dense tunnel network that makes up the interior.
        </span>
      </p>
    </div>    

    <div class="step">
      <p>
        <img src="/files/animating/portrait2.jpg" width="140" height="140" class="r ml1 natural">
        The Death Star's inner core is fortified, and all access is restricted. However, one of our operatives has informed us of <span class="orange">a large, unsecured ventilation shaft</span>, still under construction. This is our best chance to get into the core and destroy it. You must reach this target at all costs.<br>
        <em>Tip: Click and drag to see things from a different angle.</em>
      </p>
    </div>    

    <div class="step">
        <div class="quattext">
          <p class="bouncer">
            <img src="/files/animating/portrait2.jpg" width="140" height="140" class="r ml1 natural">
            <span style="display: block; height: 82px;">There are <em>large tunnels circling</em> just underneath the surface. You will fly your fighters into: <em>(Choose one)</em></span>
          </p>
        </div>
        <div class="quatcontrols">
          
        </div>
    </div>    

    <div class="step">
        <div class="quattext">
          <p class="bouncer">
            <img src="/files/animating/portrait2.jpg" width="140" height="140" class="r ml1 natural">
            <span style="display: block; height: 82px;">We will also send a detachment of Y-wings from the <span class="green">north pole</span>. These heavy bombers will <span class="gold">rendezvous</span> with the X-wings, taking the long way around, away from the defensive perimeter.</span>
          </p>
        </div>
        <div class="quatcontrols">
          
        </div>
    </div>    

    <div class="step">
        <p>
          <img src="/files/animating/portrait2.jpg" width="140" height="140" class="r ml1 natural">
          <span style="display: block; height: 82px;">Lieutenant, I have another task for you. Now that the Death Star's shields have conveniently failed, we will launch a probe ahead of our arrival, gathering detailed sensor data of the entire structure.</span>
        </p>
    </div>

    <div class="step">
        <p>
          <img src="/files/animating/portrait2.jpg" width="140" height="140" class="r ml1 natural">
          <span style="display: block; height: 82px;">It will approach directly from the <span class="red">front side</span>. It must pass through each of the <span class="orange">large tunnels</span> circling the Death Star to ensure full sensor coverage.</span>
        </p>
    </div>    
    
    <div class="step">
        <p>
          <img src="/files/animating/portrait2.jpg" width="140" height="140" class="r ml1 natural">
          <span style="display: block; height: 82px;">The probe's energy signal is shielded, but it will not escape detection for long. To minimize our chances of detection, we should complete the survey of the entire Death Star without any overlap.</span>
        </p>
    </div>    

    <div class="step">
        <div class="quattext">
          <p class="bouncer">
            <img src="/files/animating/portrait2.jpg" width="140" height="140" class="r ml1 natural">
            <span style="display: block; height: 82px;">Survey all areas of the Death Star, <em>without entering any tunnel twice</em>.</span>
          </p>
        </div>
        <div class="quatcontrols">
          
        </div>
    </div>    

    <div class="step">
        <p>
          <img src="/files/animating/portrait3.jpg" width="140" height="140" class="r ml1 natural">
          <span style="display: block; max-height: 56px;">The probe is approaching the Death Star…</span>
        </p>
    </div>    

    <div class="step">
        <p>
          <img src="/files/animating/portrait3.jpg" width="140" height="140" class="r ml1 natural">
          <span style="display: block; max-height: 56px;">Scan of the interior progressing. Plotting data now.</span><br>
        </p>
    </div>    

    <div class="step">
        <p>
          Hamilton's head hurts. Who would design such a crazy, tangled thing? Yet as he studies the structure, he notices a remarkable symmetry. <span class="blue">Grouped</span> <span class="green">by</span> <span class="red">color</span>, the tunnels form a swirling vortex around each <em>central axis</em>. Each vortex is surrounded by a great circle. He labels the three groups <span class="blue">$ i $</span>, <span class="green">$ j $</span> and <span class="red">$ k $</span>, as is the convention in this era.
        </p>
    </div>    

    <div class="step">
        <p>
          Yet mysteriously, tunnels always meet at a 90° angle, everywhere: on the central axes, on the circles, even anywhere in between. The colors also maintain their relative orientation at each intersection, including the polarity (<span class="blue">positive</span> or <span class="slate">negative</span>). Amazed, Hamilton starts scribbling down notes. "Cubic grid, twisted through itself? $ i → j → k $?". 
        </p>
    </div>    

    <div class="step">
        <p>
          In fact, he's so mesmerized by the display, he's completely lost track of what's going on. As the hustle and bustle of the starship bridge slowly creeps back into focus, he looks up and–…
        </p>
    </div>

    <div class="step">
        <p>
          <img src="/files/animating/portrait2.jpg" width="140" height="140" class="r ml1 natural">
          <span style="display: block; max-height: 56px;"><big><big>IT'S A TRAP!</big></big></span><br>
        </p>
    </div>    

    <div class="step">
        <p>
          Not to despair. Some lightning gets thrown around, the Emperor is killed, a man finds redemption in death, and the Death Star is destroyed.
        </p>
    </div>
    
    <div class="step">
      <p>That night, after many hours of celebration, the young Lieutenant falls asleep contentedly, and starts dreaming of that maze again. Maybe it was just a flash of inspiration, maybe it was the Force—or maybe the interesting neurochemical effects of fermented Endorian moonberry juice—but a long time ago, in a galaxy far, far away, William Rowan Hamilton figured out <em>quaternions</em>.
        </p>
    </div>

    <div class="step">
      <p>
        More precisely, it was in 1843 in Dublin, Ireland. He was so struck by it, he immediately carved it into the nearest bridge—true story. It shouldn't surprise you that you've been doing quaternion calculations all along: those edges weren't color-coded just to look pretty. They consistently denoted the multiplication by a <span class="blue">+X</span>/<span class="slate">-X</span>, <span class="green">+Y</span>/<span class="cyan">-Y</span> or <span class="red">+Z</span>/<span class="purple">-Z</span> quaternion, representing a particular rotation around that axis.
      </p>
    </div>

    <div class="step">
      <p>
        A key feature is how the colors wrap around the great circles. They always maintain the same relative orientation at every intersection, but the entire arrangement rotates from one place to the next. For example <span class="green">+Y</span>: it goes <em>up</em> at the core, but circles around the equator horizontally. You also saw what the inside looked like, omitted here for sanity.
      </p>
    </div>

    <div class="step">
      <p>
        But we're missing something: a 6th quaternion at every 'pole'. This space continues outward, we've just been ignoring that part of it.
      </p>
    </div>

    <div class="step">
      <p>
        This suggests there is a <span class="gold">second set of 'poles'</span>, at twice the radius. We can travel to and from them by multiplying with a quaternion. In fact that's completely true, but with one catch: all the orange points are actually all <em>one and the same point</em>. Huh?
      </p>
    </div>

    <div class="step">
      <p>
        Remember, we're looking at <em>curved space</em>, a hypersphere. To make sense of it, we need to first look at the 2D case.
      </p>
    </div>

    <div class="step">
      <p>
        If the disc represents a curved <em>plane</em> that was projected down to 2D, then in its undistorted form, it's actually a sphere. All the points on the disc's perimeter are actually the same: here they're the north pole, and the disc's center is the south pole.
      </p>
    </div>

    <div class="step">
      <p>
        In curved <em>space</em>, it works similar. We can't visualize this, because this is happening in every direction all at the same time. We experienced the result of it while navigating the Death Star. What we didn't see was that the entire <span class="orange">sphere of radius 2π</span>—in axis-angle terms—is all just one and the same point. We never bothered to go beyond radius π before: rotations up to 180° in either direction.
      </p>
    </div>

    <div class="step">
      <p>
        The important thing is to realize that the center of our diagram is not the center of the hypersphere, rather it's just another <em>pole</em>. In order to fit a hypersphere into 3D correctly, we'd somehow have to shrink the entire <span class="orange">sphere of radius 2π</span> to a point, to create a new pole, but without passing through the <span class="royal">sphere of radius π</span>. This is impossible, you need an extra dimension to make it work.
      </p>
    </div>

    <div class="step">
      <p>
        But why are 3D rotations and quaternions connected? Why does <em>axis angle</em> map so cleanly to <em>half</em> of a hypersphere in quaternion space? And what does a quaternion actually look like? Well. What other kind of mathematical thing likes to turn? When you multiply it by another one of its kind? Where the rotation angle depends on where both inputs are?
      </p>
    </div>

    <div class="step">
      <div class="extra bottom" data-delay="2">
        <big>$$ \class{blue}{|z| = 1} $$</big>
      </div>
      <p>
        Complex numbers! Yay! If you're not familiar with them however, not to worry. We won't be needing all the complex numbers: we'll only use those that have length 1. In other words, all <span class="blue">points on a circle of radius 1</span>. Much simpler.
      </p>
    </div>

    <div class="step">
      <div class="extra bottom" data-delay="1.5">
        <big>$$ z = \class{royal}{\frac{\sqrt{3}}{2}} + \class{blue}{\frac{1}{2} \cdot i} $$</big>
      </div>
      <p>
        Complex numbers are 2D vectors that lead a double life. Ordinarily, they are written as the sum of two parts. Their <span class="royal">horizontal component</span> is a real number, a multiple of $ \class{royal}{1} $. Their <span class="blue">vertical component</span>, is a so-called imaginary number, a multiple of $ \class{blue}{i} $, which is a square root of -1. Which supposedly does not exist. Lies.
      </p>
    </div>

    <div class="step">
      <div class="extra bottom" data-delay="1.5">
        <big>$$ \class{orange}{z = 1∠30°} $$</big>
      </div>
      <p>
        It is often better to see them as a length and an angle. $ \class{royal}{1} $ becomes $ \class{royal}{1∠0°} $. The number $ \class{blue}{i} $ becomes $ \class{blue}{1∠90°} $. And $ -1 $ becomes $ 1∠180° $ or $ 1∠-180° $.
      </p>
    </div>

    <div class="step">
      <div class="extra bottom" data-delay="2">
        <big>$$
          \begin{array}{rl}
          \class{orange}{z} &amp; = &amp; 1∠30° \cdot 1∠90° \\
            &amp; = &amp; 1∠120° \\
          \end{array}
        $$</big>
      </div>
      <p>
        When we multiply two complex numbers, their lengths multiply, and their angles add up. As the lengths are always 1 in our case, we can ignore them. Here, we multiply $ \class{orange}{1∠30°} $ by $ \class{blue}{1∠90°} $ to turn it 90° counter-clockwise. By the same rule, $ \class{blue}{1∠90°} \cdot \class{blue}{1∠90°} = 1∠180° $, better known as $ \class{blue}{i}^2 = -1 $. Complex numbers like to turn, and this gives them interesting properties, explored elsewhere on this site.
      </p>
    </div>

    <div class="step">
      <p>
        Representing 2D rotation with complex numbers is trivial. We can directly map the rotation angle to the complex number's angle, and we can combine rotations by adding up the angles, positive or negative. The angles 0°, 90°, 180°, 270°, 360° become 1, $ \class{blue}{i} $, -1, $ -\class{blue}{i} $, 1. Of course, this adds nothing useful, at least in 2D.
      </p>
    </div>

    <div class="step">
      <p>
        We can expand the model to 3D though, where we have three perpendicular ways of turning. First we'll try to add a second degree of rotation. We add a new imaginary component $ \class{green}{j} $, representing <span class="green">Y</span> rotation, while $ \class{blue}{i} $ is <span class="blue">X</span> rotation. Any position in this 3D space is now a quaternion, but we're still limiting them to only length 1, only interested in rotation. We'll be using the surface of what is, for now, a sphere.
      </p>
    </div>

    <div class="step">
      <p>
        But wait, this isn't right. According to this diagram, if we rotate 180° around either the <span class="blue">X</span> or <span class="green">Y</span> axis, we end up in the same place—and hence the same orientation. Clearly that's not the case. Yet we based our quaternions on complex numbers, so both $ \class{blue}{i}^2 = -1 $ and $ \class{green}{j}^2 = -1 $.
      </p>
    </div>

    <div class="step">
      <p>
        We can satisfy this condition in a different way though. If we rotate an object by <em>360°</em> around any axis, we always end up back where we started. So we can make this rule work if we agree that a 360° rotation equals a 180° quaternion.
      </p>
    </div>

    <div class="step">
      <p>
        That means each rotation is represented by a quaternion of <em>half its angle</em>. A rotation by 180° becomes a quaternion of 90°, that is $ \class{blue}{i} $ or $ \class{green}{j} $, and each rotation axis takes us to a unique place. As we still treat $ \class{royal}{1} $ as 0°, the quaternion $ \class{blue}{1∠180°} = \class{green}{1∠180°} = -1 $ now represents a rotation of 360° = 0° around any axis. So $ \class{royal}{1} $ and $ -1 $ are considered equivalent, as far as representing rotation goes.
      </p>
    </div>

    <div class="step">
      <p>
         Furthermore, $ \class{blue}{i} $ and $ \class{slate}{-i} $ are equivalent too, and so are $ \class{green}{j} $ and $ \class{cyan}{-j} $. Each represents rotating either +180° or -180° around the corresponding axis, which is the same thing. In fact, any half of this sphere is now equivalent to the other half, when you reflect it around the central point. This is why we were missing half of the hypersphere earlier: the 'outer half' is a mirror image of the 'interior'.
      </p>
    </div>

    <div class="step">
      <p>
         So what about in-between axes? Well, we could try rotating around $ \class{orange}{(1,1,0)} $ and $ \class{gold}{(1,-1,0)} $, which are the axes that lie ±45° rotated between <span class="blue">X</span> and <span class="green">Y</span>. We'd end up tracing circles right between them: this is the only possibility where <span class="orange">both</span> <span class="gold">rotations</span> are perpendicular, yet maintain an equal distance to both the X and Y situation.
      </p>
    </div>

    <div class="step">
      <p>
         Unfortunately we're missing something important. We've only applied rotations from neutral, from $ 1 $. If we apply a 180° <span class="blue">X</span> and <span class="green">Y</span> rotation <em>in series</em>, where do we end up? And what about <span class="green">Y</span> followed by <span class="blue">X</span>? The diagram might suggest we'd end up at respectively $ \class{green}{j} $ and $ \class{blue}{i} $.
      </p>
    </div>

    <div class="step">
      <p>
        But this wouldn't make sense: if $ \class{blue}{i} \cdot \class{green}{j} = \class{green}{j} $, and $ \class{green}{j} \cdot \class{blue}{i} = \class{blue}{i} $, then both $ \class{blue}{i} $ and $ \class{green}{j} $ have to be equal to $ 1 $. There'd be no rotation at all. And if we say that $ \class{blue}{i} \cdot \class{blue}{j} = \class{blue}{j} \cdot \class{blue}{i} = -1 $, then the quaternions $ \class{blue}{i} $ and $ \class{green}{j} $ have the exact same effect. We'd only have <em>one</em> imaginary dimension, not two. Even in math, a difference that makes no difference is no difference.
      </p>
    </div>

    <div class="step">
      <p>
        Whether we want to or not, we have to add a third imaginary component, $ \class{orangered}{k} $ to make this click together. So $ \class{orangered}{k}^2 = - 1 $, but it's different from both $ \class{blue}{i} $ and $ \class{green}{j} $. As we've used up our 3 dimensions, we need to project down this new 4th, putting it at an angle between the others. Again, a $ \class{orangered}{k} $ quaternion represents rotation around the <span class="red">Z</span> axis, with the angle divided by two.
      </p>
    </div>
    
    <div class="step">
      <p>
        We end up with two peculiar relationships: $ \class{blue}{i} \cdot \class{green}{j} = \class{orangered}{k} $ and $ \class{green}{j} \cdot \class{blue}{i} = \class{purple}{-k} $. The quaternion product is <em>not the same</em> when you reverse the factors. Just like an XY gyroscope turns differently than a YX gyroscope. But if we'd started with <span class="orangered">Z</span>/<span class="blue">X</span> or <span class="green">Y</span>/<span class="red">Z</span>, we'd see the exact same thing.
      </p>
    </div>
         
    <div class="step">
      <p>
        Hence we can rotate and combine these rules to get $ \class{blue}{i} \cdot \class{green}{j} \cdot \class{orangered}{k} = -1 $. This is the $ i^2 = -1 $ of quaternions, the magic rule that links together 3 separate imaginary dimensions and a real one, creating a maze of twisty passages all alike. When you cycle the axes, it still works: $ \class{green}{j} \cdot \class{orangered}{k} \cdot \class{blue}{i} = -1 $ and $ \class{orangered}{k} \cdot \class{blue}{i} \cdot \class{green}{j} = -1 $, demonstrating that quaternions link together three imaginary axes into a cyclic whole.
      </p>
    </div>

    <div class="step">
      <div class="extra bottom">
        <big>$$
          |\class{blue}{z}| = 1 \\
          z = \class{royal}{\cos(\theta)} + \class{blue}{i \cdot \sin(\theta)}
        $$</big>
      </div>
      <p>
        So how do we actually use quaternions for rotation? It's quite easy, because they are literally complex numbers whose imaginary component has sprouted two extra dimensions. Compare with an ordinary <span class="blue">complex number</span> on the unit circle. Its length (1) is divided non-linearly over the horizontal and vertical component using the <em>cosine and sine</em>: this is trigonometry 101.
      </p>
    </div>

    <div class="step">
      <div class="extra bottom edge">
        <big>$$
          |q| = 1 \\
          q = \class{royal}{\cos(\frac{\theta}{2})} + (x \cdot \class{blue}{i} + y \cdot \class{green}{j} + z \cdot \class{orangered}{k}) \cdot \sin(\frac{\theta}{2})
        $$</big>
      </div>
      <p>
        For a quaternion on the unit hypersphere, we only make two minor changes. We replace the single $ i $ with a vector $ (x\class{blue}{i}, y\class{green}{j}, z\class{red}{k}) $ where $(x,y,z)$ is the normalized axis of rotation. The cosine and sine stay, though we divide the rotation angle by two. We can visualize the 3 imaginary dimensions directly without projection, after squishing the real dimension to nothing. As the length of the imaginary vector shrinks, the <span class="royal">real component</span> grows to compensate, maintaining length 1 for the entire 4D quaternion.
      </p>
    </div>

    <div class="step">
      <div class="extra top edge">
        <big>$$

          q_1 \cdot q_2 = (w_1 + x_1 \cdot \class{blue}{i} + y_1 \cdot \class{green}{j} + z_1 \cdot \class{orangered}{k}) \cdot (w_2 + x_2 \cdot \class{blue}{i} + y_2 \cdot \class{green}{j} + z_2 \cdot \class{orangered}{k}) \\[10pt]

        $$</big>
      </div>
      <div class="extra bottom edge">
        <big>$$

          \class{blue}{i}^2 = -1 \,\,\,\,\,\,\, \class{green}{j}^2 = -1 \,\,\,\,\,\,\, \class{orangered}{k}^2 = -1 \\
          \class{blue}{i} \cdot \class{green}{j} = \class{orangered}{k} \,\,\,\,\,\,\, \class{green}{j} \cdot \class{orangered}{k} = \class{blue}{i} \,\,\,\,\,\,\, \class{orangered}{k} \cdot \class{blue}{i} = \class{green}{j} \\
          \class{green}{j} \cdot \class{blue}{i} = \class{purple}{-k} \,\,\,\,\,\,\, \class{orangered}{k} \cdot \class{green}{j} = \class{slate}{-i} \,\,\,\,\,\,\, \class{blue}{i} \cdot \class{orangered}{k} = \class{cyan}{-j} \\
          \class{blue}{i} \cdot \class{green}{j} \cdot \class{orangered}{k} = -1

        $$</big>
      </div>
      <p>
        We can apply the rules of quaternion arithmetic to multiply two quaternions. This is equivalent to performing the rotations they represent in series. Just like complex numbers, two length 1 quaternions make another length 1 quaternion. Of course, all the other quaternions have their uses too, but they're not as common. In a graphics context, you can pretty much forget they exist.
      </p>
    </div>
    
    <div class="step">
      <p>
        There's only one question left: how to smoothly move between two quaternions, that is, between two arbitrary orientations. With axis-angle, it was a very complicated procedure. With quaternions, it's super easy, because <em>unit quaternions</em> are shaped like a hypersphere. The 'angle map' is the hypersphere itself.
      </p>
    </div>

    <div class="step">
      <p>
        As it turns out though, a <em>hyperspherical interpolation</em> in 4D is exactly the same as a spherical one in 3D. So we really only need to understand the 3D case. We have a linear interpolation between <span class="orangered">two</span> <span class="green">points</span> on a sphere, and want to replace it with a spherical arc.
      </p>
    </div>

    <div class="step">
      <p>
        The line and the <span class="cyan">arc</span> share the same <span class="slate">plane</span>: the one that contains <span class="green">both</span> <span class="orangered">points</span> and the center of the sphere. Any such plane cuts the sphere into two equal halves, along an equator-sized <em>great circle</em>. Hence the arc is just an inflated version of the line, with a circular bulge applied in the plane, following the sphere's radius along the way. But we also have to traverse the arc at constant speed: otherwise we'd end up creating an uneven spline-like curve again.
      </p>
    </div>

    <div class="step">
      <div class="extra top edge">
        <big>$$ \class{orange}{\theta} = \arccos(\class{green}{x_1} \cdot \class{red}{x_2} + \class{green}{y_1} \cdot \class{red}{y_2} + \class{green}{z_1} \cdot \class{red}{z_2}) $$</big>
      </div>
      <div class="extra bottom edge">
        <big>$$ \class{cyan}{slerp}(\class{green}{\vec v_1}, \class{red}{\vec v_2}, f) = \frac{\sin((1-f) \cdot \class{orange}{\theta})}{\sin \class{orange}{\theta}} \cdot \class{green}{\vec v_1} + \frac{\sin(f \cdot \class{orange}{\theta})}{\sin \class{orange}{\theta}} \cdot \class{red}{\vec v_2} $$</big>
      </div>
      <p>
        Luckily, we can apply a little trigonometry again. We can use the 3D (4D) vector dot product to find the <span class="orange">angle</span> between two unit vectors (quaternions), after applying an arccosine. Then we weigh the two vectors (quaternions) appropriately so they sum to length 1 and move linearly with the arc length. This is the <em>slerp</em>, the spherical linear interpolation. Working it out yourself can be tricky, but the result is elegant and independent of the number of dimensions.
      </p>
    </div>

    <div class="step">
      <p>
        With all that in place, we can track any orientation with just 4 numbers, and change them linearly and smoothly. Quaternions are like a better axis-angle representation, which simply does the right thing all the time. Of course, you could just look up the formulas and cargo-cult your way through this problem. But it's more fun when you know what's actually going on in there. Even if it's in 4D.
      </p>
    </div>
    
  </div>

</div>

<div class="g8 i2"><div class="pad">

<p>So that's quaternions, the magical rotation vectors. Every serious 3D engine supports them, and if you've played any sort of 3D game within the last 15 years, you were most likely controlling a quaternion camera. Whether it's head-tracking on an Oculus Rift, or the attitude control of a spacecraft (a real one), these problems become much simpler when we give up our silly three dimensional notions and accept 3D rotation as the four dimensional curly beast that it is.</p>

<p>Ultimately though, quaternions can be treated as just vectors with a special difference and interpolation operator. We can apply all our usual linear filtering tricks, and can create sophisticated motions on the hypersphere. Combine that with smooth path-based animation with controllable velocities, and you have everything you need to build carefully tracked shots from any angle.</p>

<h2>Meaning from Motion</h2>

<p>It's useful to see where we actually are with animation tools, especially on the web. Unfortunately, it doesn't look that great. For most, animation means calling an <code>.animate()</code> method or defining a transition in CSS with a generic easing curve, fire-and-forget style. Keyframe animation is restricted to clunky CSS Animations, where we only get a single cubic bezier curve to control motion. We can't bounce, can't use real friction, can't set velocities or apply forces. We can't blend animations or use feedback. By now you know how ridiculously limiting this is.</p>

<p>In an ideal world, we'd have a perfect animation framework with all the goodies, which runs natively and handles all the math for us while still giving direct control. Until then, consider inserting a little bit of custom animation magic from time to time. Writing a simple animation loop is easy, and offers you fine grained control. Upgrades can be added later when the need presents itself. Your audience might not notice directly, but you can be sure they will remark on how pleasant it is to use, when everything seems alive.</p>

<p>But <em>buyer beware</em>: we need to be thinking as much about what isn't changing, as what is. Just like we use grids and alignment to keep layouts tidy, so should we use animation consistently to bring a rhythm and voice to our interactions.</p>

<p>In what you just saw, little was left to chance. Color, size, proportion, direction, orientation, speed, timing… they're used consistently throughout, there to reinforce the connections that are expressed. I try to hash ideas into memorable qualia, while avoiding optical illusions or accidental resemblances. If it's not the same, it should look, act or speak differently. Even if it's just a <span class="slate">slightly</span> <span class="royal">different</span> <span class="cyan">shade</span> of <span class="blue">blue</span>, or a 300ms difference in timing.</p>

<p>Though MathBox is a simple script-based animator (for now), it exposes some interesting knobs to play with and can handle arbitrary motion through custom expressions. It also supports slowable time and maintains per-slide clocks. If you map bullet time to a remote control, you can manipulate time mid-sentence: you don't need to follow your slides, your slides follow you. It feels ridiculously empowering when you're doing it. When properly applied, you can build up a huge amount of context and carry it along for extended periods of time at the forefront of people's attention.</p>

<p>Many of these slides are based on live systems that run in the background, advancing at their own pace. The entire diagram reflows, maintaining the mathematical laws and relations that are represented. Often, I use periodic or pseudo-random motion to animate the source data. While it may just seem like a cool trick at first, I think it's actually the main feature. It changes every slide from one example into many different ones. It shows them one after the other, streaming into your brain at 60 frames per second. It maximizes the use of visual bandwidth, yet cause and effect can still be read directly from any freeze frame.</p>

<p>Additionally, looping creates a continuous reinforcement of the depicted mechanisms. In my experience, our intuition can absorb math this way through mere exposure, slowly internalizing models of abstract spaces, as well as the relations and algorithms that operate within. Even if we don't notice right away, it anchors our understanding for later when it's finally expressed formally and verbally.</p>

<p>That's the theory anyway: put the murder weapon on the mantelpiece in the opening scene, and work your way towards revealing it. Only the weapon could be complex exponentials, and the mantelpiece the <a href="/blog/how-to-fold-a-julia-fractal/">real number line</a>. I'm not an education specialist or neuroscientist, though I did devour <a href="http://en.wikipedia.org/wiki/David_Marr_(neuroscientist)">David Marr</a>'s seminal work on the human visual system. I just know that whenever I manage to pour a complicated concept into a concise and correct visual representation, it instantly begins to make more sense.</p>

<p>Bret Victor has talked about media as thinking tools, about needing a better way to examine and build dynamical systems, a <a href="http://worrydream.com/MediaForThinkingTheUnthinkable/">new medium for thinking the unthinkable</a>. In that context, MathBox is an attempt at <em>viewing the unviewable</em>. To borrow a term, it's <a href="http://www.youtube.com/watch?v=4niz8TfY794#t=20m38s">qualiscopic</a>. It's about creating graphical representations that obey certain laws, and in doing so, making abstract things tangible. It encourages you to smoothly explore the space of all possible diagrams, and find paths that are meaningful.</p>

<p>By carefully crafting living dioramas of space and time this way, we can honestly say: nothing will appear, change or disappear without explanation.</p>

<p class="m3"><em>Comments, feedback and corrections are welcome on <a href="https://plus.google.com/112457107445031703644/posts/bxLbxsAu161">Google Plus</a>. Diagrams powered by <a href="/blog/making-mathbox/">MathBox</a>.</em></p>

</div></div>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Animate Your Way to Glory]]></title>
    <link href="http://acko.net/blog/animate-your-way-to-glory/"/>
    <updated>2013-09-13T00:00:00+02:00</updated>
    <id>http://acko.net/blog/animate-your-way-to-glory</id>
    <content type="html"><![CDATA[<script type="text/x-mathjax-config">
MathJax.Hub.Config({
  "HTML-CSS": { availableFonts: ["TeX"] },
  extensions: ["tex2jax.js"],
  jax: ["input/TeX","output/HTML-CSS"],
  tex2jax: {inlineMath: [["$","$"],["\\(","\\)"]]},
});
</script>

<script type="text/javascript"
  src="https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS_HTML.js">
</script>

<script type="text/javascript">
// <!--
window.MathJax && MathJax.Hub.Queue(["Typeset",MathJax.Hub]);
Acko.queue(function () { Acko.Fallback.warnWebGL(); });
// -->
</script>

<div class="g8 i2"><div class="pad">

<h2 class="sub">Math and Physics in Motion</h2>

</div></div>

<div class="c"></div>

<div class="g12"><div class="pad">

<blockquote class="m2 tc">
  <em class="bigger">“The last time that I stood here was seven years ago.”<br>
  “Seven years ago! How little do you mortals understand time.<br>Must you be so linear, Jean-Luc?”</em>
  <div class="tr m1">– Picard and Q, <a href="http://en.memory-alpha.org/wiki/All_Good_Things..._(episode)">All Good Things</a>, Star Trek: The Next Generation</div>
</blockquote>

</div></div>

<div class="g10 i1 m2"><div class="pad">

<p><em>Note: This article is significantly longer than previous instalments. It features 4 interactive slideshows, each introducing a new tool as well as related concepts around it. In one way, it's just another math guide, but going much deeper. In another, it's a thesis on everything I know about animating. Their intersection is a handbook for anyone who wants to make things move with code, but I hope it's an interesting read even if that's not your goal.</em></p>

</div></div>

<div class="g8 i2 m2"><div class="pad">

<p>Developers have a tough job these days. A seamless experience is mandatory, polish is expected. On touch devices, they are expected to become magicians. The trick is to make an electronic screen look and feel like something you can physically manipulate. Animation is the key to all of this.</p>

<p>Not just any animation though. Flash intros were hated for a reason. The <code>&lt;blink&gt;</code> tag is not your friend, and flashing banner ads only annoy rather than invite. If elaborately designed effects distract from the content, or worse, ruin smoothness and performance, it'll turn people off rather than endear. Animation can only add value when its fast and fluid enough to be responsive.</p>

<p>It's not mere polish either, a finishing touch. Animation–and UI in general—should always be an additional conversation with the user, not a representation of internal software or hardware state. When we press Play in a streaming music app, the app should respond immediately by showing a Pause control, even if the music won't actually start playing for another second. When we enable Airplane Mode on our phones, we don't care that it'll take a few seconds to say good bye to the cell tower and turn off the radio. The UI is there to respond to our wishes: it should act like a personal assistant, not a reluctant helper, or worse, a demanding master.</p>

</div></div>

<div class="c"></div>

<aside class="g5 mt2 tc l"><div class="pad">
  <img src="https://acko.net/files/animating/genie.jpg" alt="Apple Genie Effect">
  <p>The OS X 'genie' effect. Ridiculed, but it leaves no question where the window went.</p>
</div></aside>

<div class="g7 r mt1"><div class="pad">

<p>Hence animation is visual language and communicates both explicitly and implicitly. It establishes an unspoken trust and confidence between designer and user: we promise nothing will appear, change or disappear without explanation. It can show where to find things, like an application that minimizes into place in the dock, or a picture sliding into a thumbnail strip. It can tell miniature stories, like a Download button turning into a progress bar turning into a checkmark. More simply just the act of scrolling around a live document, creating the illusion of viewing an infinite canvas, persisting in space and time. Here, page layout is the use of placement and style to denote hierarchy and meaning in a 2D space.</p>

<p>As with any conversation, tone matters, in this case expressed through choreography. Items can fade into the background or pop to demand our attention, expressing calm or assertiveness. Elements can complement or oppose, creating harmony or dissonance. Animations can be minimalist or baroque, ordered or parallel, independent or layered. The proper term for this is <em>staging</em>, and research shows that it can significantly increase our understanding of diagrams and graphs when applied carefully. Whenever elements transition, preferably one at a time, it is easier to gauge changes in color, size, shape and position than when we are only shown a before and after shot.</p>

<p>This is important everywhere, but especially so for abstract topics like data visualization and mathematics. When we have no natural mental model of something, we build our understanding based on the interface we use to examine it. The more those interfaces act like real objects, the less surprising they are.</p>

<p>In doing so, we replace explicit explanations with implicit metaphors from the natural world: distance, direction, scale, shadow, color, contrast. These are the cues our brains evolved to be excellent at interpreting. By imbuing virtual objects with these properties, we make them more realistic and thus more understandable. Mind you, this is not a call for <em>skeuomorphism</em>, far from it. The properties we are seeking to mimic are far more basic, far more important, than some faux leather and stitching.</p>

</div></div>

<aside class="g5 mt1 tc l cl"><div class="pad">
  <a href="http://bl.ocks.org/mbostock/1062288"><img src="https://acko.net/files/animating/d3.jpg" alt="Apple Genie Effect"></a>
  <p>D3.js Force Directed Graph — <a href="http://bl.ocks.org/mbostock/1062288">Mike Bostock</a></p>
</div></aside>

<aside class="g5 mt1 tc l cl"><div class="pad">
  <img src="https://acko.net/files/animating/padd.jpg" alt="PADD">
  <p>Star Trek TNG PADD, aka the iPad. Arrived slightly before the 2360s.</p>
</div></aside>
  
<div class="c"></div>

<div class="g8 i2"><div class="pad">

<p>The clearest example of this has to be inertial scrolling. Compared to an ordinary mouse wheel, scrolling on a tablet is actually much more complicated. We can flick and grab, go as fast or slow as we want. When skimming through a list, often we never wait for the page to stop moving, in theory requiring more effort to read. Yet everyone who's seen a toddler with an iPad can attest to its uncanny ease of use and efficiency, offering improved control and comprehension. Our brains are very good at tracking and manipulating objects in motion, particularly when they obey the laws of physics: moving with <em>consistent inertia and force</em>.</p>

<p>Which brings me to the actual topic of this post: how animation works on a fundamental level. I'd like to teach a mental model based on physics and math, and how to precisely control it. Along the way, we'll come to understand why Apple built a physics engine into iOS 7's UI, reveal some secrets of the demoscene, compose fluid keyframe animations, and defeat the final boss: seamless rotation in 3D. In doing so, we'll also go beyond just visual animation. The techniques described here work equally well for manipulating audio, processing data or driving meatspace devices. In a world of data, animation is just a different word for <em>precise control</em>.</p>

<h2 id="physics">A Matter of Time</h2>

<p>An animation is something that <em>changes over time</em>. As it so happens, these three humble words are a veritable Pandora's box of mathematics. They open up to the strange world of the continuously and infinitely dividable, also known as <em>calculus</em>.</p>

<p>In a <a href="/blog/to-infinity-and-beyond/">previous article</a>, I covered the origins of calculus and how to approach the concept of infinity. In what follows, we won't be needing it much though. We'll be working with finite steps throughout, with <em>discrete</em> time. This makes it vastly easier to understand, and is an eminently useful stepping stone to the true theory of continuous motion, which you can find in any good physics textbook.</p>

<p>Math class hates it when we just punch numbers into our calculator instead of deducing the exact result: a decimal number is meaningless on its own. On that, I can agree. But when we punch in a couple thousand numbers and look at them in aggregate, it can tell us just as much. This page will be your calculator.</p>

</div></div>

<div class="wide slideshow full">

  <div class="iframe c">
    <iframe src="/files/animating/mb-1-ease.html" class="mathbox paged autosize" height="320"></iframe>
  </div>

  <div class="steps">

    <div class="step">
      <p>Let's start where Isaac Newton supposedly did, with an apple.</p>
    </div>

    <div class="step">
      <p>Gravity kicks in. The apple bounces off the ground, losing some energy in the process. After a few bounces, its kinetic energy (speed) and potential energy (height) have both dissipated, and the apple is at rest.</p>
    </div>

    <div class="step">
      <p>But analyzing motion by watching it in real-time is tricky. It's better to visualize time as its own dimension, here horizontal, and look at the entire animation as a whole.</p>
    </div>

    <div class="step">
      <div class="extra top right" data-delay="4"><big><big>$$ \class{blue}{p(t)} $$</big></big></div>
      <p>The apple's position $ \class{blue}{p(t)} $ moves through space and time, along arcs of decreasing height and duration. Once at rest, it continues advancing through time, without moving in space. In common parlance, this is the animation's easing curve.</p>
    </div>

    <div class="step">
      <div class="extra top right" data-delay="2"><big><big>$$ \class{blue}{p_i}, \, t_i $$</big></big></div>
      <p>It's worth pointing out they're not really arcs. This animation consists of individually numbered frames $ i $, switching 60 times per second. While a frame is displayed, the position $ \class{blue}{p_i} $ of the apple is constant. In between its value changes instantly, at times $ t_i $.</p>
    </div>

    <div class="step">
      <p>For convenience's sake, it's reasonable to consider this a curve, approximated by a series of straight lines. After all, that's the illusion that the animation successfully tricks us into seeing. The discrete nature of the curve will let us dissect it more easily. We're interested in the physics of this motion.</p>
    </div>

    <div class="step">
      <div class="extra top left" data-delay="2"><big>$$ \class{green}{v_{i→}} = \frac{\class{blue}{p_{i+1}} - \class{blue}{p_{i}}}{t_{i+1} - t_i} $$</big></div>
      <p>To determine the speed of the apple, we find the slope of a line segment: <span class="purple">vertical divided by horizontal</span>. Dividing distance by time gives us <em>speed</em>, e.g. meters per second. But actually, we're dealing with its cousin <span class="green">velocity</span> which has a direction too. Positive slope means going up, negative slope means going down. This operation is called a <em>forward or backward difference</em>, depending on whether you look forward ($ \class{green}{v_{i→}} $) or backward ($ \class{green}{v_{←i}} $) around a point.</p>
    </div>

    <div class="step">
      <div class="extra top left" data-delay="2"><big>$$ \class{green}{v_{i↓}} = \frac{\class{blue}{p_{i+1}} - \class{blue}{p_{i-1}}}{t_{i+1} - t_{i-1}} $$</big></div>
      <p>Forward differences tell us about what's happening <em>between</em> two adjacent points. We're more interested in what's happening at the points themselves. To fix this, we can take a <em>central difference</em> $ \class{green}{v_{i↓}} $, spanning two frames instead. We now get a good approximation for the slope directly at a point of interest, and thus the <span class="green">velocity</span>.</p>
    </div>

    <div class="step">
      <div class="extra top right" data-delay="4" data-hold="1"><big>$$ \class{blue}{p_i}, \, \class{green}{v_{i↓}} $$</big></div>
      <p>If we apply this procedure along the entire curve, we can graph the apple's <span class="green">velocity</span> over time, in sync with its <span class="blue">position</span>. This is the discrete version of <em>taking the derivative</em> in calculus, or <em>differentiation</em> and shows these two quantities are intimately related.</p>
    </div>

    <div class="step">
      <p>While in the air, the apple's <span class="green">velocity</span> decreases along a straight line, first positive, then negative. On impact, the velocity suddenly reverses, though only to a portion of its previous value. At the top of each arc, the velocity passes through zero, which means the apple essentially hangs motionless in the air for a fraction of a second.</p>
    </div>

    <div class="step">
      <div class="extra top right" data-delay="4" data-hold="1"><big>$$ \class{blue}{p_i}, \, \class{green}{v_{i↓}}, \, \class{orangered}{a_{i↓}} $$</big></div>
      <p>To further analyze this, we can repeat the procedure, and find the slope of the <span class="green">velocity</span>. This is the <em>change in velocity over time</em>, better known as <span class="orangered">acceleration</span>. It can be expressed in <em>meters&nbsp;per&nbsp;second&nbsp;per&nbsp;second</em>, that is, $ m / s^2 $. According to Newton, acceleration is <em>force divided by mass</em>: the heavier something is, the less effect the same force has.</p>
    </div>

    <div class="step">
      <p>What looked like a complicated animation at the <span class="blue">position</span> level is now revealed to be very simple: the apple undergoes a small constant <span class="orangered">acceleration</span> downwards from gravity. It also experiences a short burst of much stronger acceleration upwards whenever it bounces. Once the upward force goes below a critical threshold, the apple stops moving. At the end, gravity is countered by the apple's resistance to being squished, and the net acceleration is zero.</p>
    </div>

    <div class="step">
      <p>Suppose we were given only the <span class="orangered">acceleration</span>, and wanted to reconstruct the animation. Can we do that?</p>
    </div>
    
    <div class="step">
      <div class="extra bottom right" data-delay="2"><big>$$ \class{green}{v_{i+1→}} = \class{green}{v_{i→}} + \class{orangered}{a_{i→}} \cdot (t_{i+1} - t_i) $$</big></div>
      <p>Yep, we just work our way back up. If the <span class="orangered">acceleration</span> represents a difference in <span class="green">velocity</span> over time, then we can track the velocity by adding these differences back, accumulating them one step at a time. Since we divided the differences by time initially, we'll now have to multiply <span class="orangered">each value</span> by the time between frames. Technically we need forward differences ($ \class{orangered}{a_{i→}} $) for this, not central ones ($ \class{orangered}{a_{i↓}} $), but the error will be minor.</p>
    </div>

    <div class="step">
      <div class="extra bottom right" data-delay="6.5"><big>$$ \class{green}{v_{i+1→}} = \sum\limits_{k=0}^i\class{orangered}{a_{k→}} \cdot Δt $$</big></div>
      <p>In calculus, this accumulation process is called <em>integration</em>. In our case, it's a sum ($ \sum $). As we are multiplying the vertical value $ \class{orangered}{a_{k→}} $ by the horizontal time step $ Δt $, each term represents the area of a thin rectangle. By adding up all these signed areas, positive for up and negative for down, we can approximate the <em>integral</em> and get <span class="green">velocity</span> back. Integrals and areas under curves are very closely linked.</p>
    </div>

    <div class="step">
      <div class="extra top right" data-delay="6.5" data-hold="1">
        <big>$$ \class{green}{v_{i+1→}} = \class{green}{v_{0→}} + \sum\limits_{k=0}^i\class{orangered}{a_{k→}} \cdot Δt $$</big>
        <big>$$ \class{blue}{p_{i+1}} = \class{blue}{p_0} + \sum\limits_{k=0}^i\class{green}{v_{k→}} \cdot Δt $$</big>
      </div>
      <p>Similarly, we can integrate <span class="green">velocity</span> into <span class="blue">position</span> by adding up strips of area under the velocity curve, recreating the original bounce. Note that for both sums, we needed to manually specify the starting point. If we didn't set it correctly, the apple would drift, bounce on thin air or penetrate the ground.</p>
    </div>

    <div class="step">
      <p>We've produced real physical behavior from raw forces like gravity. That means we've just described a real physics engine. It's a one-dimensional one, but a physics engine none the less. It implements <em>Euler integration</em>, a fast but generally inaccurate method. In this case, the reconstruction is not perfect due to the earlier mentioned usage of <em>central</em> rather than <em>forward</em> differences.</p>
    </div>

    <div class="step">
      <p>We only need one of the three in order to produce a plausible copy of the other two. That means we can control animations on any of the three levels. If we want full control, we specify <span class="blue">position</span> directly. For simple constrained motions, we can manipulate <span class="green">velocity</span> and integrate once. For full-on physics, we set <span class="orangered">acceleration</span> from physical laws and integrate twice. This is why the Newtonian model of motion is so important.</p>
    </div>

    <div class="step">
      <p>It also reveals smoothness. A smooth animation isn't just continuous in its <span class="blue">path</span>. Its <span class="green">velocity</span> is continuous too, without sudden jumps. In some cases, we'll even want smooth acceleration too. An ordinary bounce effect is shown to involve a large <span class="orangered">acceleration</span>, a sudden <em>jerk</em>. This is a noticeable visual disruption, the kind we generally want to avoid. If you've ever tried to ignore a bouncing icon, you'll know how hard this is.</p>
    </div>

    <div class="step">
      <p>In fact, <span class="slate">jerk</span> is what we call the slope of <span class="orangered">acceleration</span>. That's three derivatives deep, and it's turtles all the way down. The next ones are imaginatively called <em>snap</em>, <em>crackle</em> and <em>pop</em>, though they signify little directly. A large <span class="slate">jerk</span> however implies a sudden, jarring <em>change in force</em>.</p>
    </div>

    <div class="step">
      <div class="extra top right">
        <big>$$ \class{purple}{E_p} = m \cdot g \cdot h $$</big>
      </div>
      <p>There's more physics hiding in plain sight. Earlier on, I mentioned energy: kinetic and potential. The apple's <span class="purple">available potential energy</span> $ \class{purple}{E_p} $ comes from gravity and is proportional to its height $ h $ above the ground, as well as the mass $ m $ and the local strength of gravity $ g $.</p>
    </div>

    <div class="step">
      <div class="extra top right">
        <big>$$ \class{cyan}{E_k} = \frac{1}{2} \cdot m \cdot v^2 $$</big>
      </div>
      <p>The <span class="cyan">kinetic energy</span> $ \class{cyan}{E_k} $ comes from its motion. It's proportional to the <span class="green">velocity</span> <em>squared</em>. That means each additional meter per second makes the previous ones more energetic, adding more kinetic energy the <em>faster it's already going</em>. To explain, we can imagine the force required to stop a moving object. By increasing the speed, you don't just add additional momentum: the impact also takes <em>less time</em>, concentrating it.</p>
    </div>
    
    <div class="step">
      <div class="extra top right">
        <big>$$ \class{purple}{E_p} = m \cdot g \cdot h $$</big>
        <big>$$ \class{cyan}{E_k} = \frac{1}{2} \cdot m \cdot v^2 $$</big>
      </div>
      <p>In a closed system, total momentum is conserved. As we are treating gravity as an outside force, this does not apply. Energy is conserved however. There's a vertical symmetry, where one energy level goes up as the other goes down, and vice versa. So we actually have a fourth level to control physics at: that of <em>energy</em> and <em>potential</em>. With some minor bookkeeping, we can create motion this way, called <em>Hamiltonian mechanics</em>.</p>
    </div>

    <div class="step">
      <div class="extra top right" data-hold="1">
        <big>$$ \class{royal}{E_t} = \class{purple}{E_p} + \class{cyan}{E_k} $$</big>
      </div>
      <p>The <span class="royal">total energy</span>, <span class="purple">potential</span> plus <span class="cyan">kinetic</span>, is perfectly constant between bounces. On impact, a significant amount is lost. Note that the dips towards zero are a side effect of the finite approximation: if the bounce occurs between two frames, the apple appears to slow down for a frame, instantly falling down and bouncing back to where it was one frame earlier. Finite differences are oblivious to this.</p>
    </div>

    <div class="step">
      <p>The energy levels follow a <span class="orangered">decaying exponential curve</span>. This is very typical: exponentials show up whenever a quantity is related to its rate of change. Hamiltonian models are useful for more complicated things like 3D roller coasters, where they allow you to abstract away complex interactions into a few concise relations like this.</p>
    </div>

    <div class="step">
      <p>In simple animation though, we'll generally stick to the direct Newtonian model. We can use it to analyze real use cases. Let's start with a common easing curve, cosine interpolation, used by default in <code>jQuery.animate()</code> and these slides too.</p>
    </div>

    <div class="step">
      <div class="extra right" data-delay="1.5">
        <big>$$ lerp(\class{orangered}{a}, \class{green}{b}, f) = \class{orangered}{a} + (\class{green}{b} - \class{orangered}{a}) \cdot f $$</big>
      </div>
      <p>We animate the apple's position, changing its Y coordinate. In practice, that means we apply <em>linear interpolation</em>, lerping, between the start $ \class{orangered}{a} $ and end $ \class{green}{b} $. We take the starting point and add a fraction $ f $ of the difference $ \class{green}{b} - \class{orangered}{a} $ to it. Half the difference gets us halfway there, and so on. As long as $ f $ is between 0 and 1, we end up somewhere in the middle. When $ f $ reaches 1, the animation is complete.</p>
    </div>

    <div class="step">
      <div class="extra bottom" data-delay="1.5">
        <big>$$ elerp(\class{orangered}{a}, \class{green}{b}, f) = \class{orangered}{a} + (\class{green}{b} - \class{orangered}{a}) \cdot  \class{blue}{ease(f)} $$</big>
        <big>$$ \class{blue}{ease(f)} = 0.5 - 0.5 \cdot \cos πf $$</big>
        <br><br>
      </div>
      <p>The purpose of the easing curve is then to make the animation non-linear, not in space, but in time: in this case, the apple smoothly starts and stops. We can use any curve we like, e.g. half of a cosine wave of period 2. This <em>eased lerp</em> is the basic building block of any animation system.</p>
    </div>

    <div class="step">
      <div class="extra bottom" data-delay="4.5" data-hold="1">
        <big>$$ \class{blue}{p_i}, \, \class{green}{v_{i↓}}, \, \class{orangered}{a_{i↓}} $$</big>
      </div>
      <p>The effect of the easing curve is visible when we take central differences again, and look at <span class="green">velocity</span> and <span class="orangered">acceleration</span>. The acceleration has been divided by 3 to fit. This doesn't seem bad, all three quantities appear to change smoothly. This picture is deceptive though.</p>
    </div>

    <div class="step">
      <p>All curves continue before and after the animation. The smooth cosine ease turns out to be quite jarring in its <span class="orangered">acceleration</span>: it's like flooring the accelerator from standstill then easing off gently. At the halfway point you start braking, more and more until you stop. It's one of the most responsive animations possible that's still smooth at both ends. Smoother easing curves have smoother accelerations, but respond slower.</p>
    </div>

    <div class="step">
      <div class="extra bottom" data-delay="2">
        <big>$$ \class{blue}{ease(f)} = f^2 $$</big>
      </div>
      <p>A simpler example is the <em>half-ease</em>, here achieved with a quadratic curve $ \class{blue}{f^2} $. The <span class="green">velocity</span> is a linearly increasing ramp. The <span class="orangered">acceleration</span> is constant, except for a very large instant deceleration at the end. This is like flooring the accelerator from standstill, holding it down for the duration, and then crashing into a wall—the <em>suicide ease</em>. Due to this, half-easing is typically used for fading transitions, where the object is invisible–or the audio inaudible–at the start or end. </p>
    </div>

    <div class="step">
      <div class="extra bottom" data-delay="4">
        <big>$$
        \class{blue}{ease(f)} = 
        \left\{
        	\begin{array}{ll}
        		f^2  &amp; \mbox{if } f \leq 1 \\
        		2f - 1 &amp; \mbox{if } f > 1
        	\end{array}
        \right.
        
        $$</big>
      </div>
      <p>But we can repurpose it quite easily. By tweaking this at the <span class="green">velocity</span> level, we can maintain a constant speed at the end. This is the <em>slow start</em>, and can be expressed directly as an open-ended easing curve. In this case, we allow $ f $ to exceed 1, and the linear interpolation turns into <em>extrapolation</em> for free, no extra charge. We can scale the curve vertically to change the final speed, and scale it horizontally to control the delay. The slow start (and stop) is used throughout these slides.</p>
    </div>

    <div class="step">
      <div class="extra bottom edge" data-delay="7">
        <big>$$
        \class{blue}{ease(f)} = \frac{1}{4} \cdot (1 - \cos 2πf)
        + \left\{
        	\begin{array}{ll}
        		f^2  &amp; \mbox{if } f \leq 1 \\
        		2f - 1 &amp; \mbox{if } f > 1
        	\end{array}
        \right.
        $$</big>
      </div>
      <p>We can combine curves too. Here, we add a cosine wave to the slow start, creating perhaps the motion of a rising jellyfish. Adding up animations is an easy way to create variations on a theme, used often in the demoscene. The derivatives add straight up too, so all three curves shift up and down by a sine or cosine wave. You can see how a small shift in <span class="blue">position</span> can have a large effect on both <span class="green">velocity</span> and <span class="orangered">acceleration</span>. </p>
    </div>

    <div class="step">
      <p>The next example is a bit different. Any guesses as to what this is? The hint is in the vertical scale, now measured in pixels. This animation moves almost 1000 pixels in just over one second.</p>
    </div>

    <div class="step">
      <p>It's an inertial flick gesture, recorded on Mac OS X. We can plot <span class="green">velocity</span> and <span class="orangered">acceleration</span> again. There's a slight measurement error, visible as noisy ripples on the acceleration, even after smoothing out the data: derivatives are very sensitive to noise. The velocity and acceleration have also been scaled down to fit, as they are both quite large.</p>
    </div>

    <div class="step">
      <p>The <span class="cyan">first part</span> of the curve is not an animation at all: it was tracking the direct motion of my finger. Fingers move very smoothly: the <span class="orangered">acceleration</span> follows a curve up and down. This is more physics: of nerve signals causing muscle fibers to contract and digits to move. This <em>work</em> smoothly converts <em>chemical potential</em> into <em>kinetic energy</em>. The small jump in speed at time 0 is easy to explain: my finger was already moving when it touched the pad.</p>
    </div>

    <div class="step">
      <p>The <span class="cyan">second part</span> is the actual inertial animation. It kicks in as soon as the finger leaves the pad. All three values follow an exponential curve past that point, disregarding the noise. But the important one is <span class="green">velocity</span>: the animation starts with the last known velocity and smoothly decays it to zero. <span class="blue">Where we end up</span> depends on how fast we were going when the finger left the pad.</p>
    </div>

    <div class="step">
      <div class="extra" data-hold="1" data-delay=".5">
        $$ \class{green}{v_{i+1→}} = \class{green}{v_{i→}} \cdot (1 - \class{royal}{f}) $$
      </div>
      <p>Inertial scroll is easiest to control at the <span class="green">velocity</span> level. We can measure the initial velocity by finding the <span class="blue">position</span>'s slope, usually averaged over several frames. We then start at this velocity, but reduce it every frame by a fraction $ \class{royal}{f} $, which is a <span class="royal">coefficient of friction</span>. We don't need to care how far we'll go or how long it'll take: we can just keep animating until the <span class="green">velocity</span> gets close enough to 0.</p>
    </div>

    <div class="step">
      <p>Suppose we do care where we end up. We might be showing a list of items, each 100 pixels tall. It could be good to control the animation so it always stops right at an item. We can't violate the principle of smooth motion, so we can't just change the <span class="blue">position</span> or <span class="green">velocity</span> directly. We have to change the <span class="royal">coefficient of friction</span>.</p>
    </div>

    <div class="step">
      <div class="extra" data-delay="1">
        $$ \class{green}{v_{i→}} = \class{green}{v_{0→}} \cdot (1 - \class{royal}{f})^i $$
      </div>
      <p>As the <span class="green">velocity</span> follows a simple curve, we don't have to track it manually. We can express it over time as a direct relation, based on the initial velocity $ \class{green}{v_{0→}} $. The exponential nature is clear, with the frame number $ i $ appearing as the exponent of a number between 0 and 1.</p>
    </div>

    <div class="step">
      <div class="extra" data-delay="2">
        $$
        \begin{array}{rl}
          \class{blue}{p_{i}} &amp; = \class{blue}{p_0} + \sum\limits_{k=0}^{i} \class{green}{v_{0→} \cdot (1 - f)^k} \cdot Δt \\
                                &amp; = \class{blue}{p_0} + \class{green}{v_{0→}} \cdot Δt \cdot \class{purple}{\sum\limits_{k=0}^{i} (1 - f)^k}
        \end{array}
        $$
      </div>
      <p>The position at frame $ i $ is then the sum of all the <span class="green">previous velocities</span> times the <em>time&nbsp;step&nbsp;$ Δt $</em>, just like before, relative to the initial position $ \class{blue}{p_0} $. As the time step and initial velocity are constant, we can move both outside <span class="purple">the sum</span>.</p>
    </div>

    <div class="step">
      <div class="extra">
        $$
        \begin{array}{rl}
        \class{blue}{p_∞} &amp; = \class{blue}{p_0} + \class{green}{v_{0→}} \cdot Δt \cdot \class{purple}{\sum\limits_{k=0}^{∞} (1 - f)^k} \\
                           &amp; = \class{blue}{p_0} + \frac{\class{green}{v_{0→}} \cdot Δt}{\class{royal}{f}}
        \end{array}
        $$
      </div>
      <p>To find the <span class="blue">final resting position</span>, we theoretically have to continue the animation all the way to infinity. This can be done using a limit. For now, we'll just look up the formula for this infinite sum, a <span class="purple">geometric series</span>. We end up <em>dividing by the <span class="royal">coefficient of friction</span></em>: the lower it is, the further we go after all. If the coefficient were 0, there'd be no friction. We'd divide by zero, because there's no final resting position when you never slow down.</p>
    </div>

    <div class="step">
      <div class="extra" data-delay="1">
        $$
        \class{royal}{f} = \frac{\class{green}{v_{0→}} \cdot Δt}{\class{blue}{Δp}}
        $$
      </div>
      <p>We can invert this relationship to find the <span class="royal">coefficient of friction</span> required to stop at a given target. We just need the initial distance to the target, $ \class{blue}{Δp} $. To apply this in practice, we determine the friction needed to reach the next couple of items, and pick the one which is closest to the default case. The user won't notice the subtle change in friction—the UI will just magically seem better.</p>
    </div>

    <div class="step">
      <p>The simulation works identical in all cases and the velocities are still continuous and exponential, which means: <em>physical</em>. This effect only requires one additional calculation at the start, which makes it all the more strange that developers have come up with increasingly jarring ways to achieve something similar.</p>
    </div>

    <div class="step">
      <p>Now let's try animating in 2D.</p>
    </div>

    <div class="step">
      <div class="extra right">
        $$ x(t) = \sin t $$
        $$ y(t) = \sin t $$
      </div>
      <p>We can move the apple in 2D by animating its X and Y coordinates. Here we animate both in lockstep, using a sine wave: the apple moves diagonally, as X and Y are always equal. By adjusting their relative amplitudes, we can control the angle of motion.</p>
    </div>

    <div class="step">
      <div class="extra right">
        $$ x(t) = \sin t $$
        $$ y(t) = \sin \frac{7}{8}t $$
      </div>
      <p>If we animate X and Y separately, we create arbitrary paths. Here they both follow a sine wave, but with different frequencies. The <span class="blue">resulting path</span> is called a Lissajous curve. The sine waves drift in and out of phase, going from a diagonal to an oval to a circle, and back again.</p>
    </div>

    <div class="step">
      <div class="extra right edge">
        $$
        \class{blue}{\vec p(t)}
        
        =
        
        \begin{bmatrix}
          \class{blue}{p_x(t)} \\
          \class{blue}{p_y(t)}
        \end{bmatrix}
        
        =
        
        \begin{bmatrix}
          \sin t \\
          \sin \frac{7}{8}t
        \end{bmatrix}
        $$
      </div>
      <p>It makes more sense to picture the position as a 2D vector, an arrow. It has both a direction and a length, relative to the origin. While the calculation is equivalent—animating X and Y separately—the vector representation is more natural once we look at the derivatives.</p>
    </div>

    <div class="step">
      <div class="extra left edge" data-align-x=".9" data-delay="2" data-hold="1">
        <big>$$
          \class{green}{\vec v_{i→}} = \frac{\class{blue}{\vec p_{i+1}} - \class{blue}{\vec p_{i}}}{t_{i+1} - t_i}
        $$</big>
      </div>
      <p>What does slope and <span class="green">velocity</span> mean in this context? The same principle applies: we take the <span class="purple">difference in position</span> between two frames, and divide it by the difference in time $ Δt $. In this case, all quantities except time are vectors.</p>
    </div>

    <div class="step">
      <p>As a single frame is very short, the <span class="green">velocity</span> is quite large, and always tangent to the <span class="blue">path</span>. Its length directly represents speed.</p>
    </div>

    <div class="step">
      <p>If we center the velocity vector, it traces out its own <span class="green">Lissajous curve</span>. This one is slightly different and doubles back on itself at regular intervals.</p>
    </div>

    <div class="step">
      <div class="extra left edge" data-align-x=".9" data-delay=".5">
        <big>$$
          \class{orangered}{\vec a_{i→}} = \frac{\class{green}{\vec v_{i+1→}} - \class{green}{\vec v_{i→}}}{t_{i+1} - t_i}
        $$</big>
      </div>
      <p>We can apply finite differences again to dissect <span class="green">velocity</span> into <span class="orangered">acceleration</span>. It follows yet another Lissajous curve, a scaled and rotated version of the <span class="blue">position</span>.</p>
    </div>

    <div class="step">
      <p>Finally, we can disentangle these curves by plotting them out over time. <span class="blue">Position</span>, <span class="green">velocity</span> and <span class="orangered">acceleration</span> dance around each other. Despite its artificial construction, even this motion is physical: it's what happens when you take an object and hang it off independently moving horizontal and vertical springs of different stiffness. With the right visualization, raw physics is quite beautiful in its own right.</p>
    </div>
    
  </div>
  
</div>

<div class="c"></div>

<div class="g8 i2"><div class="pad">
  
<p>We've seen how to examine an animation at multiple levels of change: position, velocity, acceleration. Differences  approximate <em>derivatives</em> and let us to dissect our way down the chain. Accumulators approximate <em>integration</em> and let us construct higher levels from lower ones. Thus we can manipulate an animation at any level. By plugging in correct physical laws or arbitrary formulas, we can produce behavior that is as physical or unphysical as we like.</p>

<h2 id="filters">Customer is King</h2>

<p>Everything we've done so far has been independent animation, without interaction. Even inertial scrolling has this luxury: whenever the user is touching, there is no inertia and the animation system is inactive. It's only when you let go that the surface coasts.</p>

<p>In many cases, this is not enough: animations need to be scheduled and executed while retaining full interactivity. Often the animation needs to continue despite its target changing midway. In order to handle such situations, we need to build adaptive models that remain continuous and smooth, no matter what.</p>

<p>We'll also need to drop the assumption that the frame rate—the time step—is constant. In the real world, the frame rate might drop here or there, or be variable altogether. In either case, we'd prefer it if the effect of this was minimal. If we're adding music to an animation, this is essential to prevent desynchronization. It will also have some nasty consequences for our physics engine, and we need to level it up significantly.</p>

</div></div>

<div class="wide slideshow full">

  <div class="iframe c">
    <iframe src="/files/animating/mb-2-adaptive.html" class="mathbox paged autosize" height="320"></iframe>
  </div>

  <div class="steps">

    <div class="step">
      <p>So far, we've assumed a constant frame rate.</p>
    </div>

    <div class="step">
      <p>If our animation is defined by an <span class="blue">easing curve</span>, we can look up its value at any point along the way.</p>
    </div>

    <div class="step">
      <p>It seems at first, variable frame rates are trivial: we can evaluate the curve at arbitrary times instead of pre-set intervals. </p>
    </div>

    <div class="step">
      <div class="extra top left" data-delay="1">
        <br><big>$$ \class{green}{v_{i→}} = \frac{\class{blue}{p_{i+1}} - \class{blue}{p_{i}}}{t_{i+1} - t_i} $$</big><br><br><br><br></div>
      <div class="extra top left" data-delay="2">
        <br><big><br><br><br><br>$$ \class{blue}{p_{i+1}} = \class{blue}{p_0} + \sum\limits_{k=0}^i\class{green}{v_{k→}} \cdot Δt_i $$</big>
      </div>
      <p>If we take forward differences to measure slope, we still get a smooth <span class="green">velocity curve</span>. We can accumulate—<em>integrate</em>—these differences back into <span class="blue">position</span> as long as we account for a variable time step $ Δt_i $. It seems our physics engine should be unbothered too. But there's a few problems.</p>
    </div>

    <div class="step">
      <div class="extra top right">
        $$ \class{green}{v_{i+1→}} = \class{green}{v_{i→}} \cdot (1 - \class{royal}{f}) $$
      </div>
      <p>First, if we implemented inertial scrolling like we did before, multiplying the <span class="green">velocity</span> by $ 1 - \class{royal}{f} $ every frame, we'd get the wrong curve. The amount of velocity lost per frame should now vary, we can no longer treat it as a convenient constant.</p>
    </div>

    <div class="step">
      <div class="extra top right" data-align-x=".8" data-delay="1">
        <big><big>$$ 
          \begin{array}{rcl}
        	
           (1 - \class{purple}{f_i})^\frac{t}{Δt_i} &amp; = &amp; (1 - \class{royal}{f})^\frac{t}{Δt} \\
            ⇔ \,\,\, \class{purple}{f_i} &amp; = &amp; 1 - e^{\frac{Δt_i}{Δt} \log_e (1 - \class{royal}{f})}
          
          \end{array}
          $$</big></big>
      </div>
      <p>If we do the math, we can find an expression for the correct amount of friction $ \class{purple}{f_i} $ per frame for a given step $ Δt_i $, relative to the default $ \class{royal}{f} $ and $ Δt $. Not pretty, and this is just one object experiencing one force. In more elaborate scenarios, finding exact expressions for positions or velocities can be hard or even impossible. This is what the physics engine is supposed to be doing for us.</p>
    </div>

    <div class="step">
      <p>There's another problem. If we integrate these curve segments to get position, we get an <span class="blue">exponential curve</span>, just as before. Did we achieve frame rate independence?</p>
    </div>

    <div class="step">
      <p>Well, no. If we change the time steps and run the algorithm again, it looks the same. However, the <span class="blue">new curve</span> and <span class="purple">old curve</span> don't match up. The difference is surprisingly large, as this animation is only half a second long and the average frame rate is identical in both cases. Such errors will compound the longer it runs, and make your program unpredictable.</p>
    </div>

    <div class="step">
      <p>Luckily we can have our cake and eat it too. We can achieve consistent physics and still render at arbitrary frame rates. We just have to decouple the <span class="cyan">physics clock</span> from the <span class="purple">rendering clock</span>.</p>
    </div>

    <div class="step">
      <p>Whenever we have to <span class="purple">render a new frame</span>, we compare both clocks. If the render clock has advanced past the physics clock, we do one or more <span class="blue">simulation steps</span> to catch up. Then we <span class="cyan">interpolate linearly</span> between the last two values until we run out of physics again.</p>
    </div>
    
    <div class="step">
      <p>
    This means the visuals are delayed by one physics frame, but this is usually acceptable. We can even <span class="blue">run our physics at half the frame rate</span> or less to conserve power. Though more error will creep in, this error will be identical between all runs, and we can manually compensate for it if needed.</p>
    </div>

    <div class="step">
      <p>
        When we implement variable frame rates correctly, we can produce an <span class="cyan">arbitrary number of frames</span> at arbitrary times. This buys us something very important, not for the end-user, but for the developer: the ability to skip to any point in time, or at least fast-forward as quickly as your computer can manage.
      </p>
    </div>

    <div class="step">
      <p>
        But just because the simulation is consistent, doesn't mean it's correct or even <em>stable</em>. Euler integration fits our intuitive model of how pieces add up, but it's actually quite terrible. For example, if we made our <span class="blue">bouncing apple</span> perfectly <em>elastic</em> in the physical sense—losing no energy at all—and apply Euler, it would start <span class="cyan">bouncing irregularly</span>, gaining height.
      </p>
    </div>

    <div class="step">
      <p>
        Which means the first bounce simulation wasn't using Euler at all. It couldn't have: the energy wouldn't have been conserved. All the finite differentiation and integration magic that followed only worked neatly because the <span class="blue">position</span> data was of a higher quality to begin with. We have to find the source of this phantom energy so we can correct for it, creating the <em>Verlet integration</em> that was used.
      </p>
    </div>

    <div class="step">
      <p>
        We're trying to simulate <span class="blue">this path</span>, the ideal curve we'd get if we could integrate with infinitely small steps. We imagine we start at the point in the middle, and would like to step forward by a large amount. The time step is exactly 1 second, so we can visually add accelerations and velocities like vectors, without having to scale them. Note that this is <em>not</em> a gravity arc, the downward force now varies.
      </p>
    </div>

    <div class="step">
      <div class="extra top left" data-delay="2" data-hold="1">
        <big>
          $$ \class{green}{v_{i+1→}} = \class{green}{v_{i→}} + \class{orangered}{a_{i→}} \cdot Δt $$
          $$ \class{blue}{p_{i+1}} = \class{blue}{p_{i}} + \class{green}{v_{i→}} \cdot Δt $$
        </big>
      </div>
      <p>
        Earlier, I said that if we used <span class="green">forward differences</span>, we could get the velocity between two points. And that we could make a reconstruction of <span class="blue">position</span> from forward <span class="green">velocity</span> by applying 'Euler integration'. While that's true, that's not actually what Euler integration <em>is</em>.
      </p>
    </div>

    <div class="step">
      <p>
        See, this is a chicken and egg problem. This <span class="green">velocity</span> isn't the slope at the start <em>or</em> the end or even the middle. It's the <em>average velocity over the entire time step</em>. We can't get this velocity without knowing the future <span class="blue">position</span>, and we can't get there without knowing the <span class="green">average velocity</span> in the first place.
      </p>
    </div>

    <div class="step">
      <div class="extra top left" data-delay="1" data-hold="1">
        <big>
          $$ \class{green}{v_{i+1↓}} = \class{green}{v_{i↓}} + \class{orangered}{a_{i↓}} \cdot Δt $$
          $$ \class{blue}{p_{i+1}} = \class{blue}{p_{i}} + \class{green}{v_{i↓}} \cdot Δt $$
        </big>
      </div>
      <p>
         The <span class="green">velocity</span> that we're actually tracking is for the <span class="blue">point itself</span>, at the start of the frame. Any force or <span class="orangered">acceleration</span> is calculated based on that single instant. If we integrate, we move forward along the <span class="cyan">curve's tangent</span>, not the curve itself. This is where the extra height comes from, and thus, phantom gravitational energy.
      </p>
    </div>

    <div class="step">
      <p>
        For any finite step, there will always be some overshooting, because we don't yet know what happens along the way. Euler actually made the same mistake we made earlier: he used a <em>central</em> difference where a <em>forward</em> one was required, because the forward difference can only be gotten <em>after the fact</em>. The 'central difference' here is the actual <span class="green">velocity</span> at a point, the true <em>derivative</em>.
      </p>
    </div>

    <div class="step">
      <div class="extra top left" data-delay="3">
        <big>
          $$ \class{green}{v_{i+1↓}} = \class{green}{v_{i↓}} + \class{orangered}{a_{i↓}} \cdot Δt $$
          $$ \class{blue}{p_{i+1}} = \class{blue}{p_{i}} + \frac{\class{green}{v_{i↓}} + \class{green}{v_{i+1↓}}}{2} \cdot Δt $$
        </big>
      </div>
      <p>
        As the acceleration changes in this particular scenario, we could try <span class="orangered">applying Euler</span>, and then averaging the <span class="green">start and end velocities</span> to get something in the middle. It fails, because the end velocity itself is totally wrong. Though we get closer than Euler did, we now <em>undershoot</em> by half the previous amount.
      </p>
    </div>

    <div class="step">
      <div class="extra top left" data-delay="2">
        <big>
          $$ \class{green}{v_{←i}} = \frac{\class{blue}{p_{i}} - \class{blue}{p_{i-1}}}{Δt} $$
        </big>
      </div>
      <p>
        To resolve the chicken and egg, we need to look to the past. We assume that rather than starting with one position, we start with <span class="blue">two known good frames</span>, defined by us. That means we can take a <em>backwards</em> difference and now know the <span class="green">average velocity</span> of the <em>previous frame</em>. How does this help?
      </p>
    </div>

    <div class="step">
      <p>
        Well, we assume that this velocity happens to be equal or close to the velocity at the <span class="purple">halfway point</span>. We also still assume the <span class="orangered">acceleration</span> is constant for the entire duration. If we then integrate from here to the <span class="cyan">next halfway point</span>, something magical happens.
      </p>
    </div>

    <div class="step">
      <div class="extra top left" data-delay="2.5">
        <big>
          $$ \class{green}{v_{i→}} = \class{green}{v_{←i}} + \class{orangered}{a_{i↓}} \cdot Δt $$
        </big>
      </div>
      <p>
        We get a <em>perfect prediction</em> for the next frame's <span class="green">average velocity</span>, the <em>forward</em> difference. By always remembering the previous position, we can repeat this indefinitely. That this works at all is amazing: we're applying the exact same operation as before—<span class="orangered">constant acceleration</span>—for the same amount of time. On just a <em>slightly</em> different concept of velocity. Without even knowing exactly when the object reaches that velocity. That's <em>Verlet integration</em>.
      </p>
    </div>
    
    <div class="step">
      <p>Euler integration failed on a simple <em>constant acceleration</em> like gravity and can only accurately replicate a linear ease $ f $. This motion is a <em>cubic ease</em> $ f^3 $, with <em>linear acceleration</em> that decreases. Verlet still nails it, even when leaping seconds at a time. Why does this work?</p>
    </div>

    <div class="step">
      <p>Euler integration applies a constant acceleration <span class="orangered">ahead of a point</span>. If there's any decrease in acceleration, it <span class="slate">overestimates</span> by a significant amount. That's on top of stepping in the <span class="green">wrong direction</span> to begin with. Both <span class="blue">position</span> and <span class="green">velocity</span> will instantly begin to drift away from their true values.
      </p>
    </div>

    <div class="step">
      <div class="extra left" data-delay="3">
        <big>
          $$ \class{blue}{p_{i+1}} = 2 \cdot \class{blue}{p_{i}} - \class{blue}{p_{i-1}} + \class{orangered}{a_{i↓}} \cdot Δt^2 $$
        </big>
      </div>
      <p>Verlet integration applies the same constant acceleration <span class="orangered">around a point</span>. If the acceleration is a perfect line, the error cancels out: the two triangles make up an equal <span class="cyan">positive</span> and <span class="slate">negative</span> area. By starting with a known good <span class="green">initial velocity</span> and cancelling out subsequent errors, we can precisely track velocity through a linear force. If we simplify the formula, velocity even disappears: we can work with <span class="blue">positions</span> and <span class="orangered">acceleration</span> directly.
      </p>
    </div>

    <div class="step">
      <p>As this captures the slope of acceleration, we only get errors if the acceleration <span class="orangered">curves</span>. In this case, the left and right areas don't cancel out exactly. The <span class="purple">missing area</span> however smoothly approaches 0 as the time step shrinks, a further sign of Verlet's error-defeating properties. If we do the math, we find the <span class="blue">position</span> has $ O(Δt^2) $ global error: decrease the time step $ Δt $ by a factor of 10, and it becomes 100× more accurate. Not bad.
      </p>
    </div>

    <div class="step">
      <p>
        For completeness, here's the 4th order <em>Runge-Kutta</em> method (RK4), which is a sophisticated modification of Euler integration. It involves taking full and half-frame steps and backtracking. It finds 4 estimates for the <span class="green">velocity</span> based on the <span class="orangered">acceleration</span> at the start, middle and end.
      </p>
    </div>
    
    <div class="step">
      <p>
        The physics can then be integrated from a weighted sum of these estimates, with coefficients $ [\frac{1}{6}, \frac{2}{6}, \frac{2}{6}, \frac{1}{6}] $. We end up in the <span class="blue">right place</span>, at the <span class="green">right speed</span>. This method offers an $ O(Δt^4) $ global error. Decrease the time step 10× and it becomes 10,000× more accurate. We have a choice of easy-and-good-enough (Verlet) or complicated-but-precise (RK4), at any frame rate. Each has its own perks, but Verlet is most attractive for games.
       </p>
    </div>

    <div class="step">
      <p>
        With physics under our belt, let's move on. Why not animate time itself? This is the <em>variable speed clock</em> and it's dead simple. It's also a great debugging tool: sync all your animations to a global clock and you can activate <em>bullet time</em> at will. You can tell right away if a glitch was an animation bug or a performance hiccup. On this site too: if you hold <kbd>Shift</kbd>, everything slows down 5×.
      </p>
    </div>

    <div class="step">
      <div class="extra top" data-delay="1">
        <big>$$ \class{green}{v_{←i}} = \frac{\class{blue}{t_i} - \class{blue}{t_{i-1}}}{\class{blue}{t_i} - \class{blue}{t_{i-1}}} = \frac{Δt_i}{Δt_i} = 1 $$</big>
      </div>
      <p>
        First, we differentiate the clock's <span class="blue">time</span> backwards—because in real-time applications, we don't know what the future holds. This is time's <span class="green">velocity</span> $ \class{green}{v_{←i}} $. As we have to divide by the time step too, the velocity is constant and equal to 1. Let's change that.
      </p>
    </div>

    <div class="step">
      <div class="extra top" data-delay="1.5" data-hold="1">
        <big>$$ \class{blue}{t'_i} = \sum\limits_{k=0}^i \class{green}{v'_{←k}} \cdot Δt_k $$</big>
      </div>
      <p>
        We can reduce the speed of time at will, by changing $ \class{green}{v_i} $. If we then multiply by the time step $ Δt_i $ again and <span class="green">add the pieces back together</span> incrementally, we get a <span class="blue">new clock $ t'_i $</span>. By <em>integrating</em> this way, we only need to worry about <span class="green">slope</span>, not <span class="blue">position</span>: time always advances consistently. This is also where variable frame rates pay off: going half the speed is the same job as rendering at twice the frame rate.
      </p>
    </div>

    <div class="step">
      <p>
        Using our other tricks, we can animate $ \class{green}{v_i} $ smoothly, easing in and out of slow motion, or speeding into fast-forward. If we didn't do this, then any animation cued off this clock would jerk at the transition point. This is the <em>chain rule</em> for derivatives in action: derivatives compound when you compose functions. Any jerks caused along the way will be visible in the end result.
      </p>
    </div>

    <div class="step">
      <p>If time is smooth, what about interruptions? Suppose we have a <span class="blue">cosine eased animation</span>. After half a second, the user interrupts and triggers a new animation. If we abort the animation and start a new one, we create a huge jerk. The object stops instantly and then slowly starts moving again.
      </p>
    </div>

    <div class="step">
      <p>One way to solve this is to layer on another animation: one that <span class="purple">blends between</span> the two easing curves in the middle. Here it's just another cosine ease, interpolating in the vertical direction, between <span class="blue">two changing values</span>. We blend across the entire animation for maximum smoothness. This has a downside though: if the blended animation itself is interrupted, we'd have to layer on another blend, one for each additional interruption. That's too much bookkeeping, particularly when using long animations.</p>
    </div>

    <div class="step">
      <p>We can fix this by mimicking inertial scrolling. We treat everything that came before as a black box, and assume nothing happens afterwards. We only look at one thing: <span class="green">velocity</span> at the time of interruption.</p>
    </div>

    <div class="step">
      <p>After determining the <span class="green">velocity</span> of any running animations, we can construct a <span class="blue">ramp</span> to match. We start from 0 to create a <em>relative animation</em>.</p>
    </div>

    <div class="step">
      <p>We can bend this <span class="blue">ramp</span> <span class="purple">back to zero</span> with another cosine ease, interpolating vertically. This time however, the first easing curve is no longer involved.</p>
    </div>

    <div class="step">
      <p>If we then add this to the <span class="blue">second animation</span>, it <span class="purple">perfectly fills the gap</span> at the corner. We only need to track two animations at a time: the currently active one, and a <span class="cyan">corrective bend</span>. If we get interrupted again, we measure the combined velocity, and construct a new bend that lets us forget everything that came before.
      </p>
    </div>

    <div class="step">
      <p>By using a <span class="cyan">different easing curve</span> for the correction, we can make it tighter, creating a slight wave at the end. Either way, it doesn't matter how the object was moving before, it will always recover correctly.</p>
    </div>

    <div class="step">
      <p>But what if we get interrupted all the time? We could be tracking a moving pointer, following a changing audio volume, or just have a fidgety user in the chair. We'd like to smooth out this data. The interrupted easing approach would be constantly missing its target, because there is never time for the value to settle. There is an easier way.
      </p>
    </div>

    <div class="step">
      <div class="extra top edge">
        <big>$$ \class{blue}{p_{i+1}} = lerp(\class{blue}{p_{i}}, \class{purple}{o_{i}}, \class{royal}{f}) $$</big>
      </div>
      <p>We use an <em>exponential decay</em>, just like with inertial scrolling. Only now we manipulate the <span class="blue">position $ p_{i} $</span> directly: we move it a certain constant fraction towards the target $ \class{purple}{o_{i}} $, chasing it. Here, $ \class{royal}{f} = 0.1 = 10\% $. This is a one-line feedback system that will keep trying to reach its target, no matter how or when it changes. When the <span class="purple">target</span> is constant, the <span class="blue">position</span> follows an exponential arc up or down.
      </p>
    </div>

    <div class="step">
      <div class="extra top edge">
        $$ \class{blue}{p_{i+1}} = lerp(\class{blue}{p_{i}}, \class{purple}{o_{i}}, \class{royal}{f}) $$
        $$ \class{cyan}{q_{i+1}} = lerp(\class{cyan}{q_{i}}, \class{blue}{p_{i}}, \class{royal}{f}) $$
      </div>
      <p> The <span class="blue">entire path</span> is continuous, but not <em>smooth</em>. That's fixable: we can apply <span class="cyan">exponential decay again</span>. This creates two linked pairs, each chasing the next, from $ \class{slate}{q_{i}} $ to $ \class{blue}{p_{i}} $ to $ \class{purple}{o_{i}} $. Each level appears to do something akin to <em>integration</em>: it smooths out discontinuities, one derivative at a time. Where a curve crosses its parent, it has a local maximum or minimum. These are signs that calculus is hiding somewhere.
      </p>
    </div>

    <div class="step">
      <div class="extra top edge" data-hold="1">
        $$ \class{blue}{p_{i+1}} = lerp(\class{blue}{p_{i}}, \class{purple}{o_{i}}, \class{royal}{f}) $$
        $$ \class{cyan}{q_{i+1}} = lerp(\class{cyan}{q_{i}}, \class{blue}{p_{i}}, \class{royal}{f}) $$
        $$ \class{slate}{r_{i+1}} = lerp(\class{slate}{r_{i}}, \class{cyan}{q_{i}}, \class{royal}{f}) $$
      </div>
      <p>That's not so surprising when you know these are <em>difference equations</em>: they describe a relation between a quantity and how it's changing from one to step to the next. These are the finite versions of <em>differential equations</em> from calculus. They can describe sophisticated behavior with remarkably few operations. Here I added a third feedback layer. The <span class="slate">path</span> gets smoother, but also lags more behind the target.
      </p>
    </div>

    <div class="step">
      <p>If we increase $ f $ to 0.25, the curves respond more quickly. Exponential decays are directly tuneable, and great for whiplash-like motions. The more levels, the more inertia, and the longer it takes to turn. 
      </p>
    </div>

    <div class="step">
      <div class="extra top edge">
        $$ \class{blue}{p_{i+1}} = lerp(\class{blue}{p_{i}}, \class{purple}{o_{i}}, \class{blue}{f_1}) $$
        $$ \class{cyan}{q_{i+1}} = lerp(\class{cyan}{q_{i}}, \class{blue}{p_{i}}, \class{cyan}{f_2}) $$
        $$ \class{slate}{r_{i+1}} = lerp(\class{slate}{r_{i}}, \class{cyan}{q_{i}}, \class{slate}{f_3}) $$
      </div>
      <p>We can also pick a different $ f_i $ for each stage. Remarkably, the order of the $ \class{royal}{f_i} $ values doesn't matter: <span class="blue">0.1</span>, <span class="cyan">0.2</span>, <span class="slate">0.3</span> has the <span class="slate">exact same result</span> as <span class="blue">0.3</span>, <span class="cyan">0.2</span>, <span class="slate">0.1</span>. That's because these filters are all <em>linear, time-invariant systems</em>, which have some very interesting properties.</p>
    </div>

    <div class="step">
      <p>
        If you shift or scale up/down a particular input signal, you'll get the exact same output back, just shifted and scaled in the same way. Even if you shift by less than a frame. We've created <em>filters</em> which manipulate the frequencies of signals directly. These are 1/2/3-pole <em>low-pass filters</em> that only allow slow changes. That's why this picture looks exactly like <em>sampling</em> continuous curves: the continuous and discrete are connected.</p>
    </div>

    <div class="step">
      <p>Exponential decays retain all their useful properties in 2D and 3D too. Unlike splines such as Bezier curves, they require no set up or garbage collection: just one variable per coordinate per level, no matter how long it runs. It works equally well for adding a tiny bit of mouse smoothing, or for creating grand, sweeping arcs. You can also use it to smooth existing curves, for example after randomly distorting them.</p>
    </div>

    <div class="step">
      <p>However there's one area where decay is constantly used where it really shouldn't be: download meters and load gauges. Suppose we start downloading a file. The <span class="purple">speed</span> is relatively constant, but noisy. After 1 second, it drops by 50%. This isn't all that uncommon. Many internet connections are <em>traffic shaped</em>, allowing short initial bursts to help with video streaming for example.</p>
    </div>

    <div class="step">
      <div class="extra bottom" data-align-y=".75">
        $$ \class{blue}{p_{i+1}} = lerp(\class{blue}{p_{i}}, \class{purple}{o_{i}}, \class{royal}{f}) $$
      </div>
      <p>Often developers apply <span class="blue">slow exponential easing</span> to try and get a stable reading. As you need to smooth quite a lot to get rid of all the noise, you end up with a long decaying tail. This gives a completely wrong impression, making it seem like the speed is still dropping, when it's actually been steady for several seconds. The same shape appears in Unix load meters: it's a lie.</p>
    </div>

    <div class="step">
      <div class="extra bottom" data-align-y=".75">
        $$ p'_{i+1} = lerp(p'_{i}, \class{purple}{o_{i}}, \class{royal}{f}) $$
        $$ \class{cyan}{q_{i+1}} = lerp(\class{cyan}{q_{i}}, p'_{i}, \class{royal}{f}) $$
      </div>
      <p>If we apply <span class="cyan">double exponential easing</span>, we can increase $ f $ to get a <em>shorter tail</em> for the same amount of smoothing. But we can't get rid of it entirely: the more levels of easing we add, the more the curve starts to lag behind the data. We can do much better.</p>
    </div>

    <div class="step">
      <p>We can analyze the filters by examining their response to a standard input. If we pass in a <span class="purple">single step</span> from 0 to 1, we get the <em>step response</em> for the <span class="blue">two</span> <span class="cyan">filters</span>.</p>
    </div>

    <div class="step">
      <p>Another good test pattern is a single <span class="orangered">one frame pulse</span>. This is the <em>impulse response</em> for <span class="green">both</span> <span class="gold">filters</span>. The impulse responses go on forever, decaying to 0, but never reaching it. This shows these filters effectively compute a weighted average of every single value they've ever seen before: they have a <em>memory</em>, an <em>infinite impulse response</em> (IIR).</p>
    </div>

    <div class="step">
      <p>Doesn't this look somewhat familiar? It turns out, the step response is the <em>integral</em> of the impulse response. It's a <span class="blue">position</span>. Vice versa, the impulse response is the <em>derivative</em> of the step response. It's a <span class="green">velocity</span>. Surprise, physics!</p>
    </div>

    <div class="step">
      <p>But it gets weirder. <em>Integration</em> sums together all values starting from a certain point, multiplied by the (constant) time step. That means that integration is itself a <em>filter</em>: its <span class="purple">impulse response</span> is a single step, the <em>integral of an impulse</em>. Its <span class="slate">step response</span> is a ramp, a constant increase.</p>
    </div>

    <div class="step">
      <p>It works the other way too. <em>Differentiation</em> takes the difference of neighbouring values. It's a filter and its <span class="orangered">step response</span> is just an impulse, detecting the single change in the <span class="purple">step</span>. Its <span class="slate">impulse response</span> is an upward impulse followed by a downward one: the <em>derivative of an impulse</em>. When one value is weighed positively and the other weighed negatively, the sum is their difference.</p>
    </div>

    <div class="step">
      <div class="extra left edge purple" data-align-x=".95" data-delay="3.5">
        <big><big>$$ \sum p_i \cdot Δt \,\,\, ↑ $$</big></big>
      </div>
      <div class="extra right edge slate" data-delay="3.5" data-align-x=".76">
        <big><big>$$ ↓ \,\,\, \frac{Δp}{Δt} $$</big></big>
      </div>
      <p>This explains why exponential filters seem to have integration-like qualities: these are <em>all</em> integrators, they just apply different weights to the values they add up. Every step response is another filter's impulse response, and vice versa, connected through <span class="purple">integration</span> and <span class="slate">differentiation</span>. We can use this to design filters to spec.</p>
    </div>

    <div class="step">
      <div class="extra bottom edge" data-delay="2">
        <big>$$ \class{green}{v_{i→}} = \sin \frac{π}{4} t_i $$</big>
      </div>
      <p>That said, filter design is still an art. IIR filters are feedback systems: once a value enters, it never leaves, bouncing around forever. Controlling it precisely is difficult under real world conditions, with finite arithmetic and noisy measurements to deal with. Much simpler is the <span class="green">finite impulse response</span> (FIR), where each value only affects the output for a limited time. Here I use one lobe of a sine wave over 4 seconds.</p>
    </div>

    <div class="step">
      <div class="extra bottom edge" data-delay="2">
        <big>$$ \class{blue}{p_{i+1}} = \class{blue}{p_0} + \sum\limits_{k=0}^i\class{green}{v_{i→}} \cdot Δt $$</big>
      </div>
      <p>Even if we don't know how to build the filter, we can still analyze it. We can integrate the <span class="green">impulse response</span> to get the <span class="blue">step response</span>. But there's a problem: it overshoots, and not by a little. Ideally the filtered signal should settle at the original height. The problem is that the area under the green curve does not add up to 1.</p>
    </div>

    <div class="step">
      <div class="extra bottom edge" data-delay="1">
        <big>$$ \class{green}{v_{i→}} = \frac{π}{8} \sin \frac{π}{4} t_i \,\,\,\,\,\,\,\,\,\,\, \class{blue}{p_{i}} = \frac{1}{2} + \frac{1}{2} \cos \frac{π}{4} t_i $$</big>
      </div>
      <p>To fix this, we divide the <span class="green">impulse response</span> by the area it spans, $ \class{green}{\frac{8}{π}} $, or multiply by $ \class{green}{\frac{π}{8}} $, normalizing it to 1. Such filters are said to have <em>unit DC gain</em>, revealing their ancestry in analog electronics. The step response turns out to be a <span class="blue">cosine curve</span>, and this filter must therefore act like perpetually interruptible cosine easing.</p>
    </div>
    
    <div class="step">
      <p>There's two ways of interpreting the step response. One is that we pushed a <span class="purple">step</span> through the <span class="green">filter</span>. Another is that we pushed the <span class="green">filter</span> through a <span class="purple">step</span>—an <span class="purple">integrator</span>. This symmetry is a property of <em>convolution</em>, which is the integration-like operator we've been secretly using all along.</p>
    </div>

    <div class="step">
      <p>Convolution is easiest to understand in motion. When you <em>convolve two curves</em> $ \class{purple}{q_i} ⊗ \class{green}{r_i} $, you slide them past each other, after mirroring one of them. As our <span class="green">impulse response</span> is symmetrical, we can ignore that last part for now.</p>
    </div>
    
    <div class="step">
      <div class="extra bottom edge" data-delay="8">
        <big>$$ \class{blue}{p_i} = \class{purple}{q_i} ⊗ \class{green}{r_i} = \class{cyan}{\sum\limits_{k=-∞}^{+∞}} \class{purple}{q_k} \cdot \class{green}{r_{i-k}} $$</big>
      </div>
      <p>We multiply <span class="purple">both</span> <span class="green">curves</span> with each other, creating a <span class="cyan">new curve</span> in the overlap: here a growing section of the impulse response. The area under this curve is the <span class="blue">output of the filter</span> at that time. The <span class="cyan">sum</span> goes to infinity in both directions, allowing for infinite tails. We already saw something similar when we used a <em>geometric series</em> to determine the final resting position of an inertial scroll gesture. With a FIR filter, the sum ends.</p>
    </div>

    <div class="step">
      <div class="extra bottom edge" data-delay="4.5">
        <big>$$ \class{blue}{p_i} = \class{purple}{q_i} ⊗ \class{green}{r_i}  = \class{cyan}{\sum\limits_{k=-∞}^{+∞}} \class{purple}{q_k} \cdot \class{green}{r_{i-k}} $$</big>
      </div>
      <p>But why did we have to mirror one curve? It's simple: from the <span class="green">impulse response</span>'s point of view, new values approach from the positive X side, now left, not the negative X side, right. By flipping the impulse response, it faces the <span class="purple">other signal</span>, which is what we want.</p>
    </div>

    <div class="step">
      <div class="extra bottom edge" data-hold="2">
        <big>$$ \class{blue}{p_i} = \class{green}{r_i}  ⊗ \class{purple}{q_i} = \class{cyan}{\sum\limits_{k=-∞}^{+∞}} \class{green}{r_k} \cdot \class{purple}{q_{i-k}} $$</big>
      </div>
      <p>If we center the view on the impulse response, it's clear we've swapped the role of the two curves. Now it's the <span class="purple">step</span> that's passing backwards through the <span class="green">filter</span>, rather than the other way around.</p>
    </div>

    <div class="step">
      <p>If we replace the step response with a <span class="purple">random burst of signal</span>, the filter can work its magic, smoothing out the input through convolution. It's a <em>weighted average</em> with a sliding window. The filter still lags behind the data, but the tail is now finite.</p>
    </div>
     
    <div class="step">
      <p>If we make the window narrower, its amplitude increases due to the normalization. We get a <span class="blue">more variable curve</span>, but also a shorter tail. This is like a blur filter in Photoshop, only in 1D instead of 2D. As Photoshop has the entire image at its disposal, rather than processing a real-time signal, it doesn't have to worry about lag: it can compensate directly by shifting the result back a constant distance when it's done.</p>
    </div>

    <div class="step">
      <div class="extra bottom edge" data-delay="3.5">
        <big>$$ \class{blue}{ease(f)} = \frac{1}{2} - \frac{1}{2} \cdot \cos πf 
          \,\,\,\,\,\,\,\,\,\,\,
           \class{green}{slope(f)} = \frac{1}{2}π \cdot \sin πf $$</big>
      </div>
      <p>What about custom filter design? Well, if you're an engineer, that's a topic for advanced study, learning to control the level and phase at exact frequencies. If you're an animator, it's much simpler: you pick a desired <span class="blue">easing curve</span>, and use its <span class="green">velocity</span> to make a normalized filter. You end up with the exact same step response, turning the easing curve into a perpetually interruptible animation.</p>
    </div>

    <div class="step">
      <div class="extra bottom edge" data-delay="2" data-hold="1">
        <big>$$ \class{blue}{ease(f)} = (\frac{1}{2} - \frac{1}{2} \cdot \cos πf) \cdot (1 + 20f \cdot (1 - f)^\frac{5}{2}) $$</big>
      </div>
      <p>Which leads to the last trick in this chapter: removing lag on a real-time filtered signal. There's always an inherent delay in any filter, where signals are shifted by roughly half the window length. We can't get rid of it, only reduce it. We have to change the filter to prefer certain frequencies over others, making it <em>resonate</em> to the kind of signal we expect. We use an <span class="blue">easing curve</span> that overshoots, and preferably a short one. This is just one I made up.</p>
    </div>

    <div class="step">
      <p>The <span class="green">velocity</span>—here scaled down—now has a positive and negative part. As neither part is normalized by itself, the filter will first <span class="blue">amplify</span> any signal it encounters. The second part then compensates by pulling the level <span class="blue">back down</span>.</p>
    </div>
    
    <div class="step">
      <p>
     The result is that the filter actually tries to <span class="purple">predict</span> the signal, which you can imagine is a useful thing to do. At certain points, the lag is close to 0, when the resonance frequency matches and slides into phase. When applied to animation, resonant filters can create jelly-like motions. When applied to electronic music at about 220 Hz, you&nbsp;get&nbsp;Acid&nbsp;House.
     </p>
   </div>

    <div class="step">
      <p>
     Let's put it all together, just for fun. Here we have some <span class="blue">particles</span> being simulated with Verlet integration. Each particle experiences <span class="orangered">three forces</span>. <em>Radial containment</em> pushes them to within a certain distance of the <span class="purple">target</span>. <em>Friction</em> slows them down, opposing the <span class="green">direction of motion</span>. A <em>constantly rotating force</em>, different for each particle, keeps them from bunching&nbsp;up. The target follows the mouse, with double exponential easing.
     </p>
   </div>

    <div class="step">
      <p>
        Friction links <span class="orangered">acceleration</span> to <span class="green">velocity</span>. Containment links <span class="orangered">acceleration</span> to <span class="blue">position</span>. And integration links them back the other way. These circular dependencies are not a problem for a good physics engine. Note that the particles <em>do not interact</em>, they just happen to follow similar rules.<br>
        <em>Tip: Move the mouse and hold <kbd>Shift</kbd> to see variable frame rate physics in action.</em>
     </p>
   </div>

    <div class="step">
      <p>
        If we add up the <span class="orangered">three forces</span> and trace out curves again, we can watch the <span class="blue">particles</span>—and their derivatives—speed through time. Just like you are doing right now, in your chair. As <span class="green">velocity</span> and <span class="orangered">acceleration</span> only update in steps, their curves will only be smooth if the physics clock and rendering clock are synced.
     </p>
   </div>

  </div>
  
</div>

<div class="c"></div>

<div class="g8 i2"><div class="pad">

<p>By manipulating time, we've managed to eliminate frame rate issues altogether, even make it work to our advantage. We've discovered more accurate physics engines, so we don't have to waste time simulating tiny steps. We've also created interruptible animations and turned them into filters. We can choose their easing curves and use feedback systems to remove the need for any manual interruptions altogether.</p>

<p>Here, <em>linear time-invariant</em> systems are very useful building blocks: they are simple to implement, but eminently customizable. Both IIR and FIR filters are simple in their basic form. We can also combine feedback systems with other physical or unphysical forces: we can move the target any way we like, perhaps superimposing variation onto existing curves. If we broaden our horizons a bit, we can find applications outside of animation: data analysis, audio manipulation, image processing, and much, much more.</p>

<p>Of course, there are plenty of non-linear and/or non-time-invariant systems too, too many to cover. When dealing with animation though, we'll prefer systems based on physics. They're just the trick to turn a bunch of artificial data into something that feels slick and natural. That said, physics itself is sometimes non-linear: fluids like water, smoke or fire are perfect examples. Solving those particular boondoggles requires the kind of calculus that frightens most adults and large children, so we won't go into that here. It's the same thing though: you simulate it finitely with a couple of clever tricks and the awesome power of raw number crunching.</p>

<p><em><big>Continued in <a href="/blog/animate-your-way-to-glory-pt2/">part two</a>.</big></em></p>

</div></div>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Zero to Sixty in One Second]]></title>
    <link href="http://acko.net/blog/zero-to-sixty-in-one-second/"/>
    <updated>2013-08-23T00:00:00+02:00</updated>
    <id>http://acko.net/blog/zero-to-sixty-in-one-second</id>
    <content type="html"><![CDATA[<style type="text/css" media="screen">
  .visualizer-activate {
    display: none;
  }
  .visualizer-activate a {
    display: block;
    position: absolute;
    left: 50%;
    top: 37%;
    width: 50px;
    height: 50px;
    margin-left: -25px;
    margin-top: -25px;
    text-align: center;
    vertical-align: middle;
    line-height: 58px;
    font-size: 28px;
    box-sizing: border-box;
    -moz-box-sizing: border-box;
    -webkit-box-sizing: border-box;
    padding-left: 4px;
    border-radius: 25px;
    color: #fff;
    background: rgb(108,123,132);
    box-shadow: 0 2px 0 rgb(84,98,105);
    box-shadow: 0 2px 0 rgb(84,98,105),
                0 0px 5px rgb(255,255,255),
                0 0px 20px rgb(255,255,255);
  }
  .visualizer-activate a.pause {
    display: none;
    opacity: .5;
    transition: opacity .1s ease-in-out;
    -webkit-transition: opacity .1s ease-in-out;
    line-height: 56px;
  }
  .visualizer-activate:hover a.pause {
    opacity: 1;
  }

</style>

<script type="text/javascript">
// <!--
window.Acko && Acko.queue(function () {
  if (window.gl) {
    var p = document.querySelector('.visualizer-activate');
    p.innerHTML = '<a href="#" class="play"><span class="icon-play"><span></span></span></a>'+
                  '<a href="#" class="pause"><span class="icon-pause"><span></span><span></span></span></a>';
    p.style.display = 'block';
    p.children[0].addEventListener('click', function (e) {
      e.preventDefault();
      gl.exports.visualizer.quietFrames = -420*60;
      gl.exports.visualizer.start();
      this.style.display = 'none';
      this.parentNode.children[1].style.display = 'block';
    });
    p.children[1].addEventListener('click', function (e) {
      e.preventDefault();
      gl.exports.visualizer.quietFrames = 0;
      gl.exports.visualizer.stop();
      this.style.display = 'none';
      this.parentNode.children[0].style.display = 'block';
    });
  }
});
// -->
</script>

<div class="g8 i2"><div class="pad">

<h2 class="sub">Fusing WebGL, CSS 3D and HTML</h2>

<p><em>Ladies and gentlemen, this is your captain speaking. Today's flight on WebGL Air will take us high above the cloud. Those of you sitting in Chrome class, on the desktop side of the plane, will have the clearest view. Internet Explorer class passengers may turn to their in-seat entertainment system instead. Passengers are reminded to put away their iPads and iPhones during take off and landing.</em></p>

<p><em>Edit: You can now also watch the <a href="http://www.youtube.com/watch?v=zjwA1VmuPnw">YouTube video</a> kindly provided by Leland Batey.</em></p>

<p>Acko.net, the domain, just turned 13, so it's time for a birthday present in the form of a complete front-end rewrite. The last design was entirely based on CSS 3D: it was a fun experiment, but created at a time when browser implementations were still wonky. It ultimately proved impractical: the DOM is not a good place to store complicated geometry. It's too bulky and there is a huge difference between a styled <code>&lt;div&gt;</code> and a shaded quad. After adding WebGL to the mix with MathBox and typesetting with MathJax, it turned into a catastrophic worst case for loading, and the smoothness was often lost even on fast computers.</p>

<p>Since then, we've seen a big push for rendering performance, both from the native and JS side. Hardware-accelerated DOM compositing is better understood, <code>requestAnimationFrame</code> is now common-place and reliable, and we have excellent profiling tools down to the frame level.</p>

<p>Hence the goal: to fuse 3D elements into the page like before, but with full 60 <span title="frames per second">fps</span> rendering. Plus to use WebGL instead of CSS 3D where possible, and be free of the constraints of the DOM.</p>

<p>Like <a href="http://www.voodoojs.com/">Voodoo.js</a> I use a fixed full-screen canvas and sync up scrolling with a 3D camera. The scene is mapped to CSS pixels and CSS perspective is locked to the camera. Once HTML, CSS 3D and WebGL are all in sync there's a truckload of <a href="http://acko.net/files/fullfrontal/fullfrontal/webglmath/online.html">linear algebra</a> and easing functions to keep you amused. The code is based on the platform I kludged together for the christmas demo, at times a mess of ad hoc demo formulas and spaghetti, though robust enough in the parts that count.</p>

</div></div>

<div class="c"></div>

<div class="wide mt2">
  <a target="_blank" href="http://daim.org/"><img src="https://acko.net/files/zerotosixty/daim.jpg" alt="Daim Graffiti"></a>
</div>

<div class="g8 i2 mb1">
  <a target="_blank" class="credit" href="http://daim.org/">Daim</a>
</div>

<div class="g8 i2"><div class="pad">

<h2>Procedural Wildstyle</h2>

<p>The <em>so Designers Republic</em> kitsch of last time was starting to grate, and I wanted something more stylish. Taking inspiration from street art and graffiti, in particular long-time favorite <a href="http://daim.org/">Daim</a>, Acko's gone wild, though with a math twist. This design was built procedurally using some nifty vector calculus and more difference equations than you can shake a stick at.</p>

<p>At its heart it's really a game of Snake, albeit a complicated one. Starting from a line-based skeleton of the model, the curves are traced out, smoothed, oriented and finally extruded on the fly into ribbons. The final pose shows 261 lines mapped to 2,783 curve segments, tessellated into 43,168 triangles, though that amount is just a knob that can be tuned. In other words, it's a <em>scalable vector graphic</em>. No images were harmed in the making of this header, or the rest of the window dressing for that matter. These pixels are local, organic and bespoke, though I'm pretty sure they're not vegan.</p>

</div></div>

<div class="c"></div>

<div class="wide mt2 mb2 draggable">
  <img src="https://acko.net/files/zerotosixty/skeleton.png" alt="Mesh Skeleton" data-replace="gl.exports.tracks.exportTracks()" data-position="[0, -150, 150]" data-rotation="[.3, 0, 0]">
</div>

<div class="c"></div>

<div class="g8 i2"><div class="pad">

<p>The ribbons need to animate, so I'm using a slightly exotic set up: a single Three.js <code>BufferGeometry</code> mesh is created with all the ribbons' topology in it. It only contains the connectivity of the triangles that span the vertices, stored as indices. The actual positions and normals are read from a texture that is updated on the fly by JavaScript using <code>gl.texSubImage2D</code>.</p>

</div></div>

<div class="c"></div>

<aside class="g6 mt1 tc pr"><div class="pad">
  <img src="https://acko.net/files/zerotosixty/geometrytexture.png" alt="Ribbon Geometry Texture" data-replace="gl.exports.tracks.exportTexture()">
  <p>This is the actual geometry data for the header, stored in a 288×256 RGBA texture (floating point). The columns alternate between position and normal.</p>
  <p class="visualizer-activate"></p>
</div></aside>

<div class="g6"><div class="pad">

<p>As a ribbon follows its path, only its head and tail change, growing new segments or collapsing older ones. Each ribbon scrolls through the texture, which alters only a fraction of the total data per frame. Both the head and tail can add and remove segments at will, which allows me to vary the geometrical detail along each path. As long as the number of drawn segments at any given time never exceeds the texture height, a ribbon could in theory follow an infinitely long trail, wrapping around the edge endlessly. They're basically semi-virtualized meshes, allocated out of a fixed memory space. The arrows are an easy upgrade: I allocate a couple additional segments at the end and shape them to the right widths.</p>

<p>To draw a ribbon, I draw one contiguous subset of the total mesh (or two, if wrapping around the edge) which maps into a rectangular area of the texture. Ribbon color is stored statically as a vertex attribute, which means the entire hero piece can be drawn using a single shader, texture and vertex buffer.</p>

<p>For the background, I added a static <code>BufferGeometry</code> clocking in at 3,344 triangles. It consists of rounded boxes laid out in a randomized hyperboloid pattern. Aside from breaking the uniform whiteness, it's also quite the parallax enhancer down here.</p>

</div></div>

<div class="c"></div>

<div class="g8 i2"><div class="pad">

<h2>DJ Ambient</h2>

<p>All of this is shaded with some custom GLSL. I use a basic Blinn-Phong lighting model tweaked for aesthetics, and there's some edge highlights and fog to top it off. But if I left it there, the result would still look pretty flat, without shadows. A common approach is to use shadow mapping, but that only works for point sources like the sun. The diffuse shadows that you see on an overcast day require an entirely different approach.</p>

</div></div>

<aside class="g3 i2 mt1 tc ol1"><div class="pad">
  <a href="http://http.developer.nvidia.com/GPUGems2/gpugems2_chapter14.html" target="_blank"><img src="https://acko.net/files/zerotosixty/ao1.jpg" alt="Ambient Occlusion"></a>
  <p>No shadows</p>
</div></aside>

<aside class="g3 mt1 tc ol1"><div class="pad">
  <a href="http://http.developer.nvidia.com/GPUGems2/gpugems2_chapter14.html" target="_blank"><img src="https://acko.net/files/zerotosixty/ao2.jpg" alt="Ambient Occlusion"></a>
  <p>Ambient occlusion</p>
</div></aside>

<aside class="g3 mt1 tc ol1"><div class="pad">
  <a href="http://http.developer.nvidia.com/GPUGems2/gpugems2_chapter14.html" target="_blank"><img src="https://acko.net/files/zerotosixty/ao3.jpg" alt="Ambient Occlusion"></a>
  <p>One indirect light bounce</p>
</div></aside>

<div class="c"></div>

<div class="g8 i2"><div class="pad">

<p>What's needed is <a href="http://en.wikipedia.org/wiki/Ambient_occlusion" target="_blank">ambient occlusion</a>, a measure of how much skylight is obscured by the surrounding geometry at every point. The less sky you can see from a point, the darker it should be. This is slow to compute analytically for large scenes and hence is typically either faked or baked in. An easy hack is <a href="http://en.wikipedia.org/wiki/Screen_space_ambient_occlusion" target="_blank">Screen-Space AO</a>, which is really just <em>crease darkening</em> using the final image's depth buffer. It's clever but expensive, creating only local shadows. Quality can vary a lot between implementations, often creating distracting dark or light halos around silhouettes. They all share the same blind spot: only the currently visible pixels can cast any shadow. So while I added <a href="http://alteredqualia.com/three/examples/webgl_postprocessing_ssao.html" target="_blank">alteredq's SSAO shader</a>, it's disabled by default and only rendered at quarter resolution, upscaled with a bilateral filter to hide this fact.</p>

<aside class="tc mt2 mb2">
  <p><img src="https://acko.net/files/zerotosixty/ssao.jpg" alt="Screen-space Ambient Occlusion"></p>
  <p class="mt0">Screen-space ambient occlusion (SSAO) in CryEngine 2</p>
</aside>

<p>Instead, I used a technique from NVidia's <em>GPU Gems 2</em>. It uses a <a href="http://http.developer.nvidia.com/GPUGems2/gpugems2_chapter14.html">disc-based model</a> to calculate occlusion and even indirect lighting on the fly. Rather than go real-time on the GPU, I decided to only do it statically in JS, and just do plain occlusion without light bounces.</p>

<p>The discs for all the ribbons are generated ahead of time, and the disc-to-disc shadowing is done once for the final model in two passes. The first pass overestimates due to overlapping shadows and thus is too dark. The second pass uses the first to assign lesser weights to discs in shadow to compensate, then runs the algorithm anew.</p>

<p>There's another simplification: instead of small one-sided discs based on the real geometry, I use large two-sided discs and treat each ribbon as flat. Each disc occludes in both directions, but accumulates shadow on its front and back separately.  This gives a decent approximation of light around and across the entire object.</p>
  
<p>Below is the actual disc model. In this diagram, the two radii of each disc represent its front and back illumination, with smaller discs receiving more shadow. The crosshairs mark the real disc radius.</p>

</div></div>

<div class="c"></div>

<div class="wide mt2 mb2 draggable">
  <img src="https://acko.net/files/zerotosixty/discs.png" alt="Occlusion Model" data-replace="gl.exports.tracks.exportDiscs()" data-position="[0, -150, 150]" data-rotation="[.3, 0, 0]">
</div>

<div class="c"></div>

<div class="g8 i2"><div class="pad">

<p>When streaming out new pieces of ribbon, the occlusion is baked in on the fly, stored in the unused alpha channel next to the position (RGB). The four nearest discs on the ribbon are interpolated bilinearly, and the vertex's normal and position is used to mix the front and back shadow values appropriately. Thus it's effectively trilinear filtering a 2×2×N voxel grid per ribbon, which snakes and twists along its length.</p>

<p>I'm very happy with how good it looks, even with very coarse divisions like this. Unlike SSAO it costs practically nothing once generated. If you wish, you can view the design <a href="http://acko.net/#white" target="_blank">entirely in white</a> to examine the lighting—or in <a href="http://acko.net/#sepia" target="_blank">black and white</a>, or in <a href="http://acko.net#70s" target="_blank">the seventies</a>, tone mapping is fun.</p>

<p>To ensure smooth rendering, the resolution is scaled down if it drops below 45 fps for several frames. This is essential on slower GPUs, but can sometimes over&shy;compensate, as WebGL is easily interrupted by other tasks on the machine. The browser doesn't tell you how much you're currently pushing it, so it's impossible to safely scale back up without causing more stutters. To mitigate this problem, I built in some strategic reset points, like when you focus the window or scroll to the top. In the end, 60fps happens more often than not, though it remains a goal rather than a guarantee. If you fullscreen your browser, you are now the bane of my existence, especially if you keep two dozen other windows open on the second monitor at the same time. On an underpowered retina MacBook.</p>

<p>Finally I also turned on <a href="http://mynameismjp.wordpress.com/2012/10/24/msaa-overview/" target="_blank">MultiSample Anti-Aliasing</a> to avoid the dreaded jaggies and make the vector style shine. It's substituted with <a href="http://en.wikipedia.org/wiki/Fast_approximate_anti-aliasing" target="_blank">Fast Approximate AA</a> if it's unavailable. FXAA is also used if SSAO is enabled, as you can't post-process multisampled images with WebGL.</p>

<p>Preparing all of this would take ~400ms on my 4 year old laptop, but most of that is spent on the occlusion which is static. Hence, I saved the lighting data into a JSON file for speedier loading, which cuts it down to ~100ms. The data gzips into 18KB, about the size of a small PNG, so no worries there. Add to that the time required to fetch and run the rest of the page, and we can call it an even second.</p>

<p>Of course, it only works on a subset of browsers and leaves the vast majority of mobile devices out in the cold. It does work in Chrome Beta on Android, though performance and stability is still pretty crap and more fixes are needed, both in the browser and in my own code. Rather than try and emulate some of the bling for CSS 3D-only environments, it's all or nothing. Without WebGL, you get plain images.</p>

<h2>Achievement Unlocked</h2>

<p>Reading all that, you might mistake the header as a mere love letter to hackery. But there's a twist, quite a literal one. The twisting of each ribbon is not generated arbitrarily, but mathematically derived. It embodies the differential principle of <a href="https://en.wikipedia.org/wiki/Parallel_transport" target="_blank">parallel transport</a>. The up direction changes parallel to each curve, which means the ribbons never rotate in place. They only turn when they naturally want to. Hence, the design kind of has a will of its own. I can set the initial up direction of a ribbon, but only affect it through its curved path. Arranging and tuning all the ribbons was a nice puzzle in itself, and it's a nice nod to the math that's all over the place, even if it is invisible.</p>

<p>As I started playing around with some screensaver-style shots, I realized it would make a pretty neat demo just by itself, so I built that in too, to the dulcet tones of <a href="http://www.selahsue.com/" target="_blank">Selah Sue</a>—whose last name I hope is not indicative. Here parallel transport would ensure a perfectly smooth ride, but that's not exciting, so there's some springy exponential easing with adaptive lookahead instead. That's on top of the secondary demo, which features an audio visualizer and the smooth drum and bass of <a href="http://www.secretoperations.com/" target="_blank">Seba</a>. There's maybe a third one too.</p>

<p>The songs are used here <em>entirely for educational purposes</em> of course. Not that it matters, since they're all on YouTube anyway. Click the Growl-like notifications to find out more about the artists. There's also a handful of achievements to be gathered, eight to be precise. Some of these are experiments that were turned off in the final version. Others are... trickier. Oh hey, did you notice the JS console?</p>

<p>By the way, special mention also goes to Time and how some browsers keep terrible track of it. Like when you're trying to sync a demo to a stuttery <code>&lt;audio&gt;</code> clock, Firefox ಠ_ಠ. There's also the matter of what to do when the user switches to another tab, and the answer is.... it'll desync, because I don't want to be sapping resources in a background tab. Minor constraints of putting on a live act, doors close after the show starts.</p>

<p class="tc mt2">
  <img src="https://acko.net/css/images/growl-large-01.png" class="inline flat" width="112" height="112" alt="Achievement Unlocked">
  <img src="https://acko.net/css/images/growl-large-02.png" class="inline flat" width="112" height="112" alt="Information">
  <img src="https://acko.net/css/images/growl-large-03.png" class="inline flat" width="112" height="112" alt="Music">
</p>

<h2>Turn Right Past The Header</h2>

<p>The refresh-less navigation is also back from last time, slightly cleaned up. It still works on the same basic principle of fetching static, full HTML documents. However the transition mechanism now carefully choreographs the necessary DOM manipulation to avoid stutters: any image, iframe or video inserted in the content would mean future paints at an unknown time, so they're postponed until after the transition is complete. I did it the dirty but fast way, with string manipulation before it hits the DOM: such is the luxury of being the only one, person or machine, writing the markup.</p>

<p>Which leaves of course the content. But don't fix what ain't broke: distraction-free publishing is easy enough with Jekyll and a responsive 960 pixel grid with sideburns, so things still look and work mostly the same. I only added a home for my math talks and expanded the front page to highlight some demos and experiments. I aim to keep focusing on quality rather than quantity, and to remind visitors what the web looks like when you take away everything social media's done to it.</p>

<p>It's also fun to observe that after many years, the unified front-end convergence really has arrived. There is little difference between this site and the games that use HTML for their UI, such as the latest Sim City. For dealing with typography, illustration and UI, you want the comfort of DOM and CSS. For real-time graphical content, you'll want to draw it yourself, either in 2D, or 3D. Combine the two, and you get what game developers have been doing for years. Only this lacks all the sharp C bits, which game devs been replacing with Lua for years anyhow. That's Portuguese for "JavaScript" by the way.</p>

<p>So there you have it. A fresh look with a pile of juicy hax, and hopefully not too many bugs in the wild. Thanks go to all those who came before and provided all these toys for me to play with. You can too, for the source is <a href="https://github.com/unconed/fuse10/" target="_blank">entirely on Github</a>. Like I said, <em>educational</em>. Meanwhile I'll be over here, listening to the <a href="http://en.wikipedia.org/wiki/Wipeout_(video_game)" target="_blank">wipE'out"</a> <a href="http://www.coldstorage.org.uk/" target="_blank">soundtrack</a> on repeat in memory of the old one. Comments welcome on <a href="https://plus.google.com/112457107445031703644/posts/6xhyep1357J">Google Plus</a>.</p>

<p><em>PS: The previous version <a href="http://acko.net/files/slacko/" target="_blank">has been archived</a> with its fallbacks disabled so you can see the current state of CSS 3D in browsers. Still pretty broken. Still a great test case. So is the <a href="/blog/this-is-your-brain-on-css/">disembodied head</a>.</em></p>

</div></div>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Why Android Hates You]]></title>
    <link href="http://acko.net/blog/why-android-hates-you/"/>
    <updated>2013-05-22T00:00:00+02:00</updated>
    <id>http://acko.net/blog/why-android-hates-you</id>
    <content type="html"><![CDATA[<style type="text/css" media="screen">
@font-face {
  font-family: "Roboto";
  src: url("/files/android/Roboto-Regular.ttf"), local('Roboto');
}
@font-face {
  font-family: "Roboto";
  src: url("/files/android/Roboto-Bold.ttf"), local('Roboto');
  font-weight: bold;
}
.roboto {
  font-size: 85%;
}
.roboto p {
  color: #444;
  background: #eee !important;
  padding: 28px !important;
  border-radius: 7px;
}
.roboto, .roboto strong {
  font-family: Roboto, sans-serif;
  text-transform: none;
}
.roboto strong {
  font-weight: bold;
  font-size: 120%;
}
</style>

<div class="g8 i2 first"><div class="pad">

<h2 class="sub">Usability, Affordance and Grannies in Vegas</h2>

</div></div>

<aside class="g4">
  <p class="tc">
    <img style="top: 0" src="https://acko.net/files/android/nexus4.jpg" alt="Nexus 4" class="flat">
  </p>
</aside>

<div class="g8"><div class="pad">

  <p>For quite a while, I've been an Android user. After upgrading to a Nexus 4 several months ago, I'm living the Ice Cream Sandwich and Jelly Bean dream, enjoying a beautifully designed user experience, <a href="http://en.wikipedia.org/wiki/Matias_Duarte">just like Matias intended</a>, right?</p>

  <p>Nope.</p>

  <p>Android has a problem and no, this is not about fragmentation. I'd like to talk about Android's contempt for its users instead. See, no matter how many pages of tastefully designed <a href="http://developer.android.com/guide/practices/ui_guidelines/index.html">User Interface Guidelines</a> the team puts together, Google just can't seem to shake its deep seated belief: that users are the most inconvenient and unreliable part of the cloud, and that getting adequate mediocrity across the board is the best you can do, rather than taking a holistic approach and excelling.</p>

  <p>Strong words, for sure. Ever tried to get support from Google? If you've studied <a href="http://www.amazon.com/gp/product/0465067107/ref=as_li_qf_sp_asin_tl?ie=UTF8&amp;camp=1789&amp;creative=9325&amp;creativeASIN=0465067107&amp;linkCode=as2&amp;tag=ackonet-20">how objects are supposed to be designed</a>, owning an Android phone is an exercise in frustration. It's a daily reminder of bad decisions, performed with a stubborn flourish, by someone who maybe doesn't have the skill or desire, but more likely, just isn't given room to do better.</p>

  <p>None of what follows are mortal sins, let's be clear on that. But they're missed opportunities in making Android actually pleasant, and they're a last-mile of frustration some of which Steve Jobs would've probably fired people over.</p>

</div></div>

<div class="g7"><div class="pad">

  <h2>Don't Get Comfortable</h2>

  <p>Since the dawn of time, my Android phones have claimed that app data and settings will be backed up to the cloud, courtesy of Google. By "data and settings" they mean "except the stuff you actually care about".</p>

  <p>It comes as absolutely no surprise that none of them have ever restored the layout and arrangement of icons and widgets on the home screens from the cloud. The only thing that seems to happen is that a bunch of apps get downloaded one after the other, spazzing out your notification bar for up to an hour if the connection is slow, filling up your app drawer while you're trying to put things back. Actually showing a progress of the entire restoration, perhaps estimating when you can expect it to be back to the way it was, is a pipe dream. Backing up to the cloud is done manually through apps, like G+ photo upload, or other third party mechanisms.</p>

  <p>After restoring your phone, you can also look forward to repeating all the patronizing tutorials and "Don't stick your fingers in the sockets" warnings of Google Now, Maps, Navigation, Goggles, etc. and certainly not removing "U.S." and "Sports" from your default news subscriptions, because that's what everybody likes.</p>

  <p>The fact that nobody at Google seems to consider this a problem is quite telling. But then, so is the default layout you get out of the box, or in this case, after "restoring my settings". Only the wallpaper was saved:</p>

</div></div>

<div class="g5 m1"><div class="pad">
  
  <p><img src="https://acko.net/files/android/backup.png" alt="Backup settings"></p>

  <p><img src="https://acko.net/files/android/app-down.png" alt="Downloading Twitter"></p>

  <div class="roboto">
    <p><strong>Never lose my stuff</strong><br>“Save what people took time to create and let them access it from anywhere. Remember settings, personal touches, and creations across phones, tablets, and computers. It makes upgrading the easiest thing in the world.”</p>
  </div>

</div></div>

<div class="c"></div>

<div class="wide mt2">
  <img src="https://acko.net/files/android/outofbox.jpg" alt="Android of of the box">
</div>

<div class="c"></div>

<div class="g6 m2"><div class="pad">

  <p>All the Google things are lumped into one folder, which includes the amazing labels "Play Music" alongside "Play Movie…", "Play Maga…", "Play Books" and "Play Store". The hesistant newbie has to make the cognitive leap that "Play" is not a verb here—despite the tacky golden rapper headphones—rather than conclude that Google has weird taste in words, or that this is a phone for children.</p>

  <p>The action verb they were looking for instead was "Buy Music" and "Buy Movies". It seems they replaced the media player with a shop you can keep your own tunes in. My advice: get rid of the advertisements posing as widgets and use <a href="http://www.doubletwist.com">DoubleTwist</a> instead.</p>

  <p>Actually presenting functionality is a job left for the App Drawer, paralyzing you with choice by Pokémon. There's two Mails, three Googles, three Messengers, a whole Play-set as well as assorted circles, pins and triangles. You'll find yourself dragging every app you'll use regularly somewhere more convenient. Open App Drawer. Pan. Draaag. Repeat.</p>

</div></div>

<div class="g6 m2"><div class="pad">
  <p><img src="https://acko.net/files/android/google-folder.png" alt="Google folder"></p>
</div></div>

<div class="c"></div>

<div class="g6 m1"><div class="pad">
  <p><img src="https://acko.net/files/android/app-drawer.png" alt="App Drawer"></p>
</div></div>

<div class="g6 m1"><div class="pad">

  <p>As an aside, out of the box, don't expect that people can just send you geo coordinates, video messages, meeting invites or office documents. Whenever it's really mattered that I read something on my phone right now, Android's given me more "I don't know what to do with this" errors than even my Windows 98 box back in the day. Clearly this is just a matter of hooking up the right pre-existing bits, and the newer releases are better. But still, it feels like a lot of real world use cases weren't tested, in particular without an internet connection.</p>

  <p>Just recently, I couldn't turn off the Satellite layer in Maps because I didn't have data, even though I'd cached the whole city I was in. Bug or fluke? Who knows. And when Google Now showed me flight info for the wrong day, there didn't seem to be a way to correct it. The algorithm knows better than you do, human.</p>

</div></div>

<div class="c"></div>

<div class="g12 m1"><div class="pad">
  
  <div class="roboto">
    <p><strong>Decide for me but let me have the final say</strong><br>“Take your best guess and act rather than asking first. Too many choices and decisions make people unhappy. Just in case you get it wrong, allow for 'undo'.”</p>
  </div>

</div></div>

<div class="c"></div>

<div class="g8 i2"><div class="pad">

  <h2>Don't Touch Me</h2>

  <p>As late as the Nexus One, most Android phones weren't really multi-touch. They had separate X/Y sensors, capable of distinguishing two touches from one, but unable to tell whether they were rotating clockwise or counter-clockwise. As a result, multi-touch has always been tacked on as an extra: despite now being capable of tracking 5 or even 10 touches, most surfaces still only respond to one.</p>

  <p>This all comes down to the concept of affordance: what do our devices suggest we can do with them, and what do they actually allow us to do? For example, based on the design of the handle, doors generally <em>afford</em> either pulling or pushing, giving strong hints as to how to open them. Instead of arguing over whether things should look skeuomorphic, how about making them act skeuomorphic?</p>

</div></div>

<div class="g7"><div class="pad">

  <p>Take for example the notification drawer. One way people often interact with real drawers is to pull them out with one hand, and reach in with the other. You could try doing this with the notification drawer: pull down with one finger, touch or swipe a notification while holding the drawer half open. It doesn't work: you can only interact with one thing at a time. It's not a drawer, it's a hand-operated modal dialog in disguise. This is obviously just a detail, but notable none the less.</p>
  
  <p>More importantly, some notifications can't be dismissed despite looking identical. While responding to swipe gestures, they slide back with pixel-perfect inertia, glued in place with invisible chewing gum. If you're out of cell range, or e.g. simply do not wish to incur roaming costs, the "New Voicemail" message will not go away until you call in, even if it was probably VoIP spam.</p>

  <p>The drawer is multi-functional: dragging down with two fingers brings up a dashboard of tiles. Unfortunately, this is a hard gesture to get right 100% of the time. Hence, it appears a clever Android programmer made it so you can add the second finger in the middle of the gesture. Convenient, but they messed it up, suggesting it's an accident: you have to land exactly on the gripper, instead of anywhere in the vicinity. This gesture is even harder than placing both fingers correctly in the first place.</p>

  <p>These tiles are another boondoggle: can you tell which ones you can long-press to toggle and which ones you can't? By the way, that long-press has its own non-standard delayed acknowledgement, ensuring you'll accidentally toggle things back and forth a couple of times before you get used to it. Just how much less functional is this than a real slider, some big switches and a few separate icons?</p>

  <p>This Flat UI theme has brought with it tons of unlabeled icon buttons. In some places you can long-press on an icon to get a tooltip describing it. Here too the interaction is cumbersome, requiring a long-press for every individual button rather than being able to slide your finger from icon to icon. The natural gesture of discovering a surface by feeling it has been reduced to a mechnical process of tap-hold-repeat. Heck, why not make use of the giant screen, blow up the icons, show tooltips with arrows to everything around your finger, do <em>something</em> more sophisticated than what Microsoft Office did.</p>

</div></div>

<div class="g5"><div class="pad">
  <p><img src="https://acko.net/files/android/notifications.png" alt="Notification Shade"></p>
  <p><img src="https://acko.net/files/android/notifications2.png" alt="Notification Shade"></p>
  <p><img src="https://acko.net/files/android/tooltip.png" alt="Tooltip"></p>
</div></div>

<div class="c"></div>

<div class="g6"><div class="pad">
  
  <div class="roboto">
    <p><strong>If it looks the same, it should act the same</strong><br>“Help people discern functional differences by making them visually distinct rather than subtle. Avoid modes, which are places that look similar but act differently on the same input.”</p>
  </div>

</div></div>

<div class="g6"><div class="pad">
  
  <div class="roboto">
    <p><strong>Give me tricks that work everywhere</strong><br>“People feel great when they figure things out for themselves. Make your app easier to learn by leveraging visual patterns and muscle memory from other Android apps. For example, the swipe gesture may be a good navigational shortcut.”</p>
  </div>

</div></div>

<div class="c"></div>

<div class="g7"><div class="pad">

  <p>Then there's the new Gesture-typing keyboard. If your nails are a bit long, or your hands a bit sweaty, or you're wearing thin gloves, you may find it difficult to form basic words. As soon as your finger leaves the pad for a fraction of a second, your gesture is interrupted, spawning a bunch of syllables. Even if you continue swiping and the line continues cleanly. It seems the physics of how fast humans can move their fingers are ignored here in favor of blind sensor obedience. If a gripped finger strays into the pad from the side, it'll stop working altogether. Oh and don't try to type the word "jerking", it's not allowed. What do you think this is, a real keyboard?</p>

  <p>Finally if you use Google Talk, you get to enjoy a strange bug where it randomly gets stuck in a loop and forcibly scrolls to the bottom every couple of seconds, at random intervals, resulting in the most frustrating user-computer tug of war I've encountered. Fun times.</p>

</div></div>
  
<div class="g5"><div class="pad">
  <p><img src="https://acko.net/files/android/gesture-typing.png" alt="Gesture Typing"></p>
</div></div>

<div class="g8 i2"><div class="pad">

  <h2 class="m3">It's For Your Own Good</h2>

  <p>Now, everyone knows headphones can damage your hearing. This is why the EU passed laws to help ensure consumers would be protected from themselves: devices like iPods are limited in the sound level they can emit. If you've ever sat next to someone and listened to the kacrunch-kacrunch that their hip-hop or dubstep sounded like to everyone on the outside, you may think this is a very good idea.</p>

  <p>Unfortunately, it takes a politician to think you could legislate against people being stupid, and a lawyer to come up with the convoluted verbiage that makes it seem sane to the rest of us. Then you need a developer to do a really hare-brained job in implementing it. Hence, in line with EU legislation, Android 4.2 introduces a new feature, for all users everywhere:</p>
  
</div></div>

<div class="g5"><div class="pad">

  <p><img src="https://acko.net/files/android/too-loud.png" alt="My spoon is too big"></p>

  <div class="roboto">
    <p><strong>Only interrupt me if it's important</strong><br>“Like a good personal assistant, shield people from unimportant minutiae. People want to stay focused, and unless it's critical and time-sensitive, an interruption can be taxing and frustrating.”</p>
  </div>

  <div class="roboto">
    <p><strong>Get to know me</strong><br>“Learn peoples' preferences over time. Rather than asking them to make the same choices over and over, place previous choices within easy reach.”</p>
  </div>

</div></div>

<div class="g7"><div class="pad">

  <p>This pops up whenever the device <em>thinks</em> you're raising the volume above a safe level. I say 'thinks', because reports suggest the behavior differs based on what the phone is plugged into, with some users never seeing it, and others complaining about it endlessly. In any case, it's clearly broken for two reasons.</p>

  <p>One, it is ridiculous to think a phone could know whether the volume is unsafe: it could be plugged into a speaker dock, an amp or a recording device. Some headphones also have an additional volume control on the cord, and regardless, the amount of sonic power transferred depends on the design of the headphones and whether they fit the person's ears well.</p>

  <p>Two, the feature is designed in the most user-hostile manner. If the screen happens to be off or out of sight when you push the volume above 50%, you won't notice anything except that your button stops working until you wake up the screen and press "Ok". Worse, after a certain amount of play time has passed, the volume will automatically be kicked back to 50% and the pop-up will return, for that's what the law apparently requires. There's no way to disable this 'feature' permanently. Volume regulation on Android is broken from a user's point of view.</p>

  <p>Ridiculousness of law aside, there are any number of ways this could've been done more elegantly. An audible signal could be played along with the message, to provide feedback rather than failing quietly. Pressing volume up again (or twice more) should be interpreted as consent and dismiss the message. Users should be able to permanently disable the warning, for any situation where their hearing is not at risk, or for anyone who understands that banning smoking in parks doesn't make car exhaust any less toxic. And if the lawyers tell you to annoy the hell out of your users, only do it when you are absolutely sure it's required, not across the board, to everyone, with one of the most unimaginative implementations ever.</p>

  <p>Oh and notice how it charmingly blacked out the copy of Transformers that Play Movies was playing in the background when taking a screenshot. Google <em>Play</em> clearly excludes sharing, do keep that in mind.</p>

</div></div>

<div class="g8 i2"><div class="pad">
  
  <h2>The Wobbly Camera</h2>

  <p>Moving on, Android 4.2 includes a completely redesigned camera app, with fun features like Photo Sphere and a more minimal UI. It seems pretty slick and follows the crisp white-on-black style of Jelly Bean. However within 10 seconds of using it, it will eagerly show you how stupid it is:</p>
  
</div></div>

<div class="g6 m1"><div class="pad">

  <video width="480" height="360" controls="controls" style="max-width: 100%">
     <source src="https://acko.net/files/android/camera-flip.mp4" type="video/mp4" />
     <source src="https://acko.net/files/android/camera-flip.ogv" type="video/ogg" />
     <source src="https://acko.net/files/android/camera-flip.webm" type="video/webm" />
     <div>Your browser does not support the video tag.</div>
  </video>

</div></div>

<div class="g6"><div class="pad">

  <div class="roboto">
    <p class="m0"><strong>Make important things fast</strong><br>“Not all actions are equal. Decide what's most important in your app and make it easy to find and fast to use, like the shutter button in a camera, or the pause button in a music player.”</p>
  </div>

</div></div>

<div class="g6"><div class="pad">

  <div class="roboto">
    <p><strong>Delight me in surprising ways</strong><br>“A beautiful surface, a carefully-placed animation, or a well-timed sound effect is a joy to experience. Subtle effects contribute to a feeling of effortlessness and a sense that a powerful force is at hand.”</p>
  </div>

</div></div>

<div class="g8 i2"><div class="pad">

  <p>When you rotate the device from portrait to landscape, it freezes the screen so it can do a completely useless transition. The image doesn't change, the widgets don't actually move anywhere, the controls just rotate out and back in again, and the focus ring trips out for a bit.</p>

  <p>Somewhere, a Google developer probably realized this, thought about it, and said "Fuck it, I'll use <code>com.google.android.paint.by.numbers</code>" instead of handling the odd upside down case elegantly.</p>

  <p>Switching a camera from portrait to landscape is one of its main affordances. Android manages to jank even this up. To make matters worse, this minimal UI misses the mark: there is only one thing you can do with just one tap, and that's taking a picture. You can argue this makes the camera easier to handle, but I bet there's more than one Nexus 4 user who's never used the front camera, something more important than adjusting the white balance.</p>
  
</div></div>

<div class="c"></div>

<div class="wide mt2">
  <img src="https://acko.net/files/android/camera.jpg" alt="Android camera">
</div>

<div class="c"></div>

<div class="g8 i2 m1"><div class="pad">

  <p>There's a button for toggling recording mode and a radial menu for everything else, but no labels or hints in sight, not even on confirmation. Meanwhile switching between the pre-set "scene modes" requires 4 taps and a buried menu, which either says "we forgot to include this in our wireframes" or "nobody really knows what this is for anyway".</p>

  <p>In the age of HDR imaging and live color filters, is there much reason to model a virtual camera after the confusing set-and-pray buttons of their physical counterparts, many of which only mimic film cameras to humor you?</p>

  <p>The Instagram-like filters that you can apply afterwards are functional, but the UI for applying effects and transforms is its own kind of weird, lacking useful feedback in several places. It also requires you to flick an invisible mirror to apply a mirroring effect, after pressing the Mirror button, I shit you not.</p>

  <p>The only thing that's genuinely clever about it though is how the camera acts like the newest picture frame in your gallery view, here on the left:</p>

</div></div>

<div class="g10 i1"><div class="pad">

  <p><img class="fit" src="https://acko.net/files/android/camera-strip.jpg"></p>

</div></div>

<div class="g6"><div class="pad">

  <p>However even this view is hard to use, because it uses the most uncomfortable form of inertial scrolling and snap-to-item seen on the platform yet. It moves like a slot machine wheel, flipping over at the very end. Dear UI programmers: please go learn about easing and the equations of motion, you really can't arse your way around these things anymore, unless the demographic you were aiming for was grannies in Vegas.</p>

</div></div>

<div class="g6"><div class="pad">
  
  <div class="roboto">
    <p><strong>Real objects are more fun than buttons and menus</strong><br>“Allow people to directly touch and manipulate objects in your app. It reduces the cognitive effort needed to perform a task while making it more emotionally satisfying..”</p>
  </div>

</div></div>

<div class="c"></div>

<div class="g5 m4"><div class="pad">
  
  <p><img src="https://acko.net/files/android/network1.png" alt="network selection"></p>

</div></div>

<div class="g7 m1"><div class="pad">

  <h2>The Network Cocktease</h2>

  <p>But the most amazing example has to be the Network Operator screen. This dialog, normally buried, is presented when you lose connectivity. It's not just completely dysfunctional, but has remained unchanged for years. We're talking about the feature that turns your phone from a mostly useless brick into a node on the global voice and data network. It's kind of crucial when you need it.</p>

  <p>If you're here, it means something has changed. Either you've travelled to a foreign country, you've just spent a noticeable time out of cell range, something's wrong on your operator's end, or the <a href="https://code.google.com/p/android/issues/detail?id=2845">software messed up</a>. Your goal is simple: to confirm to the phone which network you'd like to use and <em>start to reconnect</em> right now, so you can get back to business.</p>

  <p>Unlike Wi-Fi, scanning for mobile networks can take a while. I've tried to dig into the relevant specs to see exactly why this is, and there doesn't seem to be a clear reason for it: networks advertise themselves, cell phones simply listen in, tuning to various channels to do so. But I may have missed something, given that 3GPP specs make W3C specs look like florid prose.</p>
  
  <p>Most users just want the phone to choose the right network automatically, selecting their home network if it's available, or roaming on a preferred partner. This is done using a list stored in the SIM card, and hence all known ahead of time. So what went wrong?</p>

</div></div>

<div class="c"></div>

<div class="wide mt2">
  <img src="https://acko.net/files/android/network2.png" alt="Android Network Selection 2">
</div>

<div class="c"></div>

<div class="g8 i2 m1"><div class="pad">

  <p>First, the obvious one: despite Android's emphasis on letting background processes and services run freely, scanning for network operators is a modal process. Scanning starts as soon as this screen is opened, and the only way to keep it open is to not touch anything. You have to wait until the phone finds all results to select one. Which means the 99% of users who wish to "Choose Automatically" have to wait for no reason.</p>

  <p>But suppose you're travelling and you've set your display timer to a modest 15 seconds to conserve battery. Now the screen will shut off before scanning has completed. You'll want to just let the phone sit for a bit and then pick it up again, right?</p>

  <p>Wrong. See, scanning completed ages ago. But because you weren't here to watch the phone do it, you can't actually select any of these options, because the modal popup will now never go away. Tapping it does nothing. Tapping in the dimmed area around it instantly closes the whole screen. If you try to return, the whole scanning process starts over again, because the results weren't even cached for 5 seconds.</p>

  <p>This is Utterly Broken. Users go here because they want to pick an option, so it should maximize their opportunity for doing so, not do the opposite and fail completely. If there is a preferred provider available, it should indicate that it knows this, not just suggestively put it at the top of the list while being coy about it.</p>

  <p>It's also a classic example of Google-Don't-Care: the Nexus One had this screen, the Nexus 4 still has it. Because most people only use this in unusual circumstances, it hasn't been on anyone's radar to merit fixing, despite several major OS revisions. But when you're walking around a foreign city, running late and needing to call someone, few things will make you want to toss your phone against a wall more than this.</p>

</div></div>

<div class="c"></div>

<div class="g6"><div class="pad">

  <div class="roboto">
    <p><strong>It's not my fault</strong><br>“Be gentle in how you prompt people to make corrections. They want to feel smart when they use your app. If something goes wrong, give clear recovery instructions but spare them the technical details. If you can fix it behind the scenes, even better.”</p>
  </div>

</div></div>

<div class="g6"><div class="pad">

  <div class="roboto">
    <p><strong>I should always know where I am</strong><br>“Give people confidence that they know their way around. Make places in your app look distinct and use transitions to show relationships among screens. Provide feedback on tasks in progress.”</p>
  </div>

</div></div>

<div class="c"></div>

<div class="g4 m3"><div class="pad">
  
  <p><img src="https://acko.net/files/android/clock1.png" alt="android clock: good"></p>
  <p><img src="https://acko.net/files/android/clock2.png" alt="android clock: bad"></p>
  <p><img src="https://acko.net/files/android/final.jpg" alt="my phone"></p>

</div></div>

<div class="g8 m1"><div class="pad">

  <h2>The Times They Are A-Changing</h2>

  <p>Level such criticisms at any good Android fanboy though, and you'll hear back a common refrain: "Yes, but, things are changing! It takes time to change a developer culture."</p>

  <p>That would be valid, if some of the worst offenders weren't completely new in the latest upgrade. You can't set a good example with one mediocre experiment after another.</p>
  
  <p>The other rebuttal is, "There's a replacement app for that, that's the beauty of Android." And indeed, I do use a custom launcher and root my phone. But the result is still a series of apps, not a mobile desktop.</p>

  <p>The way I see it, there are basically four tiers of Android apps right now.</p>

  <p>First, there's all the iOS apps that were ported over with no regard for the platform, which just feel like second-rate versions of better things. Luckily these are being replaced with native look-and-feel, with Twitter being a recent example.</p>

  <p>Second, there's the legacy of Android 2.x, whose widgets and pop ups still show up with alarming regularity, serving as a constant reminder to stay away from the Play Store as much as possible.</p>

  <p>Third, there's the mismatched Google apps from the No-Man's Land of Inbetween, like Play, Plus, Maps, Goggles, Reader and YouTube, which each have their own spin on the flatter UI with varying levels of blandness vs sophistication.</p>

  <p>Finally there's the modern Roboto Thin style of Jelly Bean, which oscillates strangely between a Braun-esque brilliance and a confusing form-over-function Sci-Fi UI not meant to be seen or used up close.</p>
  
  <p>There's nothing to tie it together but a bunch of disjoint cloud services. Are we all going to stay in our isolated messaging silos? Keep on emailing each other DropBox URLs? Substite mediocre AI for fine-grained control and precision?</p>

  <p>As an end-user, Android feels like it's a glimpse of something great now, but there are too many forces pulling it in different directions. If there's someone whose job it is to curate the Android experience and make it excel, their vision is not being realized effectively. Too much manual assembly is still required.</p>

</div></div>

<div class="g8 i2"><div class="pad">

  <p>The past decade, we've seen what was once just Apple's domain become mainstream, that is, bringing high-end UX design to software. But I can't help but feel some of the finesse has been lost. Gripes about iOS and OS X suggest the same is true for Apple as well.</p>

  <p>You hear a lot about "first time user experience" for example. But it's not about wrapping up your product like a present. It's about creating a connection of trust through empowerment and a little bit of emotional appeal: "This is for you, you can do amazing things with this." And that means "first-time" shouldn't refer to the first time you turn on the device, but the first time you use a device <em>for a particular purpose and context</em>. Travelling to Another Country should definitely be treated as a "first time" experience, same with How Do I Work This Camera, I Don't Have an App For This, I Don't Have Data Right Now, I Dropped It Down The Stairs, I Should've Cached This Map But I Didn't, My Friend Has a Windows Phone, etc. Throwing in more obnoxious tutorials is not the answer, creating affordance is.</p>
  
  <p>One of the most brilliant things Apple did for the iPhone was to make screenshots easy. People could show what they were doing with it, it empowered them to do so, popping up in articles and feeds. It took until Android 4 before you could do that without a USB debugger, so the only screenshots we got were taken by developers. Which platform has which image today? The iPhone isn't actually <em>that</em> intuitive, but most people already knew all about swiping and pinching by the time they picked one up for the first time. But the Android experience cannot come into its own when it's only chasing the walled gardens that already exist. It lacks agency for the user and pretends the cloud can pick up the slack.</p>

  <p>The most annoying part is, the worst bugs are well known, and the war over services is obvious. The issue tracker has long been full of reports and me-too-s and <a href="http://code.google.com/p/android/issues/detail?id=40365">dear-god-when-will-this-be-fixed</a>. Owning an Android means you accept having something that's always a bit broken, which doesn't integrate as well with anything you do as an iPhone does with its family, and just substitutes Google for Apple more and more. I'll stay on this side, I like my phone to be hackable, but that means being able to shut off the stupid warnings too, and having an OS actually worth hacking for.</p>

  <p>If the insides of this phone were as thoughtfully put together as the outside, Android could be to mobile what OS X was to the desktop in the 2000s. But so far, no dice.</p>

  <p>Above all, it's true what Steve Jobs said: Android is a stolen product. After so many years, there is little about the platform that can be said to set it apart from direct competitors. It's now just a mostly well done version of the same thing. I'm one of those old fogeys who has resisted getting a tablet, simply because I don't want a gianter phone. I want a <em>touch computer</em>, with everything that implies, not just something to do email and watch movies on.</p>

  <p><em>Quotes from: <a href="http://developer.android.com/design/get-started/principles.html">Android – Design Principles</a>, Google Inc. <a href="http://creativecommons.org/licenses/by/2.5/">(cc)</a></em></p>

</div></div>



]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Storms and Teacups]]></title>
    <link href="http://acko.net/blog/storms-and-teacups/"/>
    <updated>2013-03-24T00:00:00+01:00</updated>
    <id>http://acko.net/blog/storms-and-teacups</id>
    <content type="html"><![CDATA[<style type="text/css" media="screen">
.twit {
  background: #eee;
  background-image: url(/files/storms-teacups/twit.png);
  background-position: 50% 50%;
  background-size: 100% 100%;
  width: 56px;
  height: 56px;
  margin: 0 14px 7px 0;
  float: left;
  display: block;
  border-radius: 10px;
}
.serif .twit {
  width: 64px;
  height: 64px;
  margin: 2px 16px 8px 0;
}

.twit.blue {
  background-color: rgba(48, 160, 255, .2);
}

.twit.green {
  background-color: rgba(128, 255, 24, .2);
}

.twit.yellow {
  background-color: rgba(255, 240, 24, .25);
}

.twit.orange {
  background-color: rgba(255, 128, 48, .25);
}

ul.compact, ol.compact {
  margin-left: 56px;
}
.serif ul.compact, .serif ol.compact {
  margin-left: 64px;
}
ul.compact li, ol.compact li {
  margin-top: 0;
  margin-bottom: 0;
}
.twit-shim {
  display: table;
  height: 64px;
  margin-left: 80px;
}
.twit-shim span {
  display: table-cell;
  height: 64px;
  vertical-align: middle;
}
</style>

<div class="g8 i2 serif"><div class="pad">

<p>If you've been paying attention, you'll have seen a lot more discussions about gender, feminism and harrassment lately. The conversation mostly revolves around the latest incident of the day. I'd like to reflect on the bigger picture instead, and talk about some uncomfortable truths.</p>

<p>This is about how we act, online and offline, and why we do it.<br>Please read it top to bottom, or not at all.</p>

<p><em>Special thanks go to the folks who took time to provide feedback on drafts.</em></p>

<p class="m2"><em><big><span style="font-weight: bold">The examples used in this article, whether positive or negative, are chosen for their representative nature. They are not unique exceptions that deserve special sympathy, scrutiny or scorn.</span></big></em></p>

</div></div>

<div class="c m1"></div>

<div class="wide mt2">
  <a target="_blank" href="http://www.flickr.com/photos/ngmmemuda/4756594270/"><img src="https://acko.net/files/storms-teacups/storm.jpg" alt="Storm in a Teacup"></a>
</div>

<div class="g8 i2 mb1">
  <a target="_blank" class="credit" href="http://creativecommons.org/licenses/by/2.0/deed.en"><img src="https://acko.net/files/storms-teacups/cc.png" class="skip natural flat square" width="25" height="25" style="display: inline"></a>
  <a target="_blank" class="credit" href="http://www.flickr.com/photos/ngmmemuda/4756594270/">Juliana Coutinho</a>
</div>

<div class="g10 i1 m0 serif"><div class="pad">
  
<h3>Table of Contents</h3>

<ol class="compact">
  <li><a href="#shametweet">The Shametweet</a></li>
  <li><a href="#objectification">Objectification</a></li>
  <li><a href="#socialjusticewarriors">The Social Justice Warriors</a></li>
  <li><a href="#womeninopensource">Women in Open Source</a></li>
  <li><a href="#policy">The Anti-Harassment Policy</a></li>
  <li><a href="#beatingwhichodds">Beating Which Odds?</a></li>
  <li><a href="#filterbubble">Breaking Out of The Filter Bubble</a></li>
</ol>

<h2 id="shametweet" class="m4 flat">The Shametweet</h2>

<p>Atlassian, provider of software development infrastructure, <a href="https://twitter.com/atlassian/status/304693491102011392">sends out a tweet</a> to advertise one of their services:</p>

<blockquote>
  <span class="twit blue"></span>
  <em class="twit-shim"><span style="max-width: 465px;">If you're ready for a build server so pretty you could take it to the prom, you're ready for @Atlassian Bamboo.</span></em>
</blockquote>

<p>The response is immediate and harsh:</p>
  
<blockquote>
  <div class="c"></div>
  <span class="twit green"></span>
  <em class="twit-shim"><span>Sexist ads won't win you fans!</span></em>

  <div class="c"></div>
  <span class="twit yellow"></span>
  <em class="twit-shim"><span>Grow up and don't use gendered terms to promote your tech products</span></em></blockquote>

<div class="c"></div>

<p>A reply is made:</p>

<blockquote>
  <span class="twit blue"></span>
<em class="twit-shim"><span>Sorry you don't like the wording!<br>We weren't being gender specific though. Men are pretty&nbsp;too!</span></em>
</blockquote>

<p>Finally, cue the condescending follow ups:</p>

<blockquote>
  <span class="twit orange"></span>
<em>For fuck's sake, way to exhibit absolutely no understanding whatsoever of the&nbsp;subtleties&nbsp;of&nbsp;patriarchy.&nbsp;Get&nbsp;educated.</em></blockquote>

<p>Look closely and you'll see this pattern pop up more and more, in various forms. The key word is always <em>educate</em>, or more accurately, <em>re-educate</em>. The tone varies from feigned concern to outright hostility. <em>If only you weren't so ignorant, you wouldn't have made such horribly offensive statements.</em> Apologies are dismissed as insincere, a refusal to admit one's true sins.</p>

<p>But let's step back for a bit and look at what was said. First, Atlassian's reply is right, they weren't being gender specific, they merely compare a piece of software to <em>prom</em>. That's not what the indignant reader saw. They read between the lines, and substitute it with something like this:</p>

<ul class="compact">
<li>Women are expected by society to always be pretty. We think this is great.</li>
<li>Prom is a celebration of this institutional sexism. Let's trivialize it by comparing it to server technology.</li>
<li>We think you'll enjoy our use of sexism and buy our products.</li>
</ul>

<p>For sure, everyone has their own interpretation and (I hope) I'm exaggerating. But the tweet's supposed sexism is not actually there. The speaker's intent is completely ignored, the hurt feelings of the offended take priority. The reinterpretation itself is sexist: only women can be pretty.</p>

</div></div>

<div class="c"></div>

<div class="g3 m1 serif"><div class="pad">
  <img src="https://acko.net/files/storms-teacups/shametweet.png" alt="Shametweet" class="flat">
</div></div>

<div class="g9 serif"><div class="pad">

<p>The worst form of this behavior is what I call the <em>Shametweet</em>. This is when someone retweets a statement—usually a perceived insult directed at themselves—without any further comment. The tweeter seemingly considers it beneath themselves to address the insolence directly. Instead, they choose to demonstrate their superior sensibilities to their followers. Those will then jump to his or her's defense, making the problem go away with a single click of a button, while they maintain an aura of innocent plausible deniability.</p>

<p>To my lack of surprise, it's mostly women who I see doing this, voluntarily turning themselves into objects, letting others claim their agency, and usually men who are all too eager to jump to the rescue, even when it's not requested. Some celebrities do it too, sicking a million followers on a target who failed to stroke their ego that morning. More than a few of these fragile celebs are men.</p>

</div></div>

<div class="c"></div>

<div class="g10 i1 m1 serif"><div class="pad">

<h2 id="objectification">Objectification</h2>

<p><a href="http://en.wikipedia.org/wiki/Anita_Sarkeesian">Anita Sarkeesian</a> dislikes sexist tropes and objectification of women in video games and wants to bring this problem to light. As one might expect with anyone who does anything on the internet, trolls show up, and insults and accusations of sexism start flying around. <a href="http://thinkprogress.org/alyssa/2012/07/06/512170/anita-sarkeesian/?mobile=nc">Things get ugly</a>, and valid criticism is lost in a sea of crud. Anita cleverly uses the <a href="http://en.wikipedia.org/wiki/Streisand_effect">Streisand effect</a> to her advantage, gets publicity in both feminist and general media, ending in a successful $158K Kickstarter campaign to produce a web video series.</p>

<p>Jezebel, billing itself as <em>"Celebrity, Sex, Fashion for Women"</em>, is one of the sites <a href="http://jezebel.com/anita-sarkeesian/">eagerly siding with Anita</a>. It appeals to their readership: a young audience of mostly women who enjoy seeing another woman doing her own thing, more so when it irritates men and advances the status of the sisterhood—if the comments are anything to go by.</p>

</div></div>

<div class="g7"><div class="pad">

<p><a href="http://jezebel.com/5989947/why-is-michelle-williams-in-redface"><img src="https://acko.net/files/storms-teacups/jezebel.png" alt="Why is Michelle Williams in Redface?"></a></p>

</div></div>

<div class="g5 serif"><div class="pad">

<p>Fast forward. Jezebel asks <a href="http://jezebel.com/5989947/why-is-michelle-williams-in-redface">"Why Is Michelle Williams in Redface?"</a>, <em>"You should know better"</em>.</p>

<p>Her transgression was to appear on a fashion magazine cover <em>"dressed in a braided wig, dull beads, and turkey feathers [...] in a flannel shirt, jeans, and [...] some sort of academic or legal robe. [...] An attempt to portray reservation nobility [...] like she's the member of another race."</em></p>

</div></div>

<div class="c"></div>

<div class="g10 i1 m0 serif"><div class="pad">

<p>But they don't stop there. This tasteless display is in fact <em>"akin to putting a picture of a Gentile in a stereotypical Jewish getup on the cover of Adolf Hitler's Mein Kampf"</em>. <a href="http://en.wikipedia.org/wiki/Godwin's_law">Godwin</a> triumphs once again.</p>

<p>The writer may indeed have a point in there somewhere, that is, about stereotypes of First Nations cultures. But the irony is so thick you can spread it like Nutella.</p>

<p>Jezebel eagerly celebrates the advances of women over male-dominated society at every turn, decries Patriarchy and rings the alarm bell whenever supposed standards of equality and self-determination are violated. Now they complain that <em>an industry they focus on, which treats people like objects to be dressed and painted, didn't objectify a woman in a tasteful enough fashion.</em></p>

<p>They should do an exposé on the Emperor's wardrobe next.</p>

<p>Who is it really, that is pressuring women to be passive, immaculate and above all, politically correct dolls? Is it really all men's fault? Or is it <a href="http://thelastpsychiatrist.com/2013/01/no_self-respecting_woman_would.html">fueled by media and advertising</a> that bills itself <em>"For Women"</em> in giant pink letters, but really seems to be just about <em>"Judging Women"</em> instead, telling them they need to look better, be likeable supermoms as well as executives, but deserve to have it all, honest?</p>

</div></div>

<div class="c"></div>

<div class="g4 m1 serif"><div class="pad">

<a href="http://godofwar.wikia.com/wiki/File:Ghost_of_sparta_gorgon.jpg">
  <img src="https://acko.net/files/storms-teacups/gorgon.jpg" alt="Gorgon" class="fit" style="margin-top: 10px">
</a>

</div></div>

<div class="g8 m0 serif"><div class="pad">

<p>On the other side, gaming sister-site Kotaku asks <a href="http://kotaku.com/5991145/shes-sexy-now-kill-her">"She's Sexy. Now kill her?"</a>, questioning the <em>"humiliation of sexualized females"</em> in <em>God of War: Ascension</em>. In this game's bloody quest of revenge, after a couple hours of brutally murdering several armies of mythological creatures one by one, you stab the Medusa-like <em>Gorgon</em> in the chest. On top of its giant snake body, right where its <em>breasts</em> are. Gasp.</p>

<p>This scene summarizes <em>"all [the] issues with violence against sexualized female characters in one nutshell."</em> But after describing it in the context of the game, only one real objection remains: <em>"Breasts code some enemies as female, [...] violence against [these] body parts is disturbing,"</em> and is not the usual <em>"norm in games".</em></p>

</div></div>

<div class="c"></div>

<div class="g10 i1 m0 serif"><div class="pad">

<p>The game is presenting <em>"a form of feminine beauty that associates exposed, large breasts as beautiful."</em> The author seems to be confusing "sexualized" and "sexy", as if sexualization is only what turns <em>him</em> on—I think it's breasts—and something must be sexualized before it can be arousing. Apparently if the Gorgon had been obese and flat-chested, there'd be no issue in putting it down. Which is exactly what <a href="http://godofwar.wikia.com/wiki/Euryale">Euryale</a> looked like, the repulsive Gorgon the author must've killed in the <em>previous game</em>.</p>

<p>This attempted pro-woman analysis of sexualized portrayal seems to suggest that a feminized body is automatically sexual, but only if she's hot enough, like say, the <em>"final, sexy boss."</em></p>

</div></div>

<div class="c"></div>

<div class="g9 m0 serif"><div class="pad">

<h2 id="socialjusticewarriors" class="m3">The Social Justice Warriors</h2>

<p>Skeptic blogger and retired medical doctor Harriet Hall writes a post, titled <a href="http://www.sciencebasedmedicine.org/index.php/i-am-not-your-enemy-an-open-letter-to-my-feminist-critics/">I Am Not Your Enemy: An Open Letter to My Feminist Critics</a>. She clarifies exactly what she said and meant on a previous occasion. The comments then continue to argue back and forth about what it all means.</p>

<p>It goes back to <a href="http://phawrongula.wikia.com/wiki/TAM_2012_T-Shirt_Manufacturversy">a t-shirt she wore at a conference</a>, stating she <em>"felt safe and welcome"</em> and was <em>"just a skeptic, not a 'skepchick', not a 'woman skeptic', just a skeptic"</em>. This shirt was apparently so offensive and dehumanizing it <a href="http://freethoughtblogs.com/butterfliesandwheels/2012/07/in-your-face/comment-page-1/#comment-223155">reduced one of its victims to tears</a>.</p>

</div></div>

<div class="g3 m4 serif"><div class="pad">

<a href="http://phawrongula.wikia.com/wiki/TAM_2012_T-Shirt_Manufacturversy"><img src="https://acko.net/files/storms-teacups/harriet-hall.jpg" alt="Harriet Hall's controversial tshirt" class="fit"></a>

</div></div>

<div class="c"></div>

<div class="g10 i1 m0 serif"><div class="pad">

<p>All of this is fallout from the scandal known as ElevatorGate. A man at a conference asked Rebecca Watson up for coffee in an elevator, after a late night in the hotel bar, and accepted no for an answer. Cue the public shaming based on her <a href="http://www.youtube.com/watch?v=uKHwduG1Frk">one-sided account</a>, using her position as a conference speaker, and the inevitable backlash. The man himself however has wisely chosen to stay out of it and remains unidentified. It prompted Richard Dawkins to point to <a href="http://www.redlightpolitics.info/post/7263060171/dear-muslima-stop-whining-will-you-yes-yes-i">more serious women's issues to possibly worry about</a>, who was then <a href="http://www.blaghag.com/2011/07/richard-dawkins-your-privilege-is.html">chastized for speaking from white male privilege</a>. This scandal, entirely based on hearsay, is still going on a <em>year later</em>.</p>

<p>In fact, Harriet's thread features an appearance from <a href="http://www.sciencebasedmedicine.org/index.php/i-am-not-your-enemy-an-open-letter-to-my-feminist-critics/#comment-112631">Rebecca herself</a>. She takes <em>"ten&nbsp;precious minutes"</em> out of her busy schedule to explain she <em>"doesn't really think of [her] at all"</em>, after clarifying why she feels the post talks about her directly. Despite admitting to writing and deleting both a blog post and a private email on the subject, Rebecca says Harriet <em>"doesn’t actually deserve an explanation, [or] real estate in my head"</em> which is why she <em>"let others argue over it"</em>. Which she says right after arguing over it.</p>

<p>Does this sound at all familiar? She includes that she would be <em>"concerned for [her] personal hygiene"</em> for wearing one shirt several days in a row. I'm not making this up.</p>

<p>Like Dawkins, I wonder: <em>Don't these people have more important things to get angry at?</em> Are they just self-absorbed, seeking publicity through controversy? Some undoubtedly are, but for the majority I think it's far more simple.</p>

<p>It's fair to ask: why are they so bothered and offended, spending their free leisure time organizing miniature online protests, thread after thread? Was the t-shirt (or the tweet) a direct, personal insult? Did it insult a class of people they belong to? Is it specific enough that someone could reasonably argue it applies to them, but not the next person? No.</p>

</div></div>

<div class="g3 m1 serif"><div class="pad">

  <a href="http://en.wikipedia.org/wiki/Rage_comic">
  <img src="https://acko.net/files/storms-teacups/rageface.png" class="flat square" alt="rageface"><br>
  <img src="https://acko.net/files/storms-teacups/yuno.png" class="flat square" alt="yuno">
  <img src="https://acko.net/files/storms-teacups/rageface2.png" class="flat square" alt="rageface"><br>
  </a>

</div></div>

<div class="g9 m2 serif"><div class="pad">

<p>So why take it personally? It's because it reminds us of an uncomfortable truth about ourselves or the world. In Atlassian's case, it's that beauty has a dark side, and it gives some people an unfair advantage or disadvantage. <em>Did I get this job because of my talents or my looks? Do I present myself badly? Do people judge me by things beyond my control? <a href="http://www.youtube.com/watch?list=UUOGeU-1Fig3rrDjhm9Zs_wg&amp;feature=player_embedded&amp;v=7IJyRAUxtAQ">Do I have a weird face?</a></em> It reminds us of all the times we've experienced this ourselves, and if you have children, of all the times they will too. The internet becomes a mirror for our own insecurities, and we read our worries into everything.</p>

<p>In Harriet Hall's case, it's the acknowledgement that life is what we make of it, that people disagree with us more than we like to admit, and that often the best thing to do is shrug and not let it bother you, and focus on results rather than labels. Though again, everyone's interpretation is different.</p>

<p>But we don't want to admit that, our pride does not allow it. We'd much rather explain our unease by assuming it was inflicted deliberately, and we make up convenient reasons why that is so, why we were targeted. <em>See, Atlassian is just another sexist tech company, they can't even tweet without insulting every woman on the planet!</em> Harriet Hall, born in 1945, the <em>second ever female intern</em> in the US Air Force, must be an ignorant ditz when it comes to matters of feminism, because of one <em>smelly</em> t-shirt. If you don't see it the same way, well, you're just not educated enough to read between the lines.</p>

</div></div>

<div class="c"></div>

<div class="g10 i1 m0 serif"><div class="pad">

<p>It's both men and women who do it. We can argue who is more at fault until the cows come home, but when it comes to sexism it's fair to say men take the brunt of the blame, and are the ones expected to make amends. It's completely one sided, and it's another one of those convenient excuses that we substitute for the real thing. We don't want to talk about the full complexity at play here. Indeed, the closest feminism gets to acknowledging this is, <em><a href="https://www.google.com/search?q=%22Patriarchy+hurts+men+too%22">Patriarchy hurts men too!</a></em> So it's not my fault, just the result of every single choice I've ever made?</p>

<p>When someone points out that viewing everything through a uniquely feminist and female-oriented lens gives a skewed perspective, a rapid fire meme is returned: <em><a href="https://www.google.com/search?q=what+about+the+menz">"But what about the mennzzz?"</a></em> Attempting to show that <a href="http://www.westernstandard.ca/website/article.php?id=2775">inequality applies to both genders</a>, quite often in women's favor, is considered derailing. Showing that the feminist interpretation of history as unbridled Patriarchy <a href="http://owningyourshit.blogspot.ca/2013/01/transcript-of-hi-danielle-nice-to-make.html">is unrealistic</a>, and that feminism has long ago developed <a href="http://www.womenagainstmen.com/media/feminism-is-a-hate-group.html">its own oppressive and hateful character</a>, is dismissed as misogyny, even when it's women saying it.</p>

<p>There's more handy tropes to end attempts at nuance and shut down discussion: Check Your Privilege, Stop JAQing off (Just Asking Questions), Mansplaining, Victim Blaming, Nice Guy, <a href="http://kateharding.net/2009/10/08/guest-blogger-starling-schrodinger’s-rapist-or-a-guy’s-guide-to-approaching-strange-women-without-being-maced/">Schrödinger's Rapist</a>. The list goes on, and all of a sudden, concerns about <em>gendered slurs</em> no longer apply.</p>

<p>The so-called <a href="http://atheismplus.com">"safe space"</a> that these online social justice groups claim to seek, is just another word for a censored space, and a hypocritical one at that. It's one where certain ideas and thoughts are not to be uttered, and must be replaced by less realistic and less worrisome ones. But no true safe space exists, as offense is always in the eye of the beholder.</p>

<p>Listening involves an interpretation of what people thought it meant they heard.</p>


<h2 id="womeninopensource" class="m3">Women in Open Source</h2>

<p>Statistics show that women observe sexism online to a higher degree than men, <a href="http://flosspols.org/deliverables/FLOSSPOLS-D16-Gender_Integrated_Report_of_Findings.pdf">particularly in tech and open source</a>. <a href="http://blog.wikimedia.org/2012/03/30/ada-initiatives-quest-to-bring-women-to-open-source/">Recommendations are made</a> on how to make the community more friendly to women, and most suggestions involve re-educating men to reduce their blindness. More so, it's implied that once the atmosphere is respectful enough, women will join and equality will be achieved.</p>

</div></div>

<div class="c"></div>

<div class="g4 m1 serif"><div class="pad">
  <img src="https://acko.net/files/storms-teacups/people.png" class="fit" alt="Gender in open source (2006)">
</div></div>

<div class="g8 m0 serif"><div class="pad">
  
<p>Sorry, but I don't buy it, because as late as 2006, <a href="http://flosspols.org/deliverables/FLOSSPOLS-D16-Gender_Integrated_Report_of_Findings.pdf">28% of participants in proprietary software were women, but only <span class="green">1.5%</span> in open source</a>. Most open source projects start out as hobbies, created by one person in their spare time. If the community was such a sexist hell for women, wouldn't you expect the web to be littered with the abandoned works of that 1/4th of professionals who are women, who were turned off by how it was received once published? Instead, I find that female-founded projects are far and few, and calls for women to participate consist mainly of inviting them into existing projects, and speaking at established conferences about existing technologies.</p>

<p>Is the increasing role of women in open source a consequence of empowerment and self-direction? Or does it stem from the fact that open source is becoming more important in commercial use, and now more women are tagging along? It's both, naturally, but the huge gap between the two gender ratios can't be reduced to abuse and sexism. For a multitude of reasons, women simply aren't as interested as a group.</p>

</div></div>

<div class="c"></div>

<div class="g10 i1 m0 serif"><div class="pad">

<p>A big part of the problem is confidence, and starts much earlier: <em>you must be this smart to be in open source</em>, or so people think. <a href="http://webchick.net/about">Angela Byron</a>, winner of the 2008 Google-O'Reilly Best Contributor award, called to <em>"Fight the Einstein Perception"</em> in <a href="http://webchick.net/files/presentations/women-in-open-source-owv09-2009-06.pdf">Women in Open Source</a>. It took Google's Summer of Code to convince her to take the plunge and make the career change. Programs like that are great to bring fresh talent into a community, but they won't cause the seismic shift in gender balance that feminism requires. If we want more women in open source, shouldn't we encourage them to just do their own thing, as those 98.5% of contributors who were male seemed to be doing?</p>

<p>Open source is claimed to be a meritocracy, but it really isn't. Once two people start modifying the same code, politics get involved, and I can certainly speak from experience that decisions at the top of an open source project are more about people and their interests than code. It isn't enough to create a good solution, it must be advocated and accepted, and apply to a wide variety of existing scenarios. If the work isn't good enough and fails, reputations take a hit. Like this:</p>

<p><a href="https://lkml.org/lkml/2012/12/23/75">
  <img src="https://acko.net/files/storms-teacups/linus.png" class="fit" alt="Linus Torvalds">
</a></p>

<p>Linus Torvalds can <a href="https://lkml.org/lkml/2012/12/23/75">act like a complete asshole</a>, self-admittedly so, chew out his (male) contributors, and nobody in particular seems to mind. Linux is successful either despite or because of it.</p>

<p>Linus builds and directs software millions rely on. His abrasive tone reflects the importance of the issues he deals with on a daily basis. So far, his peers have deemed it socially acceptable. You may hate this, but you can't ignore it.</p>

<p>Can we really say with a straight face that he could talk the exact same way to a female contributor, and nothing would be different? In a culture where "never hit a woman" is considered a valid rule by many, men are the <a href="http://winnipeg.ctvnews.ca/feds-pledge-200k-to-end-domestic-violence-against-women-through-work-with-men-boys-1.1194508">default assumed aggressor</a> in domestic violence, and expected to chase the burglar—another man <em>no doubt</em>—out of the house to protect their wife and children? Or would it spawn thread after thread of discussions of just how bad the transgression was, and how to make sure it never happens again?</p>

<p>Open source culture is quite competitive, but the biggest problem an open source contributor has isn't criticism, it's getting people to pay attention in the first place. Ironically, this is something women are innately privileged in: studies show women <a href="http://www.ncbi.nlm.nih.gov/pubmed/15491274">have automatic in-group bias</a>—women like women more than men like men—that people prefer their mothers to their fathers, and men are universally associated with negative behavior such as violence. It's propagated in the popular stereotypes of the bumbling husband, the insensitive jock, the aggressive bully, and so on.</p>

</div></div>

<div class="g5 m1 serif"><div class="pad">

  <a href="http://news.nationalgeographic.com/news/2013/04/life-expectancy-map?source=hp_dl2_news_life_gender_gap_2130319"><img src="https://acko.net/files/storms-teacups/natgeo.png" class="fit" alt="National Geographic: Ladies Last"></a>

</div></div>

<div class="g7 m0 serif"><div class="pad">
  
<p>That perspective is dismissed by feminists as lashing out from male privilege, and the fear of losing it. But how privileged are men over women, when their life expectancy <a href="http://www.bbc.co.uk/news/health-21760905">recedes further from that of women</a> the lower the standard of living? Is there a Kickstarter I can donate to for that? No, instead <a href="http://news.nationalgeographic.com/news/2013/04/life-expectancy-map?source=hp_dl2_news_life_gender_gap_2130319">National Geographic states matter of factly</a> that it's a <em>"troubling trend"</em> and a <em>"wake up call"</em> that men's life expectancy is getting <em>closer</em> to that of women in the US, because it means <em>women are gaining less</em>. They use the margin by which women outlive men as if it's some sort of index of prosperity.</p>

</div></div>

<div class="c"></div>

<div class="g10 i1 m0 serif"><div class="pad">

<p>Hey, remember that time when Hillary Clinton said <em><a href="http://en.wikiquote.org/wiki/Hillary_Rodham_Clinton">"Women have always been the primary victims of war"</a></em>? Because they <em>"lose their husbands, their fathers, their sons in combat."</em> A woman who survives is more of a victim than a man who dies for her, please be sure to <em>educate</em> yourself on this.</p>

<p>Could it be that the sexism women say they are constantly subjected to online, is merely the flipside of a coin? One that allows them to cultivate attention with nothing more than a well-chosen avatar, and which men eagerly give to them? How many women forego the make-up in their profiles and videos <em>before</em> lamenting the unsolicited date proposals, awkward as they may be?</p>

<p>I'm not ignoring cases like <a href="http://en.wikipedia.org/wiki/Kathy_Sierra">Kathy Sierra</a> and the persistent, real harassment she received, but let's not forget that it was inflicted by individuals upon individuals, not on <em>womankind</em>.</p>

<p>When the overwhelming majority of open source contributors are men fighting for recognition, do you suppose some of them might feel some resentment that a woman can walk into a room, real or virtual, and make everyone's head turn? If so, do women's concerns deserve automatic precedence over men's? The country I live in has a <a href="http://www.swc-cfc.gc.ca/abu-ans/min/index-eng.html">Minister for the Status of Women</a> after all. Not for Equality.</p>

<h2 id="policy" class="m3">The Anti-Harrassment Policy</h2>

<p>To attend or speak at JSConf, you must agree to a <a href="http://jsconf.com/codeofconduct.html">code of conduct</a>. Its goal is to create a positive, harassment free environment, something which I am all for. The policy is starting to be adopted verbatim by other conferences, like <a href="https://us.pycon.org/2013/about/code-of-conduct/">PyCon</a>.</p>

<p>But the wording explicitly defines harrassment as including <em>"offensive verbal comments"</em>, specifically <em>"related to gender, sexual orientation, disability, physical appearance, body size, race, religion, sexual images in public spaces, deliberate intimidation, stalking, following, harassing photography or recording, sustained disruption of talks or other events, inappropriate physical contact, and unwelcome sexual attention."</em></p>

<p>How many of the storms in teacups above would fall under this wide umbrella? If the yardstick to be applied is <em>offense</em>, then this basically forces everyone to walk on egg shells <em>and</em> admit guilt ahead of time. <em>"Participants asked to stop any harassing behavior are expected to comply immediately."</em> There is no room here to discuss the merit of a particular case, to measure the validity of a claim.</p>

</div></div>

<div class="g6 m1 serif"><div class="pad">

  <a href="http://adainitiative.org/2013/02/keeping-it-on-topic-the-problem-with-discussing-sex-at-technical-conferences/"><img src="https://acko.net/files/storms-teacups/ada.png" class="fit" alt="Keeping it on-topic: the problem with discussing sex at technical conferences"></a>

</div></div>

<div class="g6 m0 serif"><div class="pad">
  
  <p>Indeed, the latest is that we <a href="http://violetblue.tumblr.com/post/44107008572/what-happened-with-my-security-bsides-talk">cancel the talk first, ask questions later</a>, based on the concerns of a single complaint over a title <em>without a summary</em>. The threat of going public was possibly made, but accounts differ. I find the Ada Initiative's <a href="http://adainitiative.org/2013/02/keeping-it-on-topic-the-problem-with-discussing-sex-at-technical-conferences/">first response to the situation revealing</a>.</p>

  <p>While stressing the real issue is staying on topic and not devolving into unnecessary sexual talk, every negative point raised appears to concern only women. <em>"Sexual topics [...] can be perceived as encouragement to humiliate, objectify, and assault women, regardless of the intent of the speaker."</em> And, <em>"Many people are unable to separate 'talking about sex' and 'saying derogatory things about women'."</em> Their response shows they assumed the talk would not be <em>"done in a woman-positive way"</em>. That is, a talk featuring a female speaker who blogs about harm reduction.</p>

<p>At no point do they express regret at having silenced a voice. <em>"Be considerate and thoughtful,"</em> it ends.</p>
  
</div></div>

<div class="c"></div>

<div class="g10 i1 m0 serif"><div class="pad">

<p>Let me borrow a quote from Stephen Fry: <em>"The only people who are obsessed with food are anorexics and the morbidly obese, and that in erotic terms is the Catholic church in a nutshell."</em> You'll never see more talk of sexism and rape than on feminist websites.</p>

<p>Trigger warnings, humiliation, objectification, assault, rape culture: feminism's opinion of <em>neither men nor women</em>'s abilities to act mature around each other seems particularly high.</p>

<p>As an aside, have you ever noticed how Tumblr isn't just a hub for <a href="http://www.huffingtonpost.com/2012/12/21/nice-guys-of-okcupid-tumblr_n_2341720.html">bold feminism</a>, but also <em>erotic fanfics</em>? And by 'erotic' I mean <a href="http://twikinkfest.tumblr.com/">gay sex of dubious consent set in the Twilight universe</a>. You know. Rape. That fangirls write and fantasize about. And joke about in hushed tones at Comic-Con. Is that <em>woman-positive</em> enough, or are the lines blurring a bit?</p>

<p>More recently, <a href="https://news.ycombinator.com/item?id=5398681">someone lost their job</a> after public shaming involving an overheard and <em>misinterpreted</em> comment about "forking" and "dongles", and the guy still felt the need to <em>apologize profusely</em> to the female offendee. Her media presence exceeds his by far and includes tweeting about <a href="http://cdn0.dailydot.com/uploaded/images/original/2013/3/20/pycon8.jpg">"[putting] something into your pants [...] like a bunch of socks"</a>. Meanwhile followers <a href="http://cdn0.dailydot.com/uploaded/images/original/2013/3/20/pycon13.jpg">thanked her for her bravery</a>, that is, snapping a picture with a smile and throwing it to the lions. Who was abusing who here?</p>

<p>Of course it blew up into its own internet storm, but can you blame people for responding in kind to an example that's been so clearly set?</p>

<p>People read <em>Woman fired for getting upset at man's joke</em> and fill in the rest of the story themselves, like this <a href="http://feministing.com/2013/03/22/weekly-feminist-gif-25/">animated GIF equivalent</a> of a temper tantrum. More <a href="http://www.forbes.com/sites/deannazandt/2013/03/22/why-asking-what-adria-richards-could-have-done-differently-is-the-wrong-question/">dignified publications</a> instead carefully explain <em>"Why asking what [she] could have done differently is the wrong question"</em>, that is, the one question in this entire fiasco the <em>rest of us</em> could actually learn something from.</p>

<p>Judging a book by its cover is the new tolerance. We throw people into the stocks based on feelings while ignoring intent and assuming victimhood. This is why I fundamentally disagree with equating offense with harassment: it provides unlimited ammo and shuts down discussion rather than giving people the benefit of doubt. It elevates the exception to the norm, by presuming the worst.</p>

<p>Here's a clause I'd like to see instead: if you choose to air minor incidents in public one-sidely—or threaten to do so—rather than resolving the matter in private, you lose by default. Leave the soapbox for the people who actually need it. Also, if a speaker has been invited and has spent time preparing a talk, it's the most basic courtesy to honor that invitation, no matter what. Let people judge it on its own merits. We attend conferences to hear other points of view, not to be sheltered from them.</p>

<p>As for the <a href="http://geekfeminism.wikia.com/wiki/Creeper_Move_cards">creeper move cards</a>, please toss them out, because that's not how adults resolve differences. How gender-neutral is the word <em>creep</em> anyway, and how would you respond to being dismissed with a generic scrap of paper printed from the internet?</p>

<p>If you reduce communication to such a passive aggressive and childish statement, color me unsurprised when you receive an <a href="http://i.imgur.com/usNcR.png">equally childish response</a>, especially in a community that thrives on subversion and creative re-use of things they're not supposed to toy with. It's the exact same attitude that protects us from DRM, eagerly tests claims of privacy and security, and liberates closed technology for those without access. You cannot have one without the other.</p>

<p>Conferences are social gatherings, and sexuality is a normal part of that. I know several happy couples who met at a tech conference, coming from different cities or even countries. Are we to assume that none of them used this opportunity to hook up, and that relationships never happen without ambiguity and misunderstanding? It's not a binary choice between tweeting <em>#ITappedThat</em> and turning conferences into convents.</p>

</div></div>

<div class="g9 m0 serif"><div class="pad">

  <p>But why does it seem like there are so many socially maladjusted men roaming these conferences? Does anyone care about the reasons at all, like say, the high rate of autism-spectrum disorders among geeks? Could it be due to the emphasis schools and universities place on non-intellectual pursuits like sports and popularity, and the bullying that results from it? Because it seems to me what some socially awkward hackers have done is exactly what the social justice warriors want: they've created a <em>safe space</em> for themselves, where only their own rules apply.</p>
  
  <p>I never hear much about the effect "Jock culture" has on men, but quite a lot about "Rape culture" and women. We stereotype geeky men as neckbearded basement dwellers whom women are to be protected from unilaterally, rather than working towards real resolution. I don't mind the word <em>neckbeard</em> personally, it can be a <a href="https://www.neckbeardrepublic.com">humorous badge of pride</a>, but if it's offensive to anyone, surely that's men, not women?</p>

</div></div>

<div class="g3 m1 serif"><div class="pad">

  <a href="https://www.neckbeardrepublic.com"><img src="https://acko.net/files/storms-teacups/neckbeard.png" class="fit" alt="Neckbeard Republic"></a>

</div></div>

<div class="c"></div>

<div class="g10 i1 m1 serif"><div class="pad">

<h2 id="beatingwhichodds">Beating Which Odds?</h2>
  
<p>In a post titled, <a href="http://2012.jsconf.eu/2012/09/17/beating-the-odds-how-we-got-25-percent-women-speakers.html">Beating the Odds</a>, the JSConf organizers explain how they got 25% of their speakers to be women. The choice quote is: <em>"Our industry systematically biases against 50% of great speakers and misses out on a significant amount of talks, topics, discussion and thus progress."</em> The argument is that, despite only 10% of proposals coming from women, an anonymized selection process disproportionately favored female speakers.</p>

<p>Under a more traditional selection process, these women's valuable and apparently superior contributions would have been ignored. Note how they ignore the ratio of men and women in the industry, and assume this would not affect the gender ratio of good candidates: 50% of them are assumed to be women. That's not how statistics work.</p>

<p>The results: <em>"Our highest ranked talk is from a woman and we know we wouldn’t have gotten that talk without the outreach we did."</em> And: <em>"We invited 35 women to submit to the [Call For Proposals], of these 13 ended up submitting one or more proposals, 5 women submitted on their own."</em></p>

<p>So basically, there is a significant amount of pre-selection going on here. In their outreach to female candidates, organizers naturally prefer women who they already think will make good speakers. These candidates then further self-selected based on their own confidence and skill. Less than half of female speakers submitted on their own. Meanwhile, the 162 proposals from men came from the usual pool, requiring no unique outreach. Despite extolling the virtues of anonymized selection, the process was biased to favor talented women from the get go, and it's no surprise women sent in better proposals as a group.</p>

<p>Given the rates of commercial and open source tech participation for women, getting 25% female speakers is a high number, assuming fair random sampling, <em>beating the odds</em>. But it's not random at all. The cure for sexism is apparently... more special treatment for women?</p>

<p>It also bothers me on a personal level: I'm gay, and feel equally excluded when someone puts a picture of Natalie Portman in their JavaScript talk. But even if I wasn't, who's to assume my opinions on the matter would fall in line with the cliché? When people do diversity spot checks of speaker panels and rally the horde, I get counted as just another dude propagating patriarchy and hetero&shy;normativeness. What does it tell you when the first thought upon seeing a lone woman in a line-up is <em>token female</em> rather than <em>trailblazer</em>?</p>

<p>Now, I'm not against setting a good example, and I realize the perception of a boy's club can be a barrier to entry. However that shouldn't distract us from what equality of opportunity actually looks like. In tech, it's nowhere near a 50 / 50 gender split, because the imbalance starts much earlier, with more men than women going into <a href="http://en.wikipedia.org/wiki/STEM_fields">STEM fields</a>, despite the fact that <a href="http://nces.ed.gov/fastfacts/display.asp?id=72">3 women now graduate for every 2 men</a>.</p>

<p>Can we at least give women the benefit of the doubt and assume that they go after what interests them, rather than being unable to choose differently? Even in the <a href="http://www.wikigender.org/index.php/Gender_Equality_in_Norway">most gender-equal country in the world</a>, Norway, STEM fields are still male dominated and the social sector remains female dominated, despite decades of fervent pro-equality policy and education.</p>

</div></div>

<div class="g3 m1 serif"><div class="pad">

  <a href="http://vimeo.com/19707588"><img src="https://acko.net/files/storms-teacups/hjernevask.jpg" class="fit" alt="Hjernevask"></a>

</div></div>

<div class="g9 m0 serif"><div class="pad">
  
<p><a href="http://chronicle.com/blogs/brainstorm/are-women-partly-to-blame-for-the-gender-gap-in-stem-fields/22106">How solid and gender-neutral</a> is the research that traces this all back to social pressure? The 2010 documentary <a href="http://vimeo.com/19707588">Hjernevask</a> (Brainwash) provided a very revealing answer to this question and others, causing a stir in the Norwegian academic community. I highly recommend watching it, there are English subtitles. I found the resemblance to creationism and intelligent design striking: supposed scientists were dismissing observations out of hand because of perceived implications, questioning the author's motives instead. But <a href="http://en.wikipedia.org/wiki/Sexual_dimorphism">sexual dimorphism</a> doesn't imply patriarchy, any more than evolution implies <a href="http://en.wikipedia.org/wiki/Social_Darwinism">social darwinism</a>.</p>

</div></div>

<div class="c"></div>

<div class="g10 i1 m0 serif"><div class="pad">

<p>Some choice facts from honest nature vs. nurture research: <a href="http://en.wikipedia.org/wiki/Empathizing–systemizing_theory">even day-old babies show a measurable difference</a> in interest between boys and girls, when presented with both a mechanical toy and a human face. <a href="http://social.jrank.org/pages/300/Heredity-Versus-Environment.html">Genetically identical twins have similar IQs and depression rates</a> and research with adopted children shows a similar relation to their biological parents, much more than their adoptive ones. This is no reason to treat individuals any different, but some averages differ innately across gender lines, and I don't see that as something we can or should fix by overcompensating.</p>

<h2 id="filterbubble" class="m3">Breaking Out of The Filter Bubble</h2>

<p>Above all, there's a common thread I can't ignore. The women I admire and respect in tech did so primarily on their own merit, letting nobody speak for them but themselves. Like the men I look up to, they point people to their accomplishments, not their likeability. Their Twitter bios don't consist of one ism after another, showing their adherence to a pre-approved set of beliefs. They don't let random trolls derail them, and they don't find themselves at the center of fires of their own making, expecting others to put them out.</p>

<p>It's also the ideal I aim for. When a couple thousand people on YouTube <a href="http://www.youtube.com/watch?v=bcu8ZdJ2dQo">told me I had no life</a>, I laughed my ass off at the absurdity. I'd just created an accidental experiment in viral media, and learned tons in the process. Meanwhile they just watched a video they apparently didn't like, and then wasted more of their time to point this out. They weren't talking about me, they were talking about themselves.</p>

<p>When people told me <a href="http://acko.net/files/storms-teacups/killunix.png">I killed Unix</a>, that I should be shot, and that I was just some <a href="http://acko.net/blog/on-termkit/">idiot designer</a> who didn't understand code, I didn't have the privilege to retweet the offense and let my posse roll in. I could only ignore it, taking the reputation hit, or refute the misconceptions with arguments and insight, changing people's minds one post at a time. The arrogant Unix greybeards who bugged me in private? Simple: you bait them into telling you everything they know, pan for gold amongst the mud, and move on. One person against the might of Twitter, HackerNews and Reddit: it's really not so bad, just don't take it too seriously. Once the novelty wears off, the bystander effect kicks in, unless you keep stoking the fires yourself.</p>

<p>Of course, I did let it inform my choices: I stopped working on that project in public, realizing I wasn't going to get much useful participation until much later, and I could do without the distraction. But it no longer bothers me, it's just one in a long line of useful experiments. The lingering frustration I feel is about people's short sightedness, not bruised ego. Ever since then, I treat the internet like I would a lovable-but-backwards grandparent, who makes racist comments over Christmas dinner. <em>Yes Grandma, it's all the damn commie jews and faggots' fault, now, who wants dessert?</em></p>

<p>No, I don't feel bad for dropping in those sacrilegious words in there just now. I like to think you are mature enough to let those letters pass under your eyes, without burning me at the stake because it reminds you of something unpleasant. I trust you to focus on the couple thousand words I started with, rather than just two at the end. See, the reason people say <em>the n-word</em> instead of <em>nigger</em> when talking about racism, is that they don't yet realize they too <a href="http://thelastpsychiatrist.com/2013/01/no_self-respecting_woman_would.html">would have owned slaves</a> back then.</p>

<p>When the internet gets its panties in a bunch for the umpteenth time, it's worth asking: where are people getting their information from? The plural of anecdote is not <em>data</em>, after all. Every incident I've heard of lately was massively blown out of proportion. Kony 2012 anyone? <em>Look, finally a cause we can all be equally offended by.</em></p>

<p>Women are adamant about not being pigeonholed by their gender. I see no reason why we should encourage and celebrate doing it to men. Whether male or female, or any of the shades in between and around, people can have wildly different points of view, and reducing everything to a gender battle is as myopic as pretending no issues exist at all.</p>

<p>The most reasonable people are now afraid to speak their mind. They rightly fear being shamed and harassed by those who scream the loudest of abuse. I've debated writing about this for a while, because I know what a certain part of the response will be. But I'm not the only one saying it, so I'm doing it here, <em>once</em>, in full length, with honest citations, after discussion with people of experience. Women and men, in case you're wondering. <em>"Good luck"</em> was a common theme.</p>

<p>Remember, I'm not the one trying to make hay out of gender issues, turning them into ad revenue, TV appearances or book sales. In my line of work, we're expected to <em>fix</em> things, not just tell people they're broken in increasingly hyperbolic words.</p>

<p>Don't man the cannons or summon the horde. Instead, go check out the ton of links I just dropped into your lap, listen to what's already been said, and see if you can't hear the sound of a record skipping somewhere in the distance. It's not the one you think it is.</p>

<p class="m4">For the future then, something to think about. If I step outside, I can walk a couple blocks in any direction to encounter these.</p>

<p>I've taken the liberty of making them more honest:</p>

<p><img src="https://acko.net/files/storms-teacups/dead-rocks.jpg" alt="Dead Rocks" class="natural"></p>
<p><img src="https://acko.net/files/storms-teacups/audi.jpg" alt="Audi" class="natural"></p>

<p>This is what we allow advertisers to paste onto our streets, our newspapers, our TV shows. Our brains. And then the media turns around to tell us how everyone's being selfish and insecure, but <em>sexism</em> is to blame.</p>

<p>As a smarter person put it, it's <em><span style="font-weight:bold">narcissism repackaged as a gender battle</span></em>.</p>

<p>Don't say it doesn't affect you, not when <a href="http://en.wikipedia.org/wiki/Thinking,_Fast_and_Slow">a picture of dollar bills makes you more reluctant to help someone pick up pencils</a>.</p>

</div></div>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[On WebGL]]></title>
    <link href="http://acko.net/blog/on-webgl/"/>
    <updated>2013-03-11T00:00:00+01:00</updated>
    <id>http://acko.net/blog/on-webgl</id>
    <content type="html"><![CDATA[<div class="g8 i2 first"><div class="pad">

<h2 class="sub">More than pretty pictures</h2>

<div class="c m2"></div>

<p>Like a dragon, WebGL slumbers. But you've seen them, right? Those seemingly magical demos that transform your ordinary browser into a lush 3D world with one click?</p>

<p>While available in Chrome and Firefox on the desktop, WebGL is still not widely supported. So far it's mostly used for demo projects and flashy one-off brochures. On the few mobile devices that support it, you need developer access to enable it. It's certainly nowhere near to being ready for prime time. So why should you care?</p>

</div></div>

<div class="c"></div>

<div class="wide mt2">
    <a target="_blank" href="http://alteredqualia.com/three/examples/webgl_postprocessing_ssao.html"><img src="/files/on-webgl/aq-ssao.jpg" alt="City scene"></a>
</div>
<div class="g8 i2 mb0">
    <a target="_blank" class="credit" href="http://twitter.com/alteredq">AlteredQualia</a>
</div>

<div class="g8 i2 mt1"><div class="pad">

<h2>The Black Sheep</h2>

<p>The goal of WebGL is to bring the graphics capabilities of traditional apps and games into the browser, with performance as the main benefit. The graphics hardware does the work directly, leaving the CPU to just coordinate. Yet those developers look on with skepticism: "You mean we have to code in <em>JavaScript</em>?" There's grumbling about the limited capabilities too, which lag a few years behind the latest OpenGL and Direct3D APIs, and there's worries about copyright and modding.</p>

<p>First, we have to be honest: there's no question that native apps and 3D engines will continue to excel, bringing cutting edge graphics and performance. The layers of indirection in both HTML5 and WebGL cannot be circumvented.</p>

<p>But they do serve a purpose: to provide a safe sandbox for untrusted code from the web at large. Even triple-A games still occasionally crash, a result of their complexity, with thread synchronization, memory management and manual context-switching the price to pay. Random phishers shouldn't have that level of access to your system, nor should it be required.</p>

</div></div>

<div class="c"></div>

<div class="wide mt2">
    <a target="_blank" href="http://alteredqualia.com/three/examples/webgl_loader_ctm_materials.html"><img src="/files/on-webgl/aq-loader.jpg" alt="Car scene"></a>
  </div>
  <div class="g8 i2 mb1">
    <a target="_blank" class="credit" href="http://twitter.com/alteredq">AlteredQualia</a>
</div>

<div class="g8 i2 mt1"><div class="pad">

<p>WebGL represents a different way of using high-performance graphics: not as a bare metal API with caveats, but as a safe service to be exposed, to be flicked on or off without a second thought. It may not sound like much, but the security implications are big and will only be solved carefully, over time. It's undoubtedly a big reason behind Apple and Microsoft's reluctance to embrace it.</p>

<p>We should also note that this isn't a one-way cross-over. HTML has already snuck into the real-time graphics scene. First we saw in-game web views and browsers, then UIs such as Steam's overlay. In fact, <em>all</em> of Steam is WebKit. The main benefit is familiarity: designers can use the well-known techniques of the web both inside and outside the game. This mirrors the way Adobe Flash entered the gaming space before, being used to drive menus and overlays in many games.</p>

<p>It's been said that the skills required for front-end web development and game development eventually converge on the same thing. The technologies certainly have.</p>

</div></div>

<div class="c"></div>

<div class="wide mt2">
    <a target="_blank" href="http://cake23.de/turing-fluid.html"><img src="/files/on-webgl/ck-cfd.jpg" alt="Turing fluid patterns"></a>
  </div>
  <div class="g8 i2 mb0">
    <a target="_blank" class="credit" href="https://twitter.com/flexi23">Felix Woitzel</a>
</div>

<div class="g8 i2 mt0"><div class="pad">

<h2>The Procedural Canvas</h2>

<p>The web is the world's only universal procedural medium. Content is downloaded in partially assembled form, and you and your browser decide how it should be displayed. The procedural aspect has always been there, and today's practice of responsive design is just another evolution in procedural page layout. It all started with resizable windows and tables.</p>

<p>But when we decide to put a graphic into a page, we still bake it into a grid of pixels and send that down the pipe. This has worked great as a delivery mechanism, but is starting to show its age, due to high DPI displays and adaptive streaming.</p>

<p>It's also pushed the web further towards consumption: YouTube and Tumblr are obvious results. Both sites have a huge asymmetry between content creator and consumer, encouraging sharing rather than creating.</p>

</div></div>

<div class="c"></div>

<div class="wide mt2">
    <a target="_blank" href="http://cake23.de/turing-pattern-gradient-attractor-feedback.html"><img src="/files/on-webgl/ck-attract.jpg" alt="Turing pattern gradient attractor feedback"></a>
  </div>
  <div class="g8 i2 mb1">
    <a target="_blank" class="credit" href="https://twitter.com/flexi23">Felix Woitzel</a>
</div>

<div class="g8 i2 mt1"><div class="pad">

<p>Real-time graphics level the playing field: once built, both creator and consumer have the same degree of control—at least in theory. All the work necessary to produce the end result is ideally being done 60 times per second. The experience of e.g. playing a game is like a sort of benign DRM, which requires you to access the content in a certain way. All native apps implement such 'DRM' by accident: their formats are binary and often proprietary, the code is compiled. Usually modding is supported in theory—that's what <em>Downloadable Content</em> is, an official mod—but the tools simply aren't included.</p>

<p>The web is different. No matter how obfuscated, all code eventually has to talk to an interface that is both completely open and introspective. You can hook into any aspect of it and watch the data. There isn't a serious web developer around who would argue that this is a bad thing, who hasn't spent time deconstructing a site through a web inspector on a whim.</p>

</div></div>

<div class="c"></div>

<div class="wide mt2">
    <a target="_blank" href="http://codeflow.org/webgl/deferred-irradiance-volumes/www/"><img src="/files/on-webgl/fb-div.jpg" alt="Deferred irradiance volumes"></a>
  </div>
  <div class="g8 i2 mb1">
    <a target="_blank" class="credit" href="http://twitter.com/pyalot">Florian Bösch</a>
</div>

<div class="g8 i2 mt1"><div class="pad">

<p>This is where WebGL gets interesting. It takes the tools normally reserved for well, the hardcore geeks, and makes them much more open and understandable. I can certainly say from experience that coding with an engine like Three.js is an order of magnitude more productive than e.g. Ogre3D in C++. For most of the things I want to do with it, the performance difference is negligible, but there is much less code. Once you get your dev environment going, creating a new 3D scene is as simple as opening a text file. You can interact with your code live through the console for free.</p>

<p>More so, it integrates with the publishing tools we already know. I wonder for example how many hours of dev time the game industry has spent reinventing the wheel for fonts, menus, option screens, etc. To be fair, they often do so with amazing production value. But guess what: you now have CSS 3D, and soon you'll have CSS shaders. You don't need custom in-house tools when your designers can just use Chrome's Inspector and get the exact same result. Content delivery is easy: you have cloud storage, CDNs and memory caches at your disposal.</p>

<p>There is a missing link however: WebGL is a canvas inside the page, isolated from what's outside. But you could imagine APIs to help bring HTML content into a WebGL texture, taking over responsibility for drawing it. After all, most web browsers already use hardware acceleration to compose 2D web pages on screen. The convergence has already started.</p>

</div></div>

<div class="c"></div>

<div class="wide mt2">
    <a target="_blank" href="http://codeflow.org/webgl/craftscape/" alt="Landscape erosion simulation"><img src="/files/on-webgl/fb-erode.jpg"></a>
  </div>
  <div class="g8 i2 mb1">
    <a target="_blank" class="credit" href="http://twitter.com/pyalot">Florian Bösch</a>
</div>

<div class="g8 i2 mt1"><div class="pad">

<p>The web has a history of transformative changes. CSS gave us <a href="http://csszengarden.com/">real web design</a>, Flash gave us <a href="http://youtube.com/">ubiquitous video</a>, Firebug gave us <a href="https://developers.google.com/chrome-developer-tools/">Web Inspectors</a>, jQuery gave us <a href="http://sizzlejs.com">non-painful DOM manipulation</a>, and so on. None of these ideas were new in computing when they debuted, the web merely adapted to fill a need. WebGL is an idea in a similar vein, a base platform for an ecosystem of specialized frameworks on top.</p>

<p>It can help lead to a WolframAlpha-ized <a href="http://en.wikipedia.org/wiki/LCARS">LCARS future</a>, where graphics can be interactive and introspective by default. Why shouldn't you be able to click on a news graphic to filter the view, or download the dataset? For sure, this is not something that uniquely requires WebGL, and tools like d3.js are already showing the way with CSS and SVG. As a result, the <em>last mile</em> of interactivity becomes a mere afterthought: everything is live anyway. What WebGL does is raise the limit significantly on what sort of content can be displayed in a browser. It's not until those caps are lifted that we can say with a straight face that web apps can rival native apps.</p>

<p>Still, we shouldn't be aiming to recreate Unreal Engine in HTML / JS / GL, though someone will probably try, and eventually succeed. Rather we should explore what happens when you put a 3D engine inside a web page. Is it web publishing, or demoscene? Does it matter?</p>

</div></div>

<div class="c"></div>

<div class="wide mt2 tc">
    <a target="_blank" href="http://www.ro.me/tech/hatching-glow-shader"><img src="/files/on-webgl/rm-crosshatch.jpg" alt="Stylistic cross hatch effect"></a>
  </div>
  <div class="g8 i2 mb1">
    <a target="_blank" class="credit" href="http://www.ro.me/tech/">Ro.me team</a>
  </div>
  <div class="wide mt2 tc">
    <a target="_blank" href="http://mrdoob.github.com/three.js/examples/css3d_periodictable.html" alt="CSS 3D periodic table"><img src="/files/on-webgl/db-css3d.jpg"></a>
  </div>
  <div class="g8 i2 mb1">
    <a target="_blank" class="credit" href="http://twitter.com/mrdoob">Mr.doob</a>
  </div>
  <div class="wide mt2 tc">
    <a target="_blank" href="http://workshop.chromeexperiments.com/projects/armsglobe/"><img src="/files/on-webgl/ch-globe.jpg" alt="Chrome Workshop - Globe"></a>
  </div>
  <div class="g8 i2 mb1">
    <a target="_blank" class="credit" href="http://workshop.chromeexperiments.com">Chrome Workshop</a>
  </div>
  <div class="wide mt2 tc">
    <a target="_blank" href="http://acko.net/blog/how-to-fold-a-julia-fractal/" alt="How to Fold a Julia Fractal - Complex math"><img src="/files/on-webgl/ucd-julia.jpg"></a>
</div>

<div class="g8 i2 mt1"><div class="pad">

<h2>A Useful Baseline</h2>

<p>In this light, WebGL's often lamented limitation becomes its strength. WebGL is not modelled after 'grown-up' OpenGL, but mirrors OpenGL ES (Embedded Systems). It's a suite of functionality supported by most mobile devices, but eclipsed by even the crummiest integrated laptop graphics from 3 years ago.</p>

<p>This needn't be a worry for two reasons. First, WebGL supports extensions, which add to the functionality and continue to be specced out. A WebGL developer can inspect the capabilities of the system and determine an appropriate strategy to use. Many extensions are <a href="http://webglstats.com">widely supported</a>, and even without extensions, all GL code is already subject to the platform's size limits on resources. WebGL is no different from other APIs, it just puts the bar a bit lower than usual.</p>

<p>Second of all, it means WebGL is the only 3D API that has a shot at being universal, from desks to laps to pockets to living rooms, and everything in between. Your game console could be an Android computer, handheld or appliance. Your TV might run Linux or iOS. So might your fridge. WebGL fits with where hardware and software is going, and adapting to various devices is nothing new for the web. I imagine we might see a standardized benchmark library pop up, and developer tools to make e.g. desktop Chrome mimic a cellphone's limited capabilities. </p>

</div></div>

<div class="c"></div>

<div class="wide mt2">
    <a target="_blank" href="http://acko.net/files/never-seen-the-sky/git/"><img src="/files/on-webgl/ucd-aurora.jpg" alt="Never Seen The Sky - WebGL Demo"></a>
</div>

<div class="g8 i2 mt2"><div class="pad">

<p>For the Christmas demo above, I included a simple benchmark that pre-selects the demo resolution based on the time needed to generate assets up front. Additionally, it was built on a 4 year old laptop GPU, so it should run well for the majority of viewers on first viewing. The same can't be said for cutting-edge demoscene demos, which often only run smoothly on top of the line hardware. I know I'm usually resigned to watching them on YouTube instead. As neat as tomorrow's tech is, for most people it only matters what they have today.</p>

<p>This is the biggest philosophical difference between WebGL and OpenGL. WebGL aims to be a good enough baseline that you can carry in your pocket as well as put on a big screen, and make accessible with a simple link. I don't expect graphics legends like John Carmack to take anything but a cursory glance at it, but then, it's not encroaching on his territory. It is a bit surprising though that the demoscene hasn't taken to the web more quickly. It has never been about having top of the line hardware, only what you use it for. Contests like JS1K continue to demonstrate JavaScript's expressiveness, but we haven't really seen the bigger guns come out yet.</p>

<p>And it really is <em>good enough</em>. Here's 150,000 cubes, made out of 1.8 million triangles:</p>

</div></div>

<div class="c"></div>

<div class="wide mt2">
    <a target="_blank" href="http://alteredqualia.com/three/examples/webgl_cubes.html" alt="150000 cubes"><img src="/files/on-webgl/aq-cubes.jpg"></a>
  </div>
  <div class="g8 i2 mb1">
    <a target="_blank" class="credit" href="http://twitter.com/alteredq">AlteredQualia</a>
</div>

<div class="g8 i2 mt1"><div class="pad">

<p>Next up is a fractal raytracer. At 30 frames per second, 512x512 pixels, 40 iterations per pixel, each folding 3D space 18 levels deep… that's 5.6 <em>billion</em> folds per second. This intricate visualization is little more than raw number crunching power. That's just the core loop and excludes set up and lighting. It's all driven by a couple kilobytes of JavaScript wrapped in some HTML, delivered over HTTP.</p>

</div></div>

<div class="c"></div>

<div class="wide mt2">
    <a target="_blank" href="http://acko.net/files/on-webgl/whittaker/raytrace-creepy.html"><img src="/files/on-webgl/ucd-de.jpg" alt="Distance estimation with fractals"></a>
</div>

<div class="g8 i2 mt2"><div class="pad">

<p>Why wouldn't you want to play with that? Come try WebGL, <a href="http://madebyevan.com/webgl-water/" target="_blank">the water's fine</a>.</p>

<h2>Further reading</h2>

<ul>
  <li>Paul Lewis' <a href="http://aerotwist.com/tutorials/getting-started-with-three-js/">excellent tutorials</a> on Three.js. He also runs the <a href="http://thewebglpodcast.com">WebGL podcast</a>.</li>
  <li>The <a href="https://www.udacity.com/course/cs291">Interactive 3D graphics</a> WebGL course on Udacity by Eric Haines.</li>
  <li>The <a href="https://www.shadertoy.com">ShaderToy</a> and <a href="http://glsl.heroku.com">GLSL</a> sandboxes.</li>
  <li>The <a href="https://twitter.com/search?q=webgl">WebGL hashtag on Twitter</a>.</li>
</ul>

<p><em>Examples by the amazing <a href="http://twitter.com/alteredq">AlteredQualia</a>, <a href="https://twitter.com/flexi23">Felix Woitzel</a>, <a href="http://twitter.com/pyalot">Florian Bösch</a>, the <a href="http://www.ro.me/tech/">Ro.me team</a>, <a href="http://twitter.com/mrdoob">Mr.doob</a>, <a href="http://workshop.chromeexperiments.com">Chrome Workshop</a>, as well as myself. Many of these techniques are documented on <a href="http://www.iquilezles.org/">Iñigo Quilez</a>'s comprehensive site.</em></p>

<p><em>Additional demos and comments are welcome on <a href="https://plus.google.com/112457107445031703644/posts/4qcrPNzEVjk">Google Plus</a>.</em></p>
 
</div></div>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[To Infinity… And Beyond!]]></title>
    <link href="http://acko.net/blog/to-infinity-and-beyond/"/>
    <updated>2013-01-28T00:00:00+01:00</updated>
    <id>http://acko.net/blog/to-infinity-and-beyond</id>
    <content type="html"><![CDATA[<script type="text/x-mathjax-config">
MathJax.Hub.Config({
  "HTML-CSS": { availableFonts: ["TeX"] },
  extensions: ["tex2jax.js"],
  jax: ["input/TeX","output/HTML-CSS"],
  tex2jax: {inlineMath: [["$","$"],["\\(","\\)"]]},
});
</script>

<script type="text/javascript"
  src="https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS_HTML.js">
</script>

<script type="text/javascript">
// <!--
window.MathJax && MathJax.Hub.Queue(["Typeset",MathJax.Hub]);
Acko.queue(function () { Acko.Fallback.warnWebGL(); });
// -->
</script>

<div class="g8 i2"><div class="pad">

<h2 class="sub">Exploring the outer limits</h2>

</div></div>

<div class="c"></div>

<div class="g12"><div class="pad">

<blockquote class="m2">
  <em class="big">“It is known that there are an infinite number of worlds, simply because there is an infinite amount of space for them to be in. However, not every one of them is inhabited. Therefore, there must be a finite number of inhabited worlds.<br><br>
Any finite number divided by infinity is as near to nothing as makes no odds, so the average population of all the planets in the universe can be said to be zero. From this it follows that the population of the whole universe is also zero, and that any people you may meet from time to time are merely the products of a deranged imagination.”</em>
  <div class="tr m1">– <a href="http://en.wikipedia.org/wiki/The_Restaurant_at_the_End_of_the_Universe">The Restaurant at the End of the Universe</a>, Douglas Adams</div>
</blockquote>

</div></div>

<div class="g8 i2 m2"><div class="pad">

<p>If there's one thing mathematicians have a love-hate relationship with, it has to be <em>infinity</em>. It's the ultimate tease: it beckons us to come closer, but never allows us anywhere near it. No matter how far we travel to impress it, infinity remains disinterested, equally distant from everything: infinitely far!</p>

<p class="tc math">$$ 0 &lt; 1 &lt; 2 &lt; 3 &lt; … &lt; \infty $$</p>

<p>Yet infinity is not just desirable, it is absolutely necessary. All over mathematics, we find problems for which no finite amount of steps will help resolve them. Without infinity, we wouldn't have real numbers, for starters. That's a problem: our circles aren't round anymore (no <a href="http://en.wikipedia.org/wiki/Pi">$ π $ and $ \tau $</a>) and our exponentials stop growing right (no <a href="http://en.wikipedia.org/wiki/E_(mathematical_constant)">$ e $</a>). We can throw out all of our triangles too: most of their sides have exploded.</p>

</div></div>

<div class="g7">
  <p><a href="http://www.loc.gov/pictures/resource/ggbain.16674/"><img src="/files/infinity-and-beyond/steel-bridge-loc.jpg" title="Cape Cod Canal, Buzzard's Bay, 1910" alt="Steel Railroad Bridge,  Cape Cod Canal, Buzzard's Bay, 1910"></a></p>
  <p class="tc">A steel railroad bridge with a 1200 ton counter-weight.<br>Completed in 1910. Source: <a href="http://www.loc.gov/pictures/resource/ggbain.16674/">Library of Congress</a>.</p></div>

<div class="g5"><div class="pad">

<p class="math">We like infinity because it helps avoid all that. In fact even when things are not infinite, we often prefer to pretend they are—we do geometry in infinitely big planes, because then we don't have to care about where the edges are.</p>

<p class="math">Now, suppose we want to analyze a steel beam, because we're trying to figure out if our proposed bridge will stay up. If we want to model reality accurately, that means simulating each individual particle, every atom in the beam. Each has its own place and pushes and pulls on others nearby.</p>

<p class="math">But even just $ 40 $ grams of pure iron contains <a href="http://www.wolframalpha.com/input/?i=40g+of+iron+in+atoms">$ 4.31 \cdot 10^{23} $ atoms</a>. That's an inordinate amount of things to keep track of for just <a href="http://www.wolframalpha.com/input/?i=%281+teaspoon%29+%2F+%2840g+%2F+density+of+iron%29">1 teaspoon of iron</a>.</p>

</div></div>

<aside class="g6">
  <p class="mt0"><iframe class="mathbox square autosize" src="/files/infinity-and-beyond/mb-1-austenite.html"></iframe></p>
  <p class="tc">The crystal structure of 32 iron atoms in the hot <a href="http://en.wikipedia.org/wiki/Austenite">austenite</a> phase.<br>If your steel looks like this, your bridge is on fire.</p>
</aside>

<aside class="g6">
  <p class="mt0"><iframe class="mathbox square autosize" src="/files/infinity-and-beyond/mb-2-cube.html"></iframe></p>
  <p class="tc">A chunk of solid, mathematical iron.</p>
</aside>


<div class="g8 i2"><div class="pad">
<p>Instead, we pretend the steel is solid throughout. Rather than being composed of atoms with gaps in between, it's made of some unknown, filled in material with a certain density, expressed e.g. as <em>grams per cubic centimetre</em>. Given any shape, we can determine its volume, and hence its total mass, and go from there. That's much simpler than counting and keeping track of individual atoms, right?</p>

<p>Unfortunately, that's not quite true.</p>

<h2>The Shortest Disappearing Trick Ever</h2>
<p class="math">Like all choices in mathematics, this one has consequences we cannot avoid. Our beam's density is <em>mass per volume</em>. Individual points in space have zero volume. That would mean that at any given point inside the beam, the amount of mass there is $ 0 $. How can a beam that is entirely composed of nothing be solid and have a non-zero mass?</p>

<p class="tc">
<iframe class="mathbox fit" src="/files/infinity-and-beyond/mb-3-empty-cube.html" height="250"></iframe><br><em>Bam! No more iron anywhere.</em></p>

<p>While Douglas Adams was being deliberately obtuse, there's a kernel of truth there, which is a genuine paradox: what exactly is the mass of every atom in our situation?</p>

<p class="math">To make our beam solid and continuous, we had to shrink every atom down to an infinitely small point. To compensate, we had to create infinitely many of them. Dividing the finite mass of the beam between an infinite amount of atoms should result in $ 0 $ mass per atom. Yet all these masses still have to add up to the total mass of the beam. This suggests $ 0 + 0 + 0 + … &gt; 0 $, which seems impossible.</p>

<p class="math">If the mass of every atom were not $ 0 $, and we have infinitely many points inside the beam, then the total mass is infinity times the atomic mass $ m $. Yet the total mass is finite. This suggests $ m + m + m + … &lt; \infty $, which also doesn't seem right.</p>

<p class="math">It seems whatever this number $ m $ is, it can't be $ 0 $ and can't be non-zero. It's definitely not infinite, we only had a finite mass to begin with. It's starting to sound like we'll have to invent a whole new set of numbers again to even find it.</p>

</div></div>

<div class="g8"><div class="pad">

<p>That's effectively what Isaac Newton and Gottfried Leibniz set in motion at the end of the 17th century, when they <a href="http://en.wikipedia.org/wiki/Leibniz–Newton_calculus_controversy">both discovered calculus independently</a>. It was without a doubt the most important discovery in mathematics and resulted in formal solutions to many problems that were previously unsolvable— our entire understanding of physics has relied on it since. Yet it took until the late 19th century for the works of Augustin Cauchy and Karl Weierstrass to pop up, which formalized the required theory of <em>convergence</em>. This allows us to describe exactly how differences can shrink down to nothing as you approach infinity. Even that wasn't enough: it was only in the 1960s when the idea of <a href="http://en.wikipedia.org/wiki/Infinitesimal">infinitesimals</a> as fully functioning numbers—the <a href="http://en.wikipedia.org/wiki/Hyperreal_number">hyperreal numbers</a>—was finally proven to be consistent enough by Abraham Robinson.</p>

<p class="math">But it goes back much further. Ancient mathematicians were aware of problems of infinity, and used many ingenious ways to approach it. For example, $ π $ was found by considering circles to be infinite-sided polygons. <a href="http://en.wikipedia.org/wiki/Archimedes%27_use_of_infinitesimals">Archimedes' work</a> is likely the earliest use of <em>indivisibles</em>, using them to imagine tiny mechanical levers and find a shape's center of mass. He's better known for running naked through the streets shouting <a href="http://en.wikipedia.org/wiki/Eureka_(word)#Archimedes">Eureka!</a> though.</p>

<p class="math">That it took so long shows that this is not an easy problem. The proofs involved are elaborate and meticulous, all the way back. They have to be, in order to nail down something as tricky as infinity. As a result, students generally learn calculus through the simplified methods of Newton and Leibniz, rather than the most mathematically correct interpretation. We're taught to mix notations from 4 different centuries together, and everyone's just supposed to connect the dots on their own. Except the trail of important questions along the way is now overgrown with jungle.</p>

</div></div>

<aside class="g4">
  <p><img class="pad" src="/files/infinity-and-beyond/principia.png"></p>
  <p class="tc">A diagram from Isaac Newton's <a href="http://en.wikipedia.org/wiki/Philosophiæ_Naturalis_Principia_Mathematica">Philosophiæ Naturalis Principia Mathematica</a> (1687) about finding area under a smooth curve.</p></aside>

<div class="g8 i2"><div class="pad">
  
<p>Still, it shows that even if we don't understand the whole picture, we can get a lot done. This article is in no way a formal introduction to infinitesimals. Rather, it's a demonstration of why we might need them.</p>
  
<p class="math">What is happening when we shrink atoms down to points? Why does it make shapes solid yet seemingly hollow? Is it ever meaningful to write $ x = \infty $? Is there only one infinity, or are there many different kinds?</p>

<p>To answer that, we first have to go back to even simpler times, to Ancient Greece, and start with the works of Zeno.</p>

<h2>Achilles and the Tortoise</h2>

<p><a href="http://en.wikipedia.org/wiki/Zeno_of_Elea">Zeno of Elea</a> was one of the first mathematicians to pose these sorts of questions, effectively trolling mathematics for the next two millennia. He lived in the 5th century BC in southern Italy, although only second-hand references survive. In his series of paradoxes, he examines the nature of equality, distance, continuity, of time itself.</p>

<p>Because it's the ancient times, our mathematical knowledge is limited. We know about zero, but we're still struggling with the idea of nothing. We've run into negative numbers, but they're clearly absurd and imaginary, unlike the positive numbers we find in geometry. We also know about fractions and ratios, but square roots still confuse us, even though our temples stay up.</p>

</div></div>

<div class="wide slideshow full">

  <div class="iframe c">
    <iframe src="/files/infinity-and-beyond/mb-4-achilles.html" class="mathbox paged autosize" height="320"></iframe>
  </div>

  <div class="steps">

    <div class="step">
      <p>So the story goes: the tortoise challenges Achilles to a footrace.</p>
    </div>

    <div class="step">
      <p>
        "If you give me a head start," it says, "any start at all, you can never win.".<br>
        Achilles laughs and decides to be a good sport: he'll only run twice as fast as the tortoise.
      </p>
    </div>

    <div class="step">
      <p>
        The tortoise explains: "If you want to pass me, first you have to move to where I am. By the time you get there, I'll have walked ahead a little bit."
      </p>
    </div>

    <div class="step">
      <p>
        "While you cross the next distance, I will move yet again. No matter how many times you try to catch up, I'll always be some small distance ahead. Therefor, you cannot beat me."
      </p>
    </div>

    <div class="step">
      <p>Achilles realizes that talking tortoises are not a sign of positive mental health, so he decides to find a wall to run into instead. It will either confirm the theory, or end the pain.
      </p>
    </div>

    <div class="step">
      <p>
        See, the race is actually unnecessary, because the problem remains the same.<br>In order to reach the wall, Achilles first has to cross half the way there.
      </p>
    </div>
    
    <div class="step">
      <p>Then he has to go half that distance again, and again. No matter how many times he repeats this, there will always be some distance left. So if Achilles can't cross this distance in a finite amount of steps, why is he wearing that stupid helmet?
      </p>
    </div>

    <div class="step">
      <div class="extra bottom" data-delay="2.4"  data-hold="2" data-align-y=".55"><big>$$ … $$</big></div>
      <p>
        The <em>distance travelled</em> forms a never ending <span class="orangered">sequence of expanding sums</span>.<br>We have to examine the entire sequence, rather than individual numbers in it.
      </p>
    </div>

    <div class="step">
      <p class="math">
        By definition, the <span class="orangered">distance travelled</span> and <span class="gold">distance to the wall</span> always add up to $ 1 $. So one simple way to resolve this conundrum is to say: <em>Well yes, it's going to take you infinitely long to glue all those pieces together, but only because you already spent an infinite amount of time chopping them up!</em><br>
        But that's not a very mathematically satisfying answer. Let's try something else.
      </p>
    </div>

    <div class="step">
      <p class="math">
        The distance to the wall is always equal to the last step taken. We know that each step is half as long as the previous one, starting with $ \frac{1}{2} $. Therefor, the distance to the wall must decrease exponentially: $ \frac{1}{2}, \frac{1}{4}, \frac{1}{8}, \frac{1}{16}, … $, getting closer to zero with every step.
      </p>
    </div>

    <div class="step">
      <p class="math">
        But why can we say that this gap effectively closes to zero after 'infinity steps'? The number that we're building up is $ \frac{1}{2} + \frac{1}{4} + \frac{1}{8} + \frac{1}{16} + … \,$
      </p>
    </div>
    
    <div class="step">
      <p class="math">
        We know our sum will never exceed $ 1 $, as there is only $ 1 $ unit of distance being divided. This means $ \frac{1}{2} + \frac{1}{4} + \frac{1}{8} + \frac{1}{16} + … \leq 1 $, which eliminates every number past the surface of the wall—but not the surface itself.
      </p>
    </div>

    <div class="step">
      <p class="math">
        Suppose we presume $ \frac{1}{2} + \frac{1}{4} + \frac{1}{8} + \frac{1}{16} + … &lt; 1 $ and hence that this number lies some tiny distance <em>in front of the wall</em>.
      </p>
    </div>

    <div class="step">
      <p class="math">
        Well in that case, all we need to do is zoom in far enough, and we'll see our sequence jump past it after a certain finite number of steps.
      </p>
    </div>

    <div class="step">
      <p class="math">
        If we try to move it <span class="gold">closer to the wall</span>, the same thing happens. This number simply cannot be less than $ 1 $. Therefor $ \frac{1}{2} + \frac{1}{4} + \frac{1}{8} + \frac{1}{16} + … \geq 1 $
      </p>
    </div>

    <div class="step">
      <p class="math">
        The only place $ \frac{1}{2} + \frac{1}{4} + \frac{1}{8} + \frac{1}{16} + … \, $ can be is exactly $ 0 $ units away from $ 1 $. If two numbers have zero distance between them, then they are equal.
      </p>
    </div>

    <div class="step">
      <div class="extra bottom" data-delay="4.6" data-hold="1" data-align-y=".55"><big>$$ … $$</big></div>
      <p class="math">
        What we've actually done here is applied the principle of <em>limits</em>: we've defined a procedure of steps that lets us narrow down <span class="gold">the interval</span> where the infinite sum might be. The lower bound is the sequence of sums itself: it only increases towards $ 1 $, never decreases. For the upper bound, we established no sum could exceed $ 1 $. Therefor the interval must shrink to nothing, and the sequence <em>converges</em>.
      </p>
    </div>

    <div class="step">
      <div class="extra"><big><big>
        $$ \lim_{n \to +\infty} x_n = \mathop{\class{no-outline}{►\hspace{-2pt}►}}_{\infty\hspace{2pt}} x_n $$
      </big></big></div>
      <p class="math">
        The purpose of a limit is then to act as a supercharged fast-forward button. It lets us avoid the infinite amount of work required to complete sums like $ \frac{1}{2} + \frac{1}{4} + \frac{1}{8} + \frac{1}{16} + … $ and simply <em>skip to the end</em>. To do so, we have to step back, spot the pattern, and pin down where it ends. So limits allow us to literally reach the unreachable. But in fact, you already knew that.
      </p>
    </div>

    <div class="step">
      <div class="extra top right" data-delay="2.4" data-align-x="1.25" data-align-y=".5">$$ \frac{2}{3} = 0.66666… $$</div>
      <div class="extra bottom" data-delay="2.4" data-align-y=".58">$$ 0.6 + 0.06 + 0.006 + …\hspace{2pt} $$</div>
      <p class="math">
        As soon as you learned to divide, you found $ 2 \div 3 = 0.666… = 0.6 + 0.06 + 0.006 + …\hspace{2pt} $ <br>Even in primary school the opportunity to examine infinity is there. Rather than tackle it head on, it's simply noted and filed. Eight years later it's regurgitated in the form of cryptic <em>epsilon-delta definitions</em>.</p>
    </div>

    <div class="step">
      <div class="extra bottom" data-delay="1.6" data-hold="1" data-align-y="1.1">
        $$ 1 - 1 + 1 - 1 + 1 … $$<br>
      </div>
      <p class="math">
        But then there's those pesky consequences again. By allowing the idea of infinity, we can invent an entire zoo of paradoxical things. For example, imagine a lamp that's switched on ($1$) and off ($0$) at intervals that decrease by a factor of two: <em>on</em> for $ \frac{1}{2} $ second, <em>off</em> for $ \frac{1}{4} s $, <em>on</em> for $ \frac{1}{8} s $, <em>off</em> for $ \frac{1}{16} s $, …<br>After $ 1\,s $, when the switch has been flipped an infinite amount of times, is the lamp on or off?
      </p>
    </div>

    <div class="step">
      <div class="extra bottom" data-align-y="1.1">
        <br><br>
        <small><small>
        <br>
        $$ (1 - 1) + (1 - 1) + (1 - 1) + … = 0 \,? $$
        </small></small>
      </div>
      <div class="extra bottom" data-align-y="1.1">
        <br><br>
        <small><small>
        <br>
        <br><br>
        $$ 1 + (-1 + 1) + (-1 + 1) + … = 1 \,? $$
        </small></small>
      </div>
      <p class="math">
        Another way to put this is that the lamp's state at $ 1\,s $ is the result of the infinite sum $ 1 - 1 + 1 - 1 + … $ <br>Intuitively we might say each pair of $ +1 $ and $ -1 $ should cancel out and make the entire sum equal to $ 0 $. <br>But we can pair them the other way, leading to $ 1 $ instead. It can't be both.
      </p>
    </div>

    <div class="step">
      <p class="math">
        If we zoom in, it's obvious that no matter how close we get to $ 1\,s $, the lamp's state keeps switching. Therefor it's meaningless to attempt to 'fast forward' to the end, and the limit does not exist. At $ 1\,s $ the lamp is neither on nor off: it's undefined. This infinite sum <em>does not converge</em>.
      </p>
    </div>

    <div class="step">
      <p class="math">
        But actually, we overcomplicated things. Thanks to the power of limits, we can ask a simpler, equivalent question. Given a lamp that switches on and off <em>every second</em>, what is its state at infinity? The answer's the same: it never settles.
      </p>
    </div>

  </div>
  
</div>

<div class="c"></div>

<div class="g8 i2"><div class="pad">
  
  <p class="math">Limits are the first tool in our belt for tackling infinity. Given a sequence described by countable steps, we can attempt to extend it not just to the end of the world, but literally forever. If this works we end up with a finite value. If not, the limit is undefined. A limit can be equal to $ \infty $, but that's just shorthand for <em>the sequence has no upper bound</em>. Negative infinity means no lower bound.</p>

<h2>Breaking Away From Rationality</h2>
  
  <p>Until now we've only encountered fractions, that is, <em>rational numbers</em>. Each of our sums was made of fractions. The limit, if it existed, was also a rational number. We don't know whether this was just a coincidence.</p>
  
  <p class="math">It might seem implausible that a sequence of numbers that is 100% rational and converges, can approach a limit that isn't rational at all. Yet we've already seen similar discrepancies. In our first sequence, every partial sum was <em>less than</em> $ 1 $. Meanwhile the limit of the sum was <em>equal to</em> $ 1 $. Clearly, the limit does not have to share all the properties of its originating sequence.</p>
  
  <p>We also haven't solved our original problem: we've only chopped things up into infinitely many <em>finite pieces</em>. How do we get to <em>infinitely small pieces</em>? To answer that, we need to go looking for <em>continuity</em>.</p>
  
  <p>Generally, continuity is defined by what it is and what its properties are: a noticeable lack of holes, and no paradoxical values. But that's putting the cart before the horse. First, we have to show which holes we're trying to plug.</p>
  
</div></div>


<div class="wide slideshow full">

  <div class="iframe c">
    <iframe src="/files/infinity-and-beyond/mb-5-continuity.html" class="mathbox paged autosize" height="320"></iframe>
  </div>

  <div class="steps">

    <div class="step">
      <p>Let's imagine the <em>rational numbers</em>.</p>
    </div>

    <div class="step">
      <p>Actually, hold on. Is this really a line? The integers certainly weren't connected.</p>
    </div>

    <div class="step">
      <p class="math">Rather than assume anything, we're going to attempt to visualize all the rational numbers. We'll start with the <span class="blue">numbers between $ 0 $ and $ 1 $</span>.</p>
    </div>

    <div class="step">
      <div class="extra bottom"><big>$$ \class{blue}{\frac{0 + 1}{2}} $$</big></div>
      <p class="math">Between any two numbers, we can find a new number in between: their average. This leads to $ \frac{1}{2} $.</p>
    </div>

    <div class="step">
      <div class="extra bottom"><big>$$ \frac{a + b}{2} $$</big></div>
      <p>By repeatedly taking averages, we keep finding new numbers, filling up the interval.</p>
    </div>

    <div class="step">
      <p>If we separate out every step, we get a <em>binary tree</em>.</p>
    </div>

    <div class="step">
      <p class="math">You can think of this as a map of all the fractions of $ 2^n $. Given any such fraction, say <big>$ \frac{13}{32} = \frac{13}{2^5} $</big>, there is a unique path of lefts and rights that leads directly to it. At least, as long as it lies between $ 0 $ and $ 1 $.</p>
    </div>
  
    <div class="step">
      <p class="math">
      Note that the graph resembles a fractal and that the distance to the top edge is divided in half with every step. But we only ever explore a finite amount of steps. Therefor, we are not taking a limit and we'll never actually touch the edge. 
      </p>
    </div>

    <div class="step">
      <div class="extra left" data-hold="1">$$ \frac{2 \cdot a + b}{3} $$</div>
      <div class="extra right" data-hold="1">$$ \frac{a + 2 \cdot b}{3} $$</div>
      <p class="math">But we can take thirds as well, leading to fractions with a power of $ 3^n $ in their denominator.</p>
    </div>

    <div class="step">
      <p class="math">As some numbers can be reached in multiple ways, we can eliminate some lines, and end up with this graph, where every number sprouts into a three-way, <em>ternary tree</em>. Again, we have a map that gives us a unique path to any fraction of $ 3^n $ in this range, like <big>$ \frac{11}{27} = \frac{11}{3^3} $</big>.</p>
    </div>

    <div class="step">
      <div class="extra">
        $$ \frac{21}{60} = \frac{21}{2^2 \cdot 3 \cdot 5} $$
      </div>
      
      <p class="math">Because we can do this for any denominator, we can define a way to get to any rational number in a finite amount of steps. Take for example <big>$ \frac{21}{60} $</big>. We decompose its denominator into prime numbers and begin with $ 0 $ and $ 1 $ again.</p>
    </div>

    <div class="step">
      <div class="extra top edge hold3">
        <small><small>
        $$ \frac{21}{60} = \frac{21}{2^2 \cdot 3 \cdot 5} $$
        </small></small>
      </div>

      <p class="math">There is a division of $ 2^2 $, so we do two binary splits. This time, I'm repeating the previously found numbers so you can see the regular divisions more clearly. We get <span class="green">quarters</span>.</p>
    </div>

    <div class="step">
      <p class="math">The next factor is $ 3 $ so we divide into thirds once. We now have <span class="gold">twelfths</span>.</p>
    </div>

    <div class="step">
      <p class="math">For the last division we chop into fifths and get <span class="orangered">sixtieths</span>.</p>
    </div>
    
    <div class="step">
      <p class="math">$ \frac{21}{60} $ is now the <span class="orangered">21st number from the left</span>.</p>
    </div>

    <div class="step">
      <p class="math">But this means we've found a clear way to visualize <em>all</em> the rational numbers between $ 0 $ and $ 1 $: it's all the numbers we can reach by applying a finite number of binary (2), ternary (3), quinary (5) etc. divisions, for any denominator. So there's always a finite <em>gap</em> between any two rational numbers, even though there are infinitely many of them.</p>
    </div>

    <div class="step">
      <p>The rational numbers are <em>not continuous</em>. Therefor, it is more accurate to picture them as a set of tick marks than a connected number line.</p>
    </div>

    <div class="step">
      <p class="math">To find continuity then, we need to revisit one of our earlier trees. We'll pick the binary one.<br>While every fork goes two ways, we actually have a third choice at every step: we can choose to stop. That's how we get a finite path to a whole fraction of $ 2^n $.</p>
    </div>

    <div class="step">
      <p>But what if we never stop? We have to apply a limit: we try to spot a pattern and try to <span class="green">fast-forward it</span>. Note that by halving each step vertically on the graph, we've actually <em>linearized</em> each approach into a straight line which ends. Now we can <em>take limits visually</em> just by intersecting lines with the top edge.</p>
    </div>

    <div class="step">
      <p class="math">Right away we can spot two convergent limits: by always choosing either the <span class="orangered">left</span> or the <span class="green">right</span> branch, we end up at respectively $ 0 $ and $ 1 $.</p>
    </div>

    <div class="step">
      <p class="math"><span class="orangered">These</span> <span class="green">two</span> sequences both converge to $ \frac{1}{2} $. It seems that 'at infinity steps', the graph meets up with itself in the middle.</p>
    </div>

    <div class="step">
      <p class="math">But the graph is now a true fractal. So the same convergence can be found here. In fact, the graph meets up with itself anywhere there is a multiple of <big>$ \frac{1}{2^n} $</big>.</p>
    </div>
    
    <div class="step">
      <p class="math">That's pretty neat: now we can eliminate the option of stopping altogether. Instead of ending at $  \frac{5}{16} $, we can simply take <span class="purple">one additional step in either direction, followed by infinitely many opposite steps</span>. Now we're <em>only</em> considering paths that are infinitely long.</p>
    </div>

    <div class="step">
      <p class="math">But if this graph only leads to fractions of $ 2^n $, then there must be gaps between them. In the limit, the distance between any two adjacent numbers in the graph shrinks down to <em>exactly</em> $ 0 $, which suggests there are no gaps. This infinite version of the binary tree must lead to a lot more numbers than we might think.<br>
         Suppose we take a path of <span class="orangered">alternating left and right steps</span>, and extend it forever. Where do we end up?</p>
    </div>

    <div class="step">
      <p>We can apply the same principle of an <span class="gold">upper and lower bound</span>, but now we're approaching from both sides at once. Thanks to our linearization trick, the entire sequence fits snugly inside a triangle.</p>
    </div>

    <div class="step">
      <p class="math">If we zoom into the convergence at infinity, we actually end up at $ \class{orangered}{\frac{2}{3}} $.<br>
        Somehow we've managed to coax a fraction of $ 3 $ out of a perfectly regular <em>binary</em> tree.</p>
    </div>

    <div class="step">
      <p class="math">If we <span class="orangered">alternate two lefts with one right</span>, we can end up at $ \class{orangered}{\frac{4}{7}} $. This is remarkable: when we tried to visualize all the rational numbers by combining all kinds of divisions, we were overthinking it. We only needed to take <em>binary divisions</em> and repeat them infinitely with a <em>limit</em>.</p>
    </div>

    <div class="step">
      <p>Every single rational number can then be found by taking a finite amount of steps to get to a certain point, and then settling into a <em>repeating pattern of lefts and/or rights</em> all the way to infinity.</p>
    </div>

    <div class="step">
      <p class="math">If we can find numbers between $ 0 $ and $ 1 $ this way, we can apply the exact same principle to the range $ 1 $ to $ 2 $. So we can connect two of these graphs into a single graph with its tip at $ 1 $.</p>
    </div>

    <div class="step">
      <p class="math">But we can repeat it as much as we like. The full graph is not just infinitely divided, but infinitely big, in that no finite box can contain it. That means it leads to <em>every single positive rational number</em>. We can start anywhere we like. Is your mind blown yet?</p>
    </div>

    <div class="step">
      <p class="math">No? Ok. But if this works for positives, we can build a similar graph for the negatives just by mirroring it. So we now have a map of the entire rational number set. All we need to do is take <em>infinite paths that settle into a repeating pattern</em> from either a positive or a negative starting point. When we do, we find every such path leads to a rational number.<br>
        So any rational number can be found by taking an infinite stroll on one of <em>two</em> infinite binary trees.</p>
    </div>

    <div class="step">
      <p class="math">Wait, did I say two infinite trees? Sorry, I meant <em>one</em> infinitely big tree.<br>See, if we repeatedly scale up a <span class="green">fractal binary tree</span> and apply a limit to that, we end up with almost exactly the same thing. Only this time, the two downward diagonals always eventually fold back towards $ 0 $. This creates a path of <em>infinity + 1</em> steps downward. While that might not be very practical, it suggests you can ride out to the restaurant at the end of the universe, have dinner, and take a single step to get back home.</p>
    </div>

    <div class="step">
      <p class="math">Is it math, or visual poetry? It's time to bring this fellatio of the mind to its inevitable climax.</p>
    </div>
    
    <div class="step">
      <div class="extra hold2 bottom left" data-align-x=".4" data-align-y=".8">$ \class{blue}{0} $</div>
      <div class="extra hold2 bottom right" data-align-x=".4" data-align-y=".8">$ \class{green}{1} $</div>

      <div class="extra hold2 left top" data-align-x="1.0" data-align-y="0.25">$ \class{blue}{0} $</div>
      <div class="extra hold2 left top" data-align-x=".6" data-align-y="0.25">$ \class{green}{1} $</div>

      <div class="extra hold2 right top" data-align-x=".6" data-align-y="0.25">$ \class{blue}{0} $</div>
      <div class="extra hold2 right top" data-align-x="1.0" data-align-y="0.25">$ \class{green}{1} $</div>
      
      <p class="math">You may wonder, if this map is so amazing, how did we ever do without?<br>
        Let's label our branches. If we go left, we call it $ 0 $. If we go right, we call it $ 1 $.</p>
    </div>

    <div class="step">
      <div class="extra">
        $$
        \frac{5}{3} = \class{green}{11}\class{blue}{0}\hspace{2pt}\class{green}{1}\class{blue}{0}\hspace{2pt}\class{green}{1}\class{blue}{0}…
        $$
      </div>

      <p class="math">We can then identify any number by writing out the infinite path that leads there as a sequence of ones and zeroes—bits.<br><br>But you already knew that.</p>
    </div>

    <div class="step">
      <div class="extra">
        $$
        \frac{5}{3} = \class{green}{1}.\class{green}{1}\class{blue}{0}\hspace{2pt}\class{green}{1}\class{blue}{0}\hspace{2pt}\class{green}{1}\class{blue}{0}…_2
        $$
      </div>

      <p class="math">See we've just rediscovered the binary number system. We're so used to numbers in decimal, <em>base 10</em>, we didn't notice. Yet we all learned that rational numbers consist of digits that settle into a repeating sequence, a <em>repeating pattern of turns</em>. Disallowing finite paths works the same, even in decimal: the number $ 0.95 $ can be written as $\, 0.94999…\, $, i.e. <em>take one final step in one direction, followed by infinitely many steps the other way</em>.</p>
    </div>

    <div class="step">
      <div class="extra">
        $$
        \frac{4}{5} = \class{blue}{0}.\class{green}{11}\class{blue}{00}\hspace{2pt}\class{green}{11}\class{blue}{00}…_2 
        $$
      </div>

      <p class="math">
        When we write down a number digit by digit, we're really <em>following the path to it</em> in a graph like this, dialing the number's … er … number. The rationals aren't <em>shaped</em> like a binary tree, rather, they <em>look</em> like a binary tree when viewed through the lens of binary division. Every infinite binary, ternary, quinary, etc. tree is then a different but complete perspective of the same underlying thing. We don't have <em>the</em> map, we have one of infinitely many maps.</p>
    </div>

    <div class="step">
      <div class="extra top" data-delay=".8" data-align-y=".25">
        $$
        π = \class{green}{11}.\class{blue}{00}\class{green}{1}\class{blue}{00}\class{green}{1}\class{blue}{0000}\class{green}{1}…_2
        $$
      </div>
      
      <p class="math">
       Which means we can show this graph is actually an interdimensional number portal.<br>
       See, we already know <em>where</em> the missing numbers are. Irrational numbers like $ π $ form a never-repeating sequence of digits. If we want to reach $ π $, we find it's at the end of an infinite path whose turns <em>do not repeat</em>. By allowing such paths, our map leads us straight to them. Even though it's made out of only  <em>one kind of rational number</em>: division by two.</p>
    </div>

    <div class="step">
      <div class="extra" data-delay="1.6"><big><big>
        $$
          π = \mathop{\class{no-outline}{►\hspace{-2pt}►}}_{\infty\hspace{2pt}} x_n \,?
        $$
        </big></big>
      </div>

      <p class="math">
        So now we've invented <em>real numbers</em>. How do we visualize this invention? And where does continuity come in? What we need is a procedure that generates such a non-repeating path <em>when taken to the limit</em>. Then we can figure out where the behavior at infinity comes from.
      </p>
    </div>

    <div class="step">
      <p>Because the path never settles into a pattern, we can't pin it down with a single neat triangle like before. We try something else. At every step, we can see that the <em>smallest</em> number we can still reach is found by <span class="orangered">always going left</span>. Similarly, the <em>largest</em> available number is found by <span class="green">always going right</span>. Wherever we go from here, it will be somewhere in this range.</p>
    </div>

    <div class="step">
      <p>We can set up shrinking intervals by placing such triangles along the path, forming a nested sequence.</p>
    </div>

    <div class="step">
      <div class="extra left">
        $$
          \begin{align}
            3 \leq &amp; π \leq 4 \\
            3.1 \leq &amp; π \leq 3.2 \\
            3.14 \leq &amp; π \leq 3.15 \\ 
            3.141 \leq &amp; π \leq 3.142 \\ 
            3.1415 \leq &amp; π \leq 3.1416 \\
            3.14159 \leq &amp; π \leq 3.14160 \\
          \end{align}
        $$
      </div>

      <div class="extra right">
        $$
          \begin{align}
            11_2 \leq &amp; π \leq 100_2 \\ 
            11.0_2 \leq &amp; π \leq 11.1_2 \\ 
            11.00_2 \leq &amp; π \leq 11.01_2 \\
            11.001_2 \leq &amp; π \leq 11.010_2 \\
            11.0010_2 \leq &amp; π \leq 11.0011_2 \\
            11.00100_2 \leq &amp; π \leq 11.00101_2 \\
          \end{align}
        $$
      </div>

      <p class="math">
        What we've actually done is rounded up and down at every step, to find an upper and lower bound with a certain amount of digits. This works in any number base.
      </p>
    </div>

    <div class="step">
      <p class="math">Let's examine these intervals by themselves. We can see that due to the binary nature, each interval covers either the left or right side of its ancestor. Because our graph goes on forever, there are infinitely many nested intervals. This <em>tower of $ π $</em> never ends and never repeats itself, we just squeezed it into a finite space so we could see it better.</p>
    </div>

    <div class="step">
      <p class="math">If we instead approach a rational number like $ \frac{10}{3} = 3.333…\, $ then the tower starts repeating itself at some point. Note that the intervals <em>don't slide smoothly</em>. Each can only be in one of two places relative to its ancestor.</p>
    </div>

    <div class="step">
      <p class="math">In order to reach a different rational number, like $ 3.999… = 4 $, we have to establish a different repeating pattern. So we have to rearrange infinitely many levels of the tower all at once, from one configuration to another. This reinforces the notion that rational numbers are not continuous.</p>
    </div>

    <div class="step">
      <p class="math">If the tower converges to a number, then the top must be infinitely thin, i.e. $ 0 $ units wide. That would suggest it's meaningless to say what the interval at infinity looks like, because it stops existing. Let's try it anyway.</p>
    </div>

    <div class="step">
      <p class="math">
        There is only one question to answer: does the interval cover the <span class="orangered">left side</span>, or the <span class="green">right</span>?
      </p>
    </div>

    <div class="step">
      <p class="math">
        Oddly enough, in this specific case of $ 3.999…\, $ there is an answer. The tower <em>leans to the right</em>. Therefor, the state of the interval is the same all the way up. If we take the limit, it converges and the <em>final interval</em> goes right.
      </p>
    </div>

    <div class="step">
      <p class="math">
        But we can immediately see that we can build a second tower that leans left, which converges on the same number. We could distinguish between the two by writing it as $ 4.000…\, $ In this case the <em>final interval</em> goes left.
      </p>
    </div>

    <div class="step">
      <p class="math">
        If we approach $ 10/3 $, we take a path of <em>alternating left and right steps</em>. The state of the interval at infinity becomes like our paradoxical lamp from before: it has to be both left and right, and therefor it is neither, it's simply undefined.
      </p>
    </div>

    <div class="step">
      <p class="math">
        The same applies to irrational numbers like $ π $. Because the sequence of turns never repeats itself, the interval flips arbitrarily between left and right forever, therefor it is in an undefined state at the end.
      </p>
    </div>

    <div class="step">
      <p class="math">
        But there's another way to look at this.<br>
        If the interval converges to the number $ π $, then the two sequences of respectively <span class="orangered">lower</span> and <span class="green">upper bounds</span> also converge to $ π $ individually.
      </p>
    </div>

    <div class="step">
      <p class="math">
        Remember how we derived our bounds: we rounded down by always taking <span class="orangered">lefts</span> and rounded up by always taking <span class="green">rights</span>. The shape of the tower depends on the specific path you're taking, not just the number you reach at the end.
      </p>
    </div>

    <div class="step">
      <p class="math">
        That means we're approaching the <span class="orangered">lower bounds</span> so they all end in $ 0000… \, $ Their towers always lean left.
      </p>
    </div>
    
    <div class="step">
      <p class="math">If we then take the <em>limit of their final intervals</em> as we approach $ π $, that goes <span class="orangered">left</span> too. Note that this is a double limit: first we find the <em>limit of the intervals</em> of each tower individually, then we take the <em>limit over all the towers as we approach $ π $</em>.
      </p>
    </div>

    <div class="step">
      <p class="math">
        For the same reason, we can think of all the <span class="green">upper bounds</span> as ending in $ 1111 …\, $ Their towers always lean right. When we take the limit of their final intervals and approach $ π $, we find it points <span class="green">right</span>.
      </p>
    </div>

    <div class="step">
      <p class="math">
        But, we could actually just reverse the rounding for the upper and lower bounds, and end up with the exact opposite situation. Therefor it doesn't mean that we've invented a <span class="orangered">red $ π $</span> to the left and <span class="green">green $ π $</span> to the right which are somehow different. $ π $ is $ π $. This only says something about our procedure of building towers. It matters because the towers is how we're trying to reach a real number in the first place.
      </p>
    </div>

    <div class="step">
      <p class="math">
        See, our tower still represents a binary number of infinitely many bits. Every interval can still only be in one of two places. To run along the real number line, we'd have to rearrange infinitely many levels of the tower all at once to create motion. That still does not seem continuous.
      </p>
    </div>

    <div class="step">
      <p>
        We can resolve this if we picture the final interval of each tower as a <em>bit at infinity</em>. If we flip the bit at infinity, we swap between two equivalent ways of reaching a number, so this has no effect on the resulting number.
      </p>
    </div>

    <div class="step">
      <p class="math">
         In doing so, we're actually imagining that every real number is a rational number whose <em>non-repeating head</em> has grown infinitely big. Its <em>repeating tail</em> has been pushed out all the way past infinity. That means we can flip the repeating part of our tower between different configurations without creating any changes in the number it leads to.
      </p>
    </div>
    
    <div class="step">
      <p class="math">
        That helps a little bit with the intuition: if the tower keeps working <em>all the way up there</em>, it must be continuous at its actual tip, wherever that really is. A <em>continuum</em> is then what happens when the smallest possible step you can take isn't just as small as you want. It's so small that it no longer makes <em>any</em> noticeable difference. While that's not a very mathematical definition, I find it very helpful in trying to imagine how this might work.
      </p>
    </div>

    <div class="step">
      <div class="extra bottom" data-delay="1.6" data-align-y=".15">
        $ 1, 2, 3, 4, 5, 6, … $
      </div>
      
      <p class="math">
        Finally, we might wonder how many of each type of number there are.<br>The natural numbers are <em>countably infinite</em>: there is a procedure of steps which, in the limit, counts all of them. Just start at the beginning, and fast-forward.
      </p>
    </div>

    <div class="step">
      <div class="extra bottom" data-align-y=".15">
        $$ 1, 2, 3, 4, 5, 6, … $$
        <br><br><br><br>
      </div>
      <div class="extra bottom" data-delay=".4" data-align-y=".15">
        <br><br>
        $$ \class{orangered}{2, 4, 6, 8, 10, 12, …} $$
        <br><br>
      </div>
      <div class="extra bottom" data-delay=".8"  data-align-y=".15">
        <br><br><br><br>
        $$ \class{green}{0, 1, -1, 2, -2, 3, …} $$
      </div>
    
      <p class="math">
        We can find a similar sequence for the <span class="orangered">even natural numbers</span> by multiplying each number by two. We can also alternate between a positive and negative sequence to count <span class="green">the integers</span>. We can match up the elements one-to-one, which means all three sequences are <em>equally long</em>. They're all countably infinite.<br>There are as many even positives as positives. Which is exactly as many as all the integers combined. As counter-intuitive as it is, it is the only consistent answer.
      </p>
    </div>
    
    <div class="step">
      <div class="extra bottom right" style="background-image: url(/files/infinity-and-beyond/rationals.png); background-size: 100%; background-position: 0 0; background-repeat: no-repeat;" data-align-x=".2" data-align-y=".5"><small><small>
        $$
\begin{array}{cccccccc}
 1 \hspace{2pt}&amp;\hspace{2pt} 2 \hspace{2pt}&amp;\hspace{2pt} 3 \hspace{2pt}&amp;\hspace{2pt} 4 \hspace{2pt}&amp;\hspace{2pt} 5 \hspace{2pt}&amp;\hspace{2pt} 6 \hspace{2pt}&amp;\hspace{2pt} … \\[6pt]
 \frac{1}{2}
  \hspace{2pt}&amp;\hspace{2pt} \class{grey}{\frac{2}{2}}
  \hspace{2pt}&amp;\hspace{2pt} \frac{3}{2}
  \hspace{2pt}&amp;\hspace{2pt} \class{grey}{\frac{4}{2}}
  \hspace{2pt}&amp;\hspace{2pt} \frac{5}{2}
  \hspace{2pt}&amp;\hspace{2pt} \class{grey}{\frac{6}{2}}
  \hspace{2pt}&amp;\hspace{2pt}  \\[3pt]
 \frac{1}{3} 
  \hspace{2pt}&amp;\hspace{2pt} \frac{2}{3} 
  \hspace{2pt}&amp;\hspace{2pt} \class{grey}{\frac{3}{3}}
  \hspace{2pt}&amp;\hspace{2pt} \frac{4}{3} 
  \hspace{2pt}&amp;\hspace{2pt} \frac{5}{3} 
  \hspace{2pt}&amp;\hspace{2pt} \class{grey}{\frac{6}{3}}
  \hspace{2pt}&amp;\hspace{2pt}   \cdots \\[3pt]
 \frac{1}{4} 
  \hspace{2pt}&amp;\hspace{2pt} \class{grey}{\frac{2}{4}}
  \hspace{2pt}&amp;\hspace{2pt} \frac{3}{4} 
  \hspace{2pt}&amp;\hspace{2pt} \class{grey}{\frac{4}{4}}
  \hspace{2pt}&amp;\hspace{2pt} \frac{5}{4} 
  \hspace{2pt}&amp;\hspace{2pt} \class{grey}{\frac{6}{4}}
  \hspace{2pt}&amp;\hspace{2pt}  \\[3pt]
 \frac{1}{5} 
  \hspace{2pt}&amp;\hspace{2pt} \frac{2}{5} 
  \hspace{2pt}&amp;\hspace{2pt} \frac{3}{5} 
  \hspace{2pt}&amp;\hspace{2pt} \frac{4}{5} 
  \hspace{2pt}&amp;\hspace{2pt} \class{grey}{\frac{5}{5}}
  \hspace{2pt}&amp;\hspace{2pt} \frac{6}{5} 
  \hspace{2pt}&amp;\hspace{2pt}  \\[3pt]
 \frac{1}{6} 
  \hspace{2pt}&amp;\hspace{2pt} \class{grey}{\frac{2}{6}}
  \hspace{2pt}&amp;\hspace{2pt} \class{grey}{\frac{3}{6}}
  \hspace{2pt}&amp;\hspace{2pt} \class{grey}{\frac{4}{6}}
  \hspace{2pt}&amp;\hspace{2pt} \frac{5}{6} 
  \hspace{2pt}&amp;\hspace{2pt} \class{grey}{\frac{6}{6}}
  \hspace{2pt}&amp;\hspace{2pt}  \\[3pt]
  \hspace{2pt}&amp;\hspace{2pt} \vdots  \hspace{2pt}&amp;\hspace{2pt}   \hspace{2pt}&amp;\hspace{2pt}  \vdots \hspace{2pt}&amp;\hspace{2pt}   \hspace{2pt}&amp;\hspace{2pt}   \hspace{2pt}&amp;\hspace{2pt}   \hspace{2pt}&amp;\hspace{2pt} \class{white}{\ddots}
 \end{array}
        $$
        </small></small>
      </div>
      
      <p class="math">
         But we can take it one step further: we can find such a sequence for the rational numbers too, by laying out all the fractions on a grid. We can follow diagonals up and down and pass through every single one. If we eliminate duplicates like $ 1 = 2/2 = 3/3 $ and alternate positives and negatives, we can 'count them all'. So there are as many fractions as there are natural numbers. <em>"Deal with it"</em>, says Infinity, donning its sunglasses.
      </p>
    </div>
    
    <div class="step">
      <div class="extra">
        $$
        \begin{array}{c}
               0.\hspace{1pt}\class{green}{1}\hspace{1pt}0\hspace{1pt}0\hspace{1pt}1\hspace{1pt}1\hspace{1pt}1\hspace{1pt}0\hspace{1pt}…_2 \\
               0.\hspace{1pt}1\hspace{1pt}\class{blue}{0}\hspace{1pt}0\hspace{1pt}1\hspace{1pt}0\hspace{1pt}0\hspace{1pt}1\hspace{1pt}…_2 \\
               0.\hspace{1pt}1\hspace{1pt}0\hspace{1pt}\class{green}{1}\hspace{1pt}0\hspace{1pt}0\hspace{1pt}1\hspace{1pt}0\hspace{1pt}…_2 \\
               0.\hspace{1pt}0\hspace{1pt}1\hspace{1pt}1\hspace{1pt}\class{green}{1}\hspace{1pt}0\hspace{1pt}1\hspace{1pt}1\hspace{1pt}…_2 \\
               0.\hspace{1pt}1\hspace{1pt}0\hspace{1pt}1\hspace{1pt}1\hspace{1pt}\class{blue}{0}\hspace{1pt}0\hspace{1pt}1\hspace{1pt}…_2 \\
               0.\hspace{1pt}0\hspace{1pt}1\hspace{1pt}0\hspace{1pt}1\hspace{1pt}0\hspace{1pt}\class{blue}{0}\hspace{1pt}0\hspace{1pt}…_2 \\
               0.\hspace{1pt}0\hspace{1pt}1\hspace{1pt}1\hspace{1pt}1\hspace{1pt}1\hspace{1pt}0\hspace{1pt}\class{green}{1}\hspace{1pt}…_2 \\
               … \\
               \\
               0.\hspace{1pt}\class{blue}{0}\hspace{1pt}\class{green}{1}\hspace{1pt}\class{blue}{0\hspace{1pt}0}\hspace{1pt}\class{green}{1\hspace{1pt}1}\hspace{1pt}\class{blue}{0}\hspace{1pt}…_2
         \end{array}
        $$
      </div>
      
      <p class="math">
        The real numbers on the other hand are <em>uncountably infinite</em>: no process can list them all in the limit. The basic proof is short: suppose we did have a sequence of all the real numbers between $ 0 $ and $ 1 $ in some order. We could then build a new number by taking <span class="green">all</span> <span class="blue">the</span> <span class="green">bits</span> <span class="green">on</span> <span class="blue">the</span> <span class="blue">diagonal</span>, and flipping zeroes and ones.<br>That means this number is different from every listed number in at least one digit, so it's <em>not on the list</em>. But it's also between $ 0 $ and $ 1 $, so it should be on the list. Therefor, <em>the list can't exist</em>.
      </p>
    </div>

    <div class="step">
      <p class="math">
        This even matches our intuitive explanation from earlier. There are so many real numbers, that we had to invent a bit at infinity to try and count them, and find something that would <em>tick at least once</em> for every real number. Even then we couldn't say whether it was $ 0 $ or $ 1 $ anywhere in particular, because it literally depends on how you approach it.
      </p>
    </div>

  </div>

</div>

<div class="g8 i2"><div class="pad">
  
  <p class="math">What we just did was a careful exercise in hiding the obvious, namely the digit-based number systems we are all familiar with. By viewing them not as digits, but as paths on a directed graph, we get a new perspective on just what it means to use them. We've also seen how this means we can construct the rationals and reals using the least possible ingredients required: division by two, and limits.</p>
  
<h2>Drowning By Numbers</h2>
  
  <p class="math">In school, we generally work with the decimal representation of numbers. As a result, the popular image of mathematics is that it's the science of <em>digits</em>, not the underlying structures they represent. This permanently skews our perception of what numbers really are, and is easy to demonstrate. You can google to find countless arguments of why $ 0.999… $ is or isn't equal to $ 1 $. Yet nobody's wondering why $ 0.000… = 0 $, though it's practically the same problem: $ 0.1, 0.01, 0.001, 0.0001, … $</p>
  
  <p class="math">Furthermore, in decimal notation, rational numbers and real numbers look incredibly alike: $ 3.3333… $ vs $ 3.1415…\, $ The question of what it actually means to have infinitely many non-repeating digits, and why this results in continuous numbers, is hidden away in those 3 dots at the end. By imagining $ π $ as $ 3.1415…0000… $ or $ 3.1415…1111… $ we can intuitively bridge the gap to the infinitely small. We see how the distance between two neighbouring real numbers must be so small, that it really is equivalent to $ 0 $.</p>
  
  <p class="math">That's not as crazy as it sounds. In the field of <em>hyperreal numbers</em>, every number actually has additional digits 'past infinity': that's its infinitesimal part. You can imagine this to be a multiple of $ \frac{1}{\infty} $, an infinitely small unit greater than $ 0 $, which I'll call $ ε $. You can add $ ε $ to a real number to take an infinitely small step. It represents a difference that can only be revealed with an infinitely strong microscope. Equality is replaced with <em>adequality</em>: being equal aside from an infinitely small difference.</p>
  
<p class="math">You can explore this hyperreal number line below.</p>

</div></div>

<div class="wide full">

  <div class="iframe c">
    <iframe src="/files/infinity-and-beyond/canvas-1-hyperreals.html" class="mathbox autosize" data-max-height="320" height="320"></iframe>
  </div>
    
</div>

<div class="g8 i2"><div class="pad">
  
  <p class="math">As $ ε $ is a fully functioning hyperreal number, $ ε^2 $ is also infinitesimal. In fact, it's even infinitely smaller than $ ε $, and we can keep doing this for $ ε^3, ε^4, …\,$ To make matters worse, if $ ε $ is infinitesimal, then $ \frac{1}{ε} $ must be infinitely big, and $ \frac{1}{ε^2} $ infinitely bigger than that. So hyperreal numbers don't just have inwardly nested infinitesimal levels, but outward levels of increasing infinity too. They have infinitely many dimensions of infinity both ways.</p>
  
  <p class="math">So it's perfectly possible to say that $ 0.999… $ does not equal $ 1 $, if you mean they differ by an infinitely small amount. The only problem is that in doing so, you get much, <em>much</em> more than you bargained for.</p>

  <h2>A Tug of War Between the Gods</h2>
  
  <p class="math">That means we can finally answer the question we started out with: why did our continuous atoms seemingly all have $ 0 $ mass, when the total mass was not $ 0 $? The answer is that the mass per atom was <em>infinitesimal</em>. So was each atom's volume. The density, <em>mass per volume</em>, was the result of dividing one infinitesimal amount by another, to get a normal sized number again. To create a finite mass in a finite volume, we have to add up infinitely many of these atoms.</p>
  
  <p>These are the underlying principles of calculus, and the final puzzle piece to cover. The funny thing about calculus is, it's conceptually easy, especially if you start with a good example. What is hard is actually working with the formulas, because they can get hairy very quickly. Luckily, your computer will do them for you:</p>

</div></div>

<div class="c"></div>

<div class="wide slideshow full">

  <div class="iframe c">
    <iframe src="/files/infinity-and-beyond/mb-6-calculus.html" class="mathbox paged autosize" height="320"></iframe>
  </div>

  <div class="steps">

    <div class="step">
      <p class="math">We're going to go for a drive.</p>
    </div>

    <div class="step">
      <p class="math">We'll graph speed versus time. We have <em>kilometers per hour</em> vertically, and <em>hours</em> horizontally. We've also got a speedometer—how fast—and an odometer—how far.</p>
    </div>

    <div class="step">
      <p class="math">Suppose we drive for half an hour at <span class="orangered">50 km/h</span>.</p>
    </div>

    <div class="step">
      <div class="extra left bottom" data-delay=".8" data-hold="1" data-align-x=".05" data-align-y=".75">$ \class{orangered}{25} $</div>
      
      <p class="math">We end up driving for 25 km. This is the area of spanned by the two lengths: $ 50 \cdot \frac{1}{2} $, a rectangle.</p>
    </div>

    <div class="step">
      <div class="extra right" data-delay="1.6" data-align-x=".87" data-align-y=".5">$ \class{orangered}{60} $</div>

      <p class="math">Now we hit the highway and maintain 120 km/h for the rest of the hour. We go an additional 60 km, the area of the second rectangle, $ 120 \cdot \frac{1}{2} $.<br>Whenever we multiply two units like speed and time, we can always visualize the result as an area.</p>
    </div>

    <div class="step">
      <div class="extra right" data-delay="1.6" data-align-x=".42" data-align-y=".5">$ \class{slate}{85} $</div>

      <p class="math">Because we crossed 85 km in one hour, this is equivalent to driving at a constant speed of 85 km/h for the duration. The <span class="slate">total area</span> is the same.</p>
    </div>

    <div class="step">
      <p class="math">If this were a race between two <span class="orangered">different</span> <span class="slate">cars</span>, we would see a photo finish. The <em>distance travelled</em> in kilometers is identical at the 1 hour mark. Where they differ is in their speed along the way, with the red car falling behind and then catching up.</p>
    </div>

    <div class="step">
      <p class="math">The difference is visible in the <span class="green">slope</span> of both paths. The faster the car, the more quickly it <em>accumulates</em> kilometers. If it drove 25 km in half an hour, then its speed was 50 km/h, $ \frac{25}{0.5} $. This is the distance travelled <em>divided</em> by the time it took, <span class="blue">vertical divided by horizontal</span>.</p>
    </div>

    <div class="step">
      <p class="math"><span class="green">Slope</span> is a <em>relative</em> thing. If we shrink the considered time, the distance shrinks along with it, and the resulting speed is the same. What we're really doing is formalizing the concept of a <em>rate of change</em>, of distance over time.</p>
    </div>

    <div class="step">
      <p class="math">Constant speed means a constant <em>increase</em> in distance. We can directly relate the <em>area being swept out left to right</em> with the <em>accumulated distance</em> by each car. This is clue number 1.</p>
    </div>

    <div class="step">
      <p class="math">Now suppose the red car starts ahead by 10 km and drives the same speeds.<br>
        It will also end up 10 km ahead after 1 hour, its path has simply been shifted by 10 units. The slope is unchanged: it doesn't matter where you are and where you've been, only how fast you're going <em>right now</em>. It's what's called an <em>instantaneous quantity</em>, it describes a situation only in the moment. This is clue number 2.</p>
    </div>

    <div class="step">
      <p class="math">In order to get ahead, the red car had to drive there. So we can imagine it started earlier, $ \frac{1}{5} $ of an hour, driving for 10 km at the same speed. Again, the equality holds: area swept out equals accumulated distance, we add another $ 50 \cdot \frac{1}{5} $. Constant slope still equals constant speed.</p>
    </div>

    <div class="step">
      <p class="math">One curve describes how the other changes <em>in the moment</em>, therefor the two quantities are linked somehow. We add up area to go from speed to distance; we find slope to go from distance to speed. We're going to examine this two-way relationship more.</p>
    </div>

    <div class="step">
      <p class="math">Real cars don't start or stop on a dime, they <em>accelerate</em> and <em>decelerate</em>. So we're going to try more realistic behavior.</p>
    </div>

    <div class="step">
      <p class="math">Suppose the <span class="orangered">speed</span> follows a curve. In one hour, the car starts from 0 km/h, accelerates to over 100 km/h and then smoothly decelerates back to standstill. The <span class="slate">distance travelled</span> also curves smoothly, from 0 to 60 km, so we've driven 60 km in total.</p>
    </div>

    <div class="step">
      <p class="math">We can immediately see that at the point where the car was <span class="gold">going fastest</span>, the distance was increasing the most. Its slope is steepest at that point. The relationship between the two curves holds.</p>
    </div>

    <div class="step">
      <p class="math">But actually measuring it is a problem. First, there are no more straight sections to measure the slope on. If we take two points on a curve, the line that connects them doesn't touch the curve, it crosses it at an angle.</p>
    </div>

    <div class="step">
      <p class="math">Second, we can no longer measure the area by dividing it into rectangles, or any other simple geometric shape. There will always be gaps. We can solve both of these problems with a dash of infinity.</p>
    </div>

    <div class="step">
      <p class="math">
        We'll start with area. We have to find an upper and a lower bound again.<br>We're going to <span class="gold">divide</span> the curve into 4 sections.
      </p>
    </div>

    <div class="step">
      <p class="math">
        First, the <span class="orangered">upper bound</span>. We find the highest value in each section and make a rectangle of that height. This approach is too greedy and overestimates.
      </p>
    </div>

    <div class="step">
      <p class="math">
        The <span class="slate">lower bound</span> is similar. We find the smallest value in each interval and make rectangles of that height.<br>This underestimates and leaves areas uncovered.
      </p>
    </div>

    <div class="step">
      <p class="math">
        If we do 7 divisions instead. We can see that the <span class="orangered">upper bound</span> has decreased: there is less excess area. The <span class="slate">lower bound</span> has increased: the gaps are smaller and more area is covered.
      </p>
    </div>

    <div class="step">
      <p class="math">
        With 10 divisions, it's even better. It seems the upper and lower bounds are approaching each other.
      </p>
    </div>

    <div class="step">
      <p class="math">
        And the same at 13 divisions. If we keep doing this, our slices will get thinner and thinner, and we'll be adding more of them together. If we take a limit, each slice becomes <em>infinitely thin</em>, and there are <em>infinitely many of them</em>. Let's step back and see what that means.
      </p>
    </div>

    <div class="step">
      <p class="math">
        Take for example the sequence of <span class="slate">lower bounds</span>.
      </p>
    </div>

    <div class="step">
      <p class="math">
        Because every slice is equally wide, we can glue them together into a single rectangle per step.<br>Its width $ w $ is the thickness of a single slice, and its height $ h $ is the sum of the heights of the slices.
      </p>
    </div>

    <div class="step">
      <p class="math">
        In the limit, this rectangle becomes both infinitely thin and infinitely tall. This is a <em>tug of war</em> between Zero and Infinity where at first sight, they <em>both</em> seem to win. That's a problem. Luckily, we're not interested in the rectangle itself, but rather its <em>area</em>.
      </p>
    </div>

    <div class="step">
      <p class="math">
        We can change a rectangle's sides without changing its area. We <em>multiply</em> its width by one factor (e.g. $ 2 $), and <em>divide</em> the height by the same amount. The area $ 2w \cdot \frac{h}{2} $ is unchanged. Hence, we can <em>normalize</em> our rectangles to all have the same width, for example $ 1 $.
      </p>
    </div>

    <div class="step">
      <p class="math">
        We can do the same for the upper bounds. We can see that both areas are converging on the same value. This is the true area under the curve, which is neither zero nor infinite. In this tug of war, both parties are equally matched.
      </p>
    </div>
    
    <div class="step">
      <p class="math">
        Now our sequence looks very different: it's approaching a definite area, sandwiched between red and blue.
      </p>
    </div>

    <div class="step">
      <div class="extra left bottom" data-delay="1.6" data-align-x=".04" data-align-y=".74">$ \class{slate}{60} $</div>
      <p class="math">
        If we take the limit, we get the <em>area under our curve</em>.
      </p>
    </div>

    <div class="step">
      <div class="extra right bottom" data-delay="1.6" data-align-x=".2" data-align-y=".2">$ \class{orangered}{60} $</div>
      <p class="math">
        This way we can find the area <em>under any smooth curve</em>. This process is called <em>integration</em>. The symbol for integration is $ \int_a^b $ where $ a $ and $ b $ are the start and end points you're integrating between. The S-shape stands for our <em>sum</em>, adding up infinitely many pieces.
      </p>
    </div>

    <div class="step">
      <div class="extra bottom edge" data-delay="4.6" data-align-y="1">$$ \int_0^T \! f(t) \mathrm{d} t $$</div>
      <p class="math">
        We can then integrate one curve to make another, by sweeping out area horizontally from a fixed starting point. We move the end point to a time $ T $ and plot the accumulated value along the way. Using limits, we can do this <em>continuously</em>. This takes us from <span class="orangered">speed</span> to <span class="slate">distance travelled</span>. The quantity $ \,\mathrm{d}t\, $ is the <em>infinitesimal width</em> of each slice, an infinitely small amount of time.
      </p>
    </div>
     
    <div class="step">
       <p class="math">Now we just need to figure out the reverse and find slopes. We'll go back to our failed attempt from earlier.</p>
     </div>

     <div class="step">
       <p class="math">If we shrink the distance we're considering, our <span class="green">slope estimate</span> gets closer to the true value. But if we try to take a limit, we end up dividing $ 0 $ by $ 0 $.</p>
     </div>

     <div class="step">
       <p class="math">Instead we need to normalize our sequence again so it doesn't vanish.</p>
     </div>

     <div class="step">
       <p class="math">We only care about <span class="green">slope</span>: the ratio of the <span class="blue">two right sides</span>. Which means, if we scale up each triangle, the ratio is unchanged. That just comes down to <em>multiplying</em> both sides by the same number. Again we can scale them all to the exact same width.</p>
     </div>

     <div class="step">
       <p class="math">Now we've created a limit that <em>does converge</em> to something rather than nothing.</p>
     </div>

     <div class="step">
       <p class="math">This finite value is the slope at the point we were homing in on. Because we can apply this process at any point on the curve, we can find the exact slope anywhere. This is called <em>finding the derivative</em> or <em>differentiation</em>.</p>
     </div>

     <div class="step">
       <div class="extra bottom edge" data-delay="4.6" data-align-y="1">$$ \frac{ \mathrm{d} f(t) }{\mathrm{d} t} $$</div>
       <p class="math">We can also apply this process over an entire curve to generate a new one.  So now we know how to go the other way: <span class="slate">distance</span> to <span class="orangered">speed</span>. Mathematically, we are dividing an infinitesimal piece of the distance, $ \,\mathrm{d} \class{slate}{f(t)}\, $, by an infinitesimal slice of time $ \,\mathrm{d} t\, $. Working with infinitesimal formulas is tricky however. There's always an implied limit being taken in order to reach them in the first place. Indeed, it took centuries to formalize this fuzzy explanation into what we call <em>differential forms</em> today.</p>
     </div>

     <div class="step">
       <p class="math">We can note that if we shift the <span class="slate">distance curve</span> up or down, the <span class="orangered">speed</span> is unchanged. When you take a derivative, any constant value you've added to your function simply disappears. This shows again that speed is always <em>in the moment</em>, it only describes what's going on in an infinitely short piece of curve.</p>
     </div>

     <div class="step">
       <p class="math">Differentiation is then like <em>x-ray specs</em> for curves and quantities, and it's turtles all the way down. For example, if we differentiate <span class="orangered">speed</span>, we get <span class="green">acceleration</span>. This is another <em>rate of change</em>, of speed over time. We see the car's acceleration is initially positive, speeding up, and then goes negative, to slow down, i.e. accelerate in the opposite direction.<br><em>Note: The acceleration has been divided by 4 to fit.</em></p>
     </div>

     <div class="step">
       <p class="math">If we <span class="green">integrate acceleration</span> to get <span class="orangered">speed</span>, we have to count the second part as <em>negative area</em>: it is causing the speed to <em>decrease</em>.</p>
     </div>

     <div class="step">
       <p class="math">We can see that the point of <span class="gold">maximum speed</span> is the point where the <span class="green">acceleration</span> passes through $ 0 $. One of the most useful applications of derivatives is indeed to find a maximum or minimum of a curve more easily. No matter where it is, the slope at such a point must always be <em>horizontal</em>—provided the curve is smooth.</p>
     </div>

     <div class="step">
       <p class="math">Let's end this with a more exciting example. What's tall, fast and makes kids scream?</p>
     </div>

     <div class="step">
       <p class="math">A roller coaster! We'll construct a little track by welding together pieces of circles and lines.</p>
     </div>

     <div class="step">
       <p class="math">Alas, we shouldn't be too proud of our creation. Even though it looks smooth, there's something very wrong. This is how you build roller coasters when you <em>don't want people to have fun</em>. To see the problem, we need to use our x-ray specs.</p>
     </div>

     <div class="step">
       <div class="extra bottom edge">$$ \class{orangered}{f^{\prime}(x)} = \frac{\mathrm{d}\class{slate}{f(x)}}{\mathrm{d}x} $$</div>
       
       <p class="math">We differentiate the height into its <span class="orangered">slope</span>. It has sharp corners all over the place. Even though the track itself looks smooth, it doesn't <em>change</em> smoothly. The slope is constant in the straight sections and changes rapidly in the curved sections.</p>
     </div>

     <div class="step">
       <div class="extra bottom edge">$$ \class{green}{f^{\prime\prime}(x)} = \frac{\mathrm{d^2}\class{slate}{f(x)}}{\mathrm{d}x^2} $$</div>

       <p class="math">If we take the derivative of the <span class="orangered">slope</span>, i.e. find the <em>slope's slope</em>, we get a measure of <span class="green">curvature</span>. It's positive inside valleys, negative on top of crests. This graph is even worse: there are sharp peaks and cliffs. Note that in the formula, we are now dividing by the square of the infinitesimal distance $ \mathrm{d}x $. This is like going <em>two levels down</em> on the hyperreal number line and back up again.</p>
     </div>

     <div class="step">
       <div class="extra bottom edge">$$ \class{teal}{κ(x)} = \frac{1}{ρ} = \frac{ \class{green}{f^{\prime\prime}(x)} } { (1 + \class{orangered}{f^{\prime}(x)}^2)^{3/2} } $$</div>

       <p class="math">      
         We can see better if we replace the second derivative with the <em>2D curvature</em>.<br>This is the radius of the circle that touches the curve at a given point. As this radius gets infinitely big on straight sections, we use its inverse, $ \class{teal}{κ} $. Because of how we built the track, $ κ $ switches between $ 0 $ and a constant positive or negative value.<br>At every switch, there will be a corresponding change in force, a <em>jerk</em>.</p>
     </div>

     <div class="step">

       <p class="math">      
         Let's simulate a ride. As riders go through our curved sections, their inertia will push them to the outside of the curve. From their point of view, this is a <em>centrifugal force</em> up or down. We'll plot the (subjective) <span class="purple">vertical G force</span> including gravity. It starts at a comfy <em>1 G</em>, but then swings wildly between 0.5 G and 1.25 G.</p>
     </div>

     <div class="step">
       <p class="math">Even though the track seems smooth, we can see that the vertical G's are not. Every time we enter a curve, we experience a sudden jerk up or down. This is due to the jumps in the curvature. The G's are themselves curved, because the rider's sense of gravity decreases as the cart goes vertical. The sharp dips below 0.5 G are not simulation errors: this is actually what it would feel like.</p>
     </div>

     <div class="step">
       <p class="math">To really highlight the problem, we need to x-ray the G's and derive again. G forces are a form of <em>acceleration</em>. The derivative of acceleration is a change in force, called <span class="blue">jerk</span>. Whenever it's non-zero, you feel <em>jerked</em> in a particular direction.</p>
     </div>

     <div class="step">
       <p class="math">
         To fix this, we need to alter the curve of the track and smooth it out at all the different levels of differentiation. Here I've applied a <em>relaxation</em> procedure. It's like a blur filter in photoshop: we replace every point on the track with the average of its neighbours. We get a subtly <span class="green">different curve</span>. Its height hasn't changed much at all, it's just a little bit less tense.
      </p>
     </div>

     <div class="step">
       <p class="math">
         But this minor change has a huge effect on both <span class="orangered">slope</span> and <span class="teal">radius of curvature</span>. They are completely smoothed out, with all corners and jumps removed.
      </p>
     </div>

     <div class="step">
       <p class="math">
         If we do another simulation, the <span class="purple">G force graph</span> looks completely different. There are no more jumps.
      </p>
     </div>

     <div class="step">
       <p class="math">
         But the real difference is in <span class="blue">jerk</span>. There are no more actual <em>jerks</em>, only smooth oscillations. Instead of bruises, riders will get butterflies. Thanks to calculus, we avoided that painful lesson without ever having to ride it ourselves.
      </p>
     </div>
     
     <div class="step">
       <p class="math">
         Please check your pockets for loose items. Lost property will not be returned.
      </p>
     </div>

     <div class="step">
       <p class="math">
         Let's start with the original, unrelaxed track. Thanks to calculus, we can simulate head-bobbing so you can get a feel for how jerky this is. Even virtually, this isn't very pleasant.
      </p>
     </div>

     <div class="step">
       <p class="math">
         This is the improved track. Notice the smooth transitions in and out of curves.
      </p>
     </div>

     <div class="step">
       <p class="math">
         And that's how you make sweet roller coasters: by building them out of infinitely small, <em>smooth</em> pieces, so you don't get jerked around too much.
      </p>
     </div>

  </div>

</div>

<div class="g8 i2"><div class="pad">
  
<p>That was differential and integral calculus in a nutshell. We saw how many people actually spend hours every day sitting in front of an <em>integrator</em>: the odometers in their cars, which integrate speed into distance. And the derivative of speed is acceleration—i.e. how hard you're pushing on the gas pedal or brake, combined with forces like drag and friction.</p>

<p>By using these tools in equations, we can describe laws that relate quantities to their <em>rates of change</em>. Drag, also known as air resistance, is a force which gets stronger the faster you go. This is a relationship between the first and second derivatives of position.</p>

<p>In fact, the relaxation procedure we applied to our track is equivalent to another physical phenomenon. If the curve of the coaster represented the temperature along a thin metal rod, then the heat would start to equalize itself in exactly that fashion. Temperature wants to be smooth, eventually averaging out completely into a flat curve.</p>

<p>Whether it's heat distribution, fluid dynamics, wave propagation or a head bobbing in a roller coaster, all of these problems can be naturally expressed as so called <em>differential equations</em>. Solving them is a skill learned over many years, and some solutions come in the form of infinite series. Again, infinity shows up, ever the uninvited guest at the dinner table.</p>

<h2>Closing Thoughts</h2>
  
<p>Infinity is a many splendored thing but it does not lift us up where we belong. It boggles our mind with its implications, yet is absolutely essential in math, engineering and science. It grants us the ability to see the impossible and build new ideas within it. That way, we can solve intractable problems and understand the world better.</p>

<p>What a shame then that in pop culture, it only lives as a caricature. Conversations about infinity occupy a certain sphere of it—Pink Floyd has been playing on repeat, and there's usually someone peddling crystals and incense nearby.<br>
<em>"Man, have you ever, like, tried to imagine infinity…?"</em> they mumble, staring off into the distance.</p>

<p>"Funny story, actually. We just came from there…"</p>

<p><em>Comments, feedback and corrections are welcome on <a href="https://plus.google.com/112457107445031703644/posts/eso1TQwYman">Google Plus</a>. Diagrams powered by <a href="/blog/making-mathbox/">MathBox</a>.</em></p>

<p><em>More like this: <a href="/blog/how-to-fold-a-julia-fractal/">How to Fold a Julia Fractal</a>.</em></p>
  
</div></div>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[How to Fold a Julia Fractal]]></title>
    <link href="http://acko.net/blog/how-to-fold-a-julia-fractal/"/>
    <updated>2013-01-05T00:00:00+01:00</updated>
    <id>http://acko.net/blog/how-to-fold-a-julia-fractal</id>
    <content type="html"><![CDATA[<script type="text/x-mathjax-config">
MathJax.Hub.Config({
  "HTML-CSS": { availableFonts: ["TeX"] },
  extensions: ["tex2jax.js"],
  jax: ["input/TeX","output/HTML-CSS"],
  tex2jax: {inlineMath: [["$","$"],["\\(","\\)"]]},
});
</script>

<script type="text/javascript"
  src="https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS_HTML.js">
</script>

<script type="text/javascript">
// <!--
window.MathJax && MathJax.Hub.Queue(["Typeset",MathJax.Hub]);
Acko.queue(function () { Acko.Fallback.warnWebGL(); });
// -->
</script>

<div class="g8 i2 first"><div class="pad">

<h2 class="sub">A tale of numbers that like to turn</h2>

</div></div>

<div class="c"></div>

<div class="g12 first"><div class="pad">

<blockquote class="m2">
  <em class="big">"Take the universe and grind it down to the finest powder and sieve it through the finest sieve and then show me one atom of justice, one molecule of mercy. And yet," Death waved a hand, "And yet you act as if there is some ideal order in the world, as if there is some… some rightness in the universe by which it may be judged."</em>
  <div class="tr m1">– <a href="http://en.wikipedia.org/wiki/Hogfather">The Hogfather</a>, Discworld, Terry Pratchett</div>
</blockquote>

</div></div>

<aside class="g4 m2"><div class="pad tc">
  <iframe class="mathbox" src="/files/fold-a-julia/mb-0-teaser.html?22e389c1" height="600"></iframe>
</div></aside>

<div class="g8 first m2"><div class="pad">

<p>Mathematics has a dirty little secret. Okay, so maybe it's not so dirty. But neither is it little. It goes as follows:</p>

<p class="tc"><em>Everything in mathematics is a choice.</em></p>

<p>You'd think otherwise, going through the modern day mathematics curriculum. Each theorem and proof is provided, each formula bundled with convenient exercises to apply it to. A long ladder of subjects is set out before you, and you're told to climb, climb, climb, with the promise of a payoff at the end. "You'll need this stuff in real life!", they say, oblivious to the enormity of this lie, to the fact that most of the educated population walks around with <em>"vague memories of math class and <a href="http://www.maa.org/external_archive/devlin/LockhartsLament.pdf">clear memories of hating it</a>."</em></p>

<p>Rarely is it made obvious that all of these things are entirely optional—that mathematics is the art of making choices so you can discover what the consequences are. That algebra, calculus, geometry are just words we invented to group the most interesting choices together, to identify the most useful tools that came out of them. The act of mathematics is to play around, to put together ideas and see whether they go well together. Unfortunately that exploration is mostly absent from math class and we are fed pre-packaged, pre-digested math pulp instead.</p>

</div></div>

<div class="g9"><div class="pad">

<p>And so it also goes with the numbers. We learn about the natural numbers, the integers, the fractions and eventually the real numbers. At each step, we feel hoodwinked: we were only shown a part of the puzzle! As it turned out, there was a 'better' set of numbers waiting to be discovered, more comprehensive than the last.</p>

<p>Along the way, we feel like our intuition is mostly preserved. Negative numbers help us settle debts, fractions help us divide pies fairly, and real numbers help us measure diagonals and draw circles. But then there's a break. If you manage to get far enough, you'll learn about something called the <em>imaginary numbers</em>, where it seems sanity is thrown out the window in a variety of ways. Negative numbers can have square roots, you can no longer say whether one number is bigger than the other, and the whole thing starts to look like a pointless exercise for people with far too much time on their hands.</p>

<p>I blame it on the name. It's misleading for one very simple reason: all numbers are imaginary. You cannot point to anything in the world and say, "This is a 3, and that is a 5." You can point to three apples, five trees, or chalk symbols that represent 3 and 5, but the concepts of 3 and 5, the numbers themselves, exist only in our heads. It's only because we are taught them at such a young age that we rarely notice.</p>

</div></div>

<aside class="g3">
  <p class="math">
    $$ 3 - 5 = \,? $$
    $$ 4\;/\; 6 = \,? $$
    $$ \sqrt{50} = \,? $$
    $$ \sqrt{-4} = \,? $$
  </p>
  <p class="tc">
    Questions that required us to invent new numbers in order to answer them consistently.
  </p>
</aside>

<div class="c"></div>

<div class="g7 r"><div class="pad">

<p>So when mathematicians finally encountered numbers that acted just a little bit different, they couldn't help but call them <em>fictitious</em> and <em>imaginary</em>, setting the wrong tone for generations to follow. Expectations got in the way of seeing what was truly there, and it took decades before the results were properly understood.</p>

<p>Now, this is not some esoteric point about a mathematical curiosity. These imaginary numbers—called <em>complex numbers</em> when combined with our ordinary real numbers—are essential to quantum physics, electromagnetism, and many more fields. They are naturally suited to describe anything that turns, waves, ripples, combines or interferes, with itself or with others. But it was also their unique structure that allowed <a href="http://en.wikipedia.org/wiki/Benoit_Mandelbrot">Benoit Mandelbrot</a> to create his stunning fractals in the late 70s, dazzling every math enthusiast that saw them.</p>

<p>Yet for the most part, complex numbers are treated as an inconvenience. Because they are inherently multi-dimensional, they defy our attempts to visualize them easily. Graphs describing complex math are usually simplified schematics that only hint at what's going on underneath. Because our brains don't do more than 3D natively, we can glimpse only slices of the hyperspaces necessary to put them on full display. But it's not impossible to peek behind the curtain, and we can gain some unique insights in doing so. All it takes is a willingness to imagine something different.</p>

<p>So that's what this is about. And a lesson to be remembered: complex numbers are typically the first kind of numbers we see that are undeniably strange. Rather than seeing a sign that says <em>Here Be Dragons, Abandon All Hope</em>, we should explore and enjoy the fascinating result that comes from one very simple choice: <em>letting our numbers turn</em>. That said, there <em>are</em> dragons. Very pretty ones in fact.</p>

</div></div>

<aside class="g5 m1">
  <a href="http://en.wikipedia.org/wiki/Mandelbrot_set"><img src="/files/fold-a-julia/mandelbrot.jpg" alt="Mandelbrot set"></a>
  <p class="tc math">
    The <a href="http://en.wikipedia.org/wiki/Mandelbrot_set">Mandelbrot Fractal</a>, powered by the simple formula $ f(z) = z^2 + c $ where $ z $ is a complex number. These sorts of relations were first studied by <a href="http://en.wikipedia.org/wiki/Gaston_Julia">Gaston Julia</a>.
  </p>
</aside>

<aside class="g5 m1">
  <a href="http://en.wikipedia.org/wiki/Dragon_curve"><img src="/files/fold-a-julia/dragon.jpg" alt="Heighway Dragon Curve"></a>
  <p class="tc">
    The <a href="http://en.wikipedia.org/wiki/Dragon_curve">Heighway Dragon Curve</a>, which has a surprising connection to complex numbers.
  </p>
</aside>

<!-- -->

<div class="g8 i2"><div class="pad">

<h2>Like Hands on a Clock</h2>

<p class="math">What does it mean to let numbers turn? Well, when making mathematical choices, we have to be careful. You could declare that $ 1 + 1 $ should equal $ 3 $, but that only opens up more questions. Does $ 1 + 1 + 1 $ equal $ 4 $ or $ 5 $ or $ 6 $? Can you even do meaningful arithmetic this way? If not, what good are these modified numbers? The most important thing is that our rules need to be consistent for them to work. But if all we do is swap out the <em>symbols</em> for $ 2 $ and $ 3 $, we didn't actually change anything in the underlying mathematics at all.</p>

<p>So we're looking for choices that don't interfere with what already works, but add something new. Just like the negative numbers complemented the positives, and the fractions snugly filled the space between them—and the reals somehow fit in between <em>that</em>—we need to go look for new numbers where there currently aren't any.</p>

</div></div>

<div class="wide slideshow full">

  <div class="iframe c">
    <iframe src="/files/fold-a-julia/mb-1-line.html?22e389c1" class="mathbox paged autosize" height="320"></iframe>
  </div>

  <div class="steps">
    <div class="step">
      <p>We'll start with the classic real number line, marked at the integer positions, and poke around.<br>
         We imagine the line continues to the left and right indefinitely.</p>
    </div>

    <div class="step">
      <div class="extra bottom right math" data-hold="4">
        $$ \class{blue}{2} + \class{green}{3} = \class{red}{5} $$
      </div>
      <p class="math">But there's a problem with this visualization: by picturing numbers as points, <br>it's not clear how they act upon each other.<br>
         For example, the two adjacent numbers $ \class{blue}{2} + \class{green}{3} $ sum to $ \class{red}{5} $ …
        </p>
    </div>

    <div class="step">
      <div class="extra bottom left math" data-hold="3">
        $$ \class{blue}{-2} + \class{green}{-1} = \class{red}{-3} $$
      </div>
      <p class="math">
        … but the similarly adjacent pair $ \class{blue}{-2} + \class{green}{-1} = \class{red}{-3} $.<br>We can't easily spot where the red point is going to be based on the blue and green.</p>
    </div>

    <div class="step">
      <p class="math">
        A better solution is to represent our numbers using arrows instead, or <em>vectors</em>.<br>
        Each arrow represents a number through its length, pointing right/left for positive/negative.
      </p>
    </div>

    <div class="step">
      <p class="math">
        The nice thing about arrows is that you can move them around without changing them.<br>
        To add two arrows, just lay them end to end. You can easily spot why $ \class{blue}{-2} + \class{green}{-1} = \class{red}{-3} $ …
      </p>
    </div>

    <div class="step">
      <p class="math">
        … and why $ \class{blue}{2} + \class{green}{3} = \class{red}{5} $, similarly.<br>As long as we apply positives and negatives correctly, everything still works.
      </p>
    </div>

    <div class="step">
      <div class="extra bottom math" data-hold="1">
        $$ \times \class{green}{1.5} ... $$
      </div>
      <p class="math">
        Now let's examine multiplication. We're going to start with $ \class{blue}{1} $ and then we'll multiply it by $ \class{green}{1.5} $ repeatedly.
      </p>
    </div>

    <div class="step">
      <p class="math">
        With every multiplication, the vector gets longer by 50 percent.<br>These vectors represent the numbers $ \class{red}{1}, \class{red}{1.5}, \class{red}{2.25}, \class{red}{3.375} $, $ \class{red}{5.0625} $, a nice exponential sequence.        
      </p>
    </div>

    <div class="step">
      <div class="extra bottom math" data-hold="3">
        $$ \times (\class{green}{-1.5}) ... $$
      </div>
      <p class="math">
        Now we're going to do the same, but multiplying by the negative, $ \class{green}{-1.5} $, repeatedly.
      </p>
    </div>

    <div class="step">
      <p class="math">
        The vectors still grow by 50%, but they also flip around, alternating between positive and negative.<br>These vectors represent the sequence $ \class{red}{1}, \class{red}{-1.5}, \class{red}{2.25}, \class{red}{-3.375}, \class{red}{5.0625} $.
      </p>
    </div>

    <div class="step">
      <p class="math">
        But there's another way of looking at this. What if instead of flipping from positive to negative, passing through zero, we went around instead, by rotating the vector as we're growing it?
      </p>
    </div>

    <div class="step">
      <p class="math">
        We'd get the same numbers, but we've discovered something remarkable: a way to enter and pass through the netherworld around the number line. The question is, is this mathematically sound, or plain non-sense?
      </p>
    </div>

    <div class="step">
      <div class="extra edge left" data-hold="8">$$ +180^\circ $$</div>
      <div class="extra edge right" data-hold="8">$$ 0^\circ $$</div>
      <p class="math">
        The challenge is to come up with a consistent rule for applying these rotations. We start with normal arithmetic. Multiplying by a positive didn't flip the sign, so we say we rotated by $ 0^\circ $. Multiplying by a negative flips the sign, so we rotated by $ \class{green}{180^\circ} $. The lengths are multiplied normally in both cases.
      </p>
    </div>

    <div class="step">
      <div class="extra bottom math" data-hold="1">
        $$ \times \class{green}{1.5 \angle 90^\circ} ... $$
      </div>
      <div class="extra edge top" data-hold="7">$$ +90^\circ $$</div>
      <div class="extra edge bottom" data-hold="7">$$ +270^\circ $$</div>

      <p class="math">
        Now suppose we pick one of the in-between nether-numbers, say the vector of length $ 1.5 $, at a $ 90^\circ $ angle. What does that mean? That's what we're trying to find out! We'll write that as $ \class{green}{1.5 \angle 90^\circ} $ (<em>1.5 at 90 degrees</em>). It could make sense to say that multiplying by this number should rotate by $ \class{green}{90^\circ} $ while again growing the length by 50%.
      </p>
    </div>

    <div class="step">
      <p class="math">
        This creates the spiral of points: $ \class{red}{1 \angle 0^\circ} $, $ \class{red}{1.5 \angle 90^\circ} $, $ \class{red}{2.25 \angle 180^\circ} $, $ \class{red}{3.375 \angle 270^\circ} $, $ \class{red}{5.0625 \angle 360^\circ} $. Three of those are normal numbers: $ +1 $, $ -2.25 $ and $ +5.0625 $, lying neatly on the real number line. The other two are new numbers conjured up from the void.
      </p>
    </div>

    <div class="step">

      <div class="extra edge left top" data-hold="5">$$ +135^\circ $$</div>
      <div class="extra edge right top" data-hold="5">$$ +45^\circ $$</div>
      <div class="extra edge left bottom" data-hold="5">$$ +225^\circ $$</div>
      <div class="extra edge right bottom" data-hold="5">$$ +315^\circ $$</div>

      <div class="extra bottom math">
        $$ \times \class{green}{1 \angle 45^\circ} ... $$
      </div>

      <p class="math">
        Let's examine this rotation more. We can pick $ 1 $ at a $ \class{green}{45^\circ} $ angle. Multiplying by a $ 1 $ probably shouldn't change a vector's length, which means we'd get a pure rotation effect.
      </p>
    </div>
    
    <div class="step">
      <p class="math">
        By multiplying by $ \class{green}{1 \angle 45^\circ} $, we can rotate in increments of $ 45^\circ $.<br>It takes 4 multiplications to go from $ +1 $, around the circle of ones, and back to the real number $ -1 $.
      </p>
    </div>

    <div class="step">
      <p class="math">
        And that's actually a remarkable thing, because it means our invented rule has created a square root of $ -1 $.<br>It's the number $ \class{green}{1 \angle 90^\circ} $.
      </p>
    </div>

    <div class="step">
      <div class="extra bottom math">
        $ (\class{green}{1 \angle 90^\circ})^2 = \class{blue}{-1} $
      </div>

      <p class="math">
      If we multiply it by itself, we end up at angle $ \class{green}{90} + \class{green}{90} = \class{blue}{180^\circ} $, which is $ \class{blue}{-1} $ on the real line.<br>
      </p>
    </div>
    
    <div class="step">
      <p class="math">
      But actually, the same goes for $ \class{green}{1 \angle 270^\circ} $.
      </p>
    </div>

    <div class="step">
      <div class="extra left top math">
        $ (\class{green}{1 \angle 270^\circ})^2 = \class{blue}{-1} $
      </div>

      <p class="math">
      When we multiply it by itself, we end up at angle $ \class{green}{270} + \class{green}{270} = \class{blue}{540^\circ} $. But because we went around the circle once, that's the same as rotating by $ \class{blue}{180^\circ} $. So that's also equal to $ \class{blue}{-1} $.
      </p>
    </div>

    <div class="step">
      <div class="extra edge left" data-hold="4">$$ \pm180^\circ $$</div>
      <div class="extra edge right" data-hold="4">$$ 0^\circ $$</div>
      <div class="extra edge bottom" data-hold="3">$$ -90^\circ $$</div>
      <div class="extra edge top" data-hold="3">$$ +90^\circ $$</div>

      <div class="extra edge left bottom" data-hold="4">$$ -135^\circ $$</div>
      <div class="extra edge right bottom" data-hold="4">$$ -45^\circ $$</div>
      <div class="extra edge left top" data-hold="4">$$ +135^\circ $$</div>
      <div class="extra edge right top" data-hold="4">$$ +45^\circ $$</div>

      <div class="extra top math">
        $ (\class{green}{1 \angle -90^\circ})^2 = \class{blue}{-1} $
      </div>

      <p class="math">
        Or we could think of $ +270^\circ $ as $ -90^\circ $, and rotate the other way. It works out just the same. This is quite remarkable: our rule is consistent no matter how many times we've looped around the circle.
      </p>
    </div>

    <div class="step">
      <div class="extra top left math">
        $ (\class{green}{1 \angle 90^\circ})^2 = \class{blue}{-1} $
      </div>
      <div class="extra bottom left math">
        $ (\class{green}{1 \angle 270^\circ})^2 = \class{blue}{-1} $
      </div>

      <p class="math">
        Either way, $ \class{blue}{-1} $ has two square roots, separated by $ 180^\circ $, namely $ \class{green}{1 \angle 90^\circ} $ and $ \class{green}{1 \angle 270^\circ} $.<br>This is analogous to how both $ 2 $ and $ -2 $ are square roots of $ 4 $.
      </p>
    </div>

    <div class="step">
      <div class="extra bottom">$$ \class{blue}{a} \cdot \class{green}{b} = \class{red}{c}$$</div>
      <p class="math">
        Complex multiplication can then be summarized as: <em>angles add up, lengths multiply</em>, taking care to preserve clockwise and counterwise angles. Above, we multiply two random complex numbers <span class="blue">a</span> and <span class="green">b</span> to get <span class="red">c</span>.
      </p>
    </div>

    <div class="step">
      <div class="extra bottom">$$ \class{blue}{a} \cdot \class{green}{b} = \class{red}{c}$$</div>
      <p class="math">
        When we start changing the vectors, <span class="red">c</span> turns along, being tugged by both <span class="blue">a</span> and <span class="green">b</span>'s angles. It wraps around the circle, while its length changes. Hence, complex numbers like to turn, and it's this rule that separates them from ordinary vectors.
      </p>
    </div>
    
     

    <div class="step">
      <div class="extra right"><span class="red"><big><big><big>$$ \hspace{35 pt} + $$</big></big></big></span></div>
      <div class="extra left"><span class="red"><big><big><big>$$ - \hspace{35 pt} $$</big></big></big></span></div>

      <p class="math">
        We can then picture the complex plane as a grid of concentric circles. There's a circle of ones, a circle of twos, a circle of one-and-a-halfs, etc. Each number comes in many different versions or flavors, one positive, one negative, and infinitely many others in between, at arbitrary angles on both sides of the circle.
      </p>
    </div>

    <div class="step">
      <div class="extra edge left bottom" data-hold="1">$$ \pm180^\circ $$</div>
      <div class="extra edge right bottom" data-hold="1">$$ 0^\circ $$</div>
      <div class="extra edge top" data-hold="1">$$ +90^\circ $$</div>

      <div class="extra"><big>$$ \hspace{15pt} \class{blue}{i} $$</big></div>
      <p class="math">
        Which brings us to our reluctant and elusive friend, $ \class{blue}{i} $. This is the proper name for $ \class{blue}{1 \angle 90^\circ} $, and the way complex numbers are normally introduced: $ i^2 = -1 $. The magic is that we can put a complex number anywhere a real number goes, and the math still works out, oddly enough. We get complex answers about complex inputs.
      </p>
    </div>

    <div class="step">
      <p class="math">
        Complex numbers are then usually written as the sum of their (real) X coordinate, and their (imaginary) Y coordinate, much like ordinary 2D vectors. But this is misleading: the ugly number $ \class{red}{\frac{\sqrt{3}}{2} + \frac{1}{2}i } $ is actually just $ \class{green}{1 \angle 30^\circ} $ in disguise, and it acts more like a $ 1 $ than a $ \frac{1}{2} $ or $ \frac{\sqrt{3}}{2} $. While knowing how to convert between the two is required for any real calculations, you can cheat by doing it visually.
      </p>
    </div>

    <div class="step">
      <div class="extra edge bottom">$$ \pm180^\circ $$</div>
      <div class="extra edge top">$$ 0^\circ $$</div>
      <div class="extra edge right">$$ -90^\circ $$</div>
      <div class="extra edge left">$$ +90^\circ $$</div>

      <div class="extra edge right bottom">$$ -135^\circ $$</div>
      <div class="extra edge right top">$$ -45^\circ $$</div>
      <div class="extra edge left bottom">$$ +135^\circ $$</div>
      <div class="extra edge left top">$$ +45^\circ $$</div>

      <div class="extra top edge"><br><br><br><br><big><big>$$ \class{blue}{+1} $$</big></big></div>
      <div class="extra left"><big><big>$$ \hspace{55pt}\class{green}{+i} $$</big></big></div>
      <div class="extra bottom edge"><big><big>$$ \class{blue}{-1} $$</big></big><br><br><br><br></div>
      <div class="extra right"><big><big>$$ \class{green}{-i}\hspace{55pt} $$</big></big></div>

      <p class="math">
        But looking at individual vectors only gets us so far. We study functions of real numbers by looking at a graph that shows us every output for every input. To do the same for complex numbers, we need to understand how these numbers-that-like-to-turn, this field of vectors, change as a whole.<br>
        <em>Note: from now on, I'll put $ +1 $, i.e. $ 0^\circ $ at the 12 o'clock position for simplicity.</em>
      </p>
    </div>

    <div class="step">
      <p class="math">
        When we apply a square root, each vector shifts. But really, it's the entire fabric of the complex plane that's warping. Each circle has been squeezed into a half-circle, because all the angles have been halved—the opposite of squaring, i.e. doubling the angle. The lengths have had a normal square root applied to them, compressing the grid at the edges and bulging it in the middle.
      </p>
    </div>

    <div class="step">
      <p class="math">
        But remember how every number had two opposite square roots? This comes from the circular nature of complex math. If we take a vector and rotate it $ 360 ^\circ $, we end up in the same place, and the two vectors are equal. But after dividing the angles in half, those two vectors are now separated by only $ 180 ^\circ $ and lie on opposite ends of the circle. In complex math, they can both emerge.
      </p>
    </div>

    <div class="step">
      <p class="math">
        Complex operations are then like folding or unfolding a piece of paper, only it's weird and stretchy and circular. This can be hard to grasp, but is easier to see in motion. To help see what's going on, I've cut the disc and separated the positive from the negative angles in 3D.
      </p>
    </div>

    <div class="step">
      <p class="math">
        When we square our numbers to undo the square root, the angles double, folding the plane in on itself. The lengths are also squared, restoring the grid spacing to normal.
      </p>
    </div>

    <div class="step">
      <p class="math">
        After squaring, each square root has now ended up on top of its identical twin, and we can merge everything back down to a flat plane. Everything matches up perfectly.
      </p>
    </div>
    
    <div class="step">
      <p class="math">
        Thus the square root actually looks like this. New numbers flow in from the 'far side' as we try and shear the disc apart. The complex plane is stubborn and wants to stay connected, and will fold and unfold to ensure this is always the case. This is one of its most remarkable properties. 
      </p>
    </div>

    <div class="step">
      <p class="math">
        There's no limit to this folding or unfolding. If we take every number to the fourth power, angles are multiplied by four, while lengths are taken to the fourth power. This results in 4 copies of the plane being folded into one.
      </p>
    </div>

    <div class="step">
      <p class="math">
        However, things are not always so neat. What happens if we were to take everything to an irrational power, say $ \frac{1}{\sqrt{2}} $? Angles get multiplied by $ 0.707106... $, which means a rotation of $ 360^\circ $ now becomes $ \sim 254.56^\circ $.
      </p>
    </div>

    <div class="step">
      <p class="math">
        Because no multiple of $ 360 $ is divisible by $ \frac{1}{\sqrt{2}} $, the circular grid never matches up with itself again no matter how far we extend it. Hence, this operation splits a single unique complex number into an infinite amount of distinct copies.
      </p>
    </div>

    <div class="step">
      <p class="math">
        For any irrational power $ p $, there are an infinite number of solutions to $ z^p = c $, all lying on a circle. For a hint as to why this is so, we can look at Taylor series: an arbitrary function $ f(z) $ can be written as an infinite sum $ a + bz + cz^2 + dz^3 + ... \,$ When z is complex, such a sum doesn't just represent a finite amount of folds, but a mindboggling infinite origami of complex space.
      </p>
    </div>
  </div>

  <div class="edge-bottom c"><div class="edge-left"></div><div class="edge-right"></div></div>
</div>

<div class="c"></div>


<div class="g8 i2 m1"><div class="pad">

<p>We've seen how complex numbers are arrows that like to turn, which can be made to behave like numbers: we can add and multiply them, because we can come up with a consistent rule for doing so. We've also seen what powers of complex numbers look like: we fold or unfold the entire plane by multiplying or dividing angles, while simultaneously applying a power to the lengths.</p>

</div></div>

<!-- -->

<div class="g8 i2"><div class="pad">

<h2>Pulling a Dragon out of a Hat</h2>

<p>With a basic grasp of what complex numbers are and how they move, we can start making Julia fractals.</p>

<p>At their heart lies the following function:</p>

<p class="math tc">$$ f(z) = z^2 + c $$</p>

<p>This says: map the complex number $ z $ onto its square, and then add a constant number to it. To generate a Julia fractal, we have to apply this formula repeatedly, feeding the result back into $ f $ every time.</p>

<p class="math tc">$$ z_{n+1} = (z_n)^2 + c $$</p>

<p>We want to examine how $ z_n $ changes when we plug in different starting values for $ z_1 $ and iterate $ n $ times. So let's try that and see what happens.</p>

</div></div>

<div class="wide slideshow full">

  <div class="iframe c">
    <iframe src="/files/fold-a-julia/mb-2-julia.html?22e389c1" class="mathbox paged autosize" height="320"></iframe>
  </div>

  <div class="steps">
    <div class="step">
      <p class="math">Our region of interest is the disc of complex numbers less than $ 2 $ in length. I've marked the circle of ones as a reference.</p>
    </div>

    <div class="step">
      <p class="math">We take an arbitrary set of numbers, like this grid, and start applying the formula $ f(z) = z^2 + c $ to each. Rather than use vectors, I'll just draw points, to avoid cluttering the diagram.</p>
    </div>

    <div class="step">
      <p class="math">First we square each number. That is, their lengths are squared, their angles are doubled.<br>The squaring has a dual effect: numbers larger than $ 1 $ grow bigger and are pushed outwards, numbers less than $ 1 $ grow smaller and are pulled inwards.</p>
    </div>

    <div class="step">
      <p class="math">Next, we reset the grid back to neutral, keeping the numbers in their new place.<br>We also pick a random value for the constant $ \class{green}{c} $, e.g. $ \class{green}{0.57 \angle -59^\circ} $.</p>
    </div>

    <div class="step">
      <p class="math">Now we add $ \class{green}{c} $ to each point, completing one round of Julia iteration, $ f(z) = z^2 + c $. As a result, some numbers have ended up closer towards the origin (i.e. $ 0 $), others further away from it. The combination of folding + shifting has had a non-obvious effect on the numbers.</p>
    </div>

    <div class="step">
      <p class="math">We begin the second iteration and square each number again. Any number not inside the critical circle of $ 1 $ in the middle will get pushed out again. The other numbers continue to linger in the middle.</p>
    </div>

    <div class="step">
      <p class="math">If we zoom out, we can see the larger numbers are spiralling outwards and are permanently lost. The minor nudge by $ \class{green}{c} $ won't be enough to bring them back.</p>
    </div>

    <div class="step">
      <p class="math">Others remain in the middle, being drawn in, but are also at risk of being pushed out of the circle by $ \class{green}{c} $.</p>
    </div>

    <div class="step">
      <p class="math">Resetting the grid again, we add the same value $ \class{green}{c} $ to our vectors again to finish. At this point, our original grid of numbers has been completely jumbled up.
      </p>
    </div>
    
    <div class="step">
      <p class="math">
        If we continued this process would any numbers remain in the middle? Or would they eventually all get flung out? Unfortunately it's very hard to see what's going on while iterating forwards, because we lose track of where each point came from.</p>
    </div>

    <div class="step">
      <p class="math">So we're going to go backwards instead. We'll establish a safe-zone of all numbers less than $ 2 $, forming a solid disc of all those which aren't irretrievably lost. We want to know where all these numbers can possibly come from. To help track these points, I've coloured one area in a different shade.</p>
    </div>

    <div class="step">
      <p class="math">First we have to shift the numbers again, this time in the opposite direction to subtract $ c $.</p>
    </div>

    <div class="step">
      <p class="math">Now we apply the square root to find $ z_{n-1} = \pm \sqrt{z_n - c} $, which is a Julia iteration in reverse.</p>
    </div>

    <div class="step">
      <p class="math">After one backwards iteration, the disc has been squished down into an oval at an angle.<br>These are all the points that will definitely stay in the middle after one iteration.</p>
    </div>

    <div class="step">
      <p class="math">When we apply the second iteration, a pattern starts to develop. Because of the repeated unfolding, we create two bulges wherever there was previously only one.</p>
    </div>

    <div class="step">
      <p class="math">At the same time, the square root alters the length of each number as well. As a result, we squeeze in the radial direction, scaling down earlier features as they combine with newly created ones.</p>
    </div>

    <div class="step">
      <p class="math">After 4 iterations, we start to see the first hints of self-similarity. The shape's lobes are sprouting into spirals.</p>
    </div>
    
    <div class="step">
      <p class="math">But all we've really done is narrow down our blue safe-zone to include only those points that 'survive' up to 5 Julia iterations.</p>
    </div>

    <div class="step">
      <p>
        Remarkably this seems to distort the fractal evenly: our highlighted circles don't stretch into ovals. This is not a coincidence. Complex operations are indeed stubborn, in that they all preserve right angles everywhere. To do so, the mapping must act like a pure scaling and rotation at every point, without shearing off in any particular direction. This is what allows the fractal to look like itself at different scales.
      </p>
    </div>

    <div class="step">
      <p class="math">
        Skipping ahead to iteration 12, we've definitely abandoned the realm of neat, traditional geometry.<br>
        Despite curving wildly, the total mapping $ z_{12} $ still has this property of evenness, which is properly referred to as a <em>conformal</em> mapping.</p>
    </div>

    <div class="step">
      <p class="math">After 128 iterations, we end up with this intricate dragon-like shape, approximating the safe zone for the true fractal map $ z_\infty $. The numbers that make up the blue area are the hardiest points that will survive the next 128 attempts on their life. All the others will definitely get flung out.
      </p>
    </div>

    <div class="step">
      <p class="math">Yet this complicated shape is merely the result of folding over and over again, adding a simple constant in between. If we perform a forwards Julia iteration, i.e. squaring and shifting, we see this shape matches up with itself, and looks identical before and after.
      </p>
    </div>

    <div class="step">
      <p class="math">For different values of $ \class{green}{c} $, the fractal morphs into other shapes. There's literally an infinite variety to discover. Some sets are made up of disconnected parts. In this case, $ |c| $ is large enough to push the solid disc away from the center in a single iteration, but not so far that some points can't fold back in. If $ |c| $ gets much larger, the set vanishes.
      </p>
    </div>

    <div class="step">
      <p class="math">For a smaller $ c $, Julia sets are solid. Even a small shift in the value of $ c $ can accumulate into a large difference. Here we zone in on some fluffy clouds right outside the 'solid zone'. Oddly enough, it seems when $ c $ is not inside of its own Julia set, the set is not solid. Note that in this case, 128 iterations is not sufficient: large solid patches remain, which would be divided further with more iterations.
      </p>
    </div>

    <div class="step">
      <p class="math">This area of fractal space is dubbed Seahorse Valley, for rather obvious reasons.
      </p>
    </div>

    <div class="step">
      <p class="math">Nearby, we find these jewel-like spirals.
      </p>
    </div>

    <div class="step">
      <p class="math">Buried deep inside, there are remarkable combinations of shapes, like this pearl necklace covered in something resembling palm trees.
      </p>
    </div>

    <div class="step">
      <p class="math">And we can even make snowflakes. The dramatic changes due to $ c $ reveal the chaotic nature of fractals. Mathematically, chaos occurs when even the tiniest change can accumulate and blow up to an arbitrarily large effect.
      </p>
    </div>

    <div class="step">
      <p class="math">If we change our iteration formula, for example to a fourth power $ f(z) = z^4 + c $, the entire shape changes. Because each iteration now turns one bulge into four, the resulting shape has four-fold rotational symmetry.
      </p>
    </div>

    <div class="step">
      <p class="math">Again, different values of $ \class{green}{c} $ make different shapes, precipitating dramatic changes.
      </p>
    </div>

    <div class="step">
      <p class="math">To understand the effect of $ c $ we need to make a Mandelbrot set. This is similar to a Julia set, but the formula is applied differently. We'll use $ z^2 + c $ again. Instead of different starting values $ z_1 $, we choose different values of $ c $ and start with $ z_1 = 0 $ every time. Because $ c $ is no longer constant, the mapping stops being a simple folding operation. Each iteration is now unique and not so easy to visualize.
        </p>
    </div>
    
    <div class="step">
        <p class="math">
        Because the Mandelbrot set traverses all possible values of $ c $ across its surface, it has a part of every associated Julia set in it. Around any number $ \class{green}{c} $ it looks like the Julia set which has that value as its constant. Here, we move towards the three-way cross at the bottom of the Mandelbrot set. The Julia set develops similar features.
      </p>
    </div>

    <div class="step">
        <p class="math">
        Where the Mandelbrot set is round and bulbous, the Julia set is too.
      </p>
    </div>

    <div class="step">
        <p class="math">
        The spirals and seahorses from earlier are located here. You can literally see the shapes on both sides of the valley evolving towards horseheads and spirals respectively. But the Mandelbrot set acts like a map to Julia sets in a much more direct way: anywhere the Mandelbrot set is filled in (blue), the corresponding Julia set is solid too. The white areas are values of $ c $ which create disconnected Julia sets.
      </p>
    </div>

    <div class="step">
        <p class="math">
        That the Mandelbrot set is a 'pixel-perfect' map of Julia sets is a big clue. It reflects that they're actually both slices of a single higher dimensional object. By viewing these slices as we travel through, we can get a vague idea of its shape and complexity. In this object, every point in the Mandelbrot set is connected to the center of the corresponding Julia set. Actually picturing this 4D object is a challenge.</p>
    </div>

    <div class="step">
      <p class="math">
        But like any fractal, the Mandelbrot set also contains copies of itself, buried inside its edge. This is just one of the many varied copies. As a result, deep Mandelbrot zooms can reach astonishing levels of beauty in complexity. This is best done with specialized software that can calculate with hundreds of digits of precision.
      </p>
    </div>

  </div>

  <div class="edge-bottom c"><div class="edge-left"></div><div class="edge-right"></div></div>
</div>

<div class="c"></div>

<div class="g8 i2 m1"><div class="pad">

<p>Making fractals is probably the least useful application of complex math, but it's an undeniably fascinating one. It also reveals the unique properties of complex operations, like conformal mapping, which provide a certain rigidity to the result.</p>

<p>However, in order to make complex math practical, we have to figure out how to tie it back to the real world.</p>

<h2>Travelling without Moving</h2>

<p>It's a good thing we don't have to look far to do so. Whenever we're describing wavelike phenomena, whether it's sound, electricity or subatomic particles, we're also interested in how the wave evolves and changes. Complex operations are eminently suited for this, because they naturally take place on circles. Numbers that oppose can cancel out, numbers in the same direction will amplify each other, just like two waves do when they meet. And by folding or unfolding, we can alter the frequency of a pattern, doubling it, halving it, or anything in between.</p>

<p>More complicated operations are used for example to model electromagnetic waves, whether they are FM radio, wifi packets or ADSL streams. This requires precise control of the frequencies you're generating and receiving. Doing it without complex numbers, well, it just sucks. So why use boring real numbers, when complex numbers can do the work for you?</p>

</div></div>

<div class="wide slideshow full">

  <div class="iframe c">
    <iframe src="/files/fold-a-julia/mb-3-waves.html?22e389c1" class="mathbox paged autosize" height="320"></iframe>
  </div>

  <div class="steps">
    <div class="step">
      <div class="extra right bottom">$$ w(x) = \sin(x) $$</div>

      <p class="math">Take for example a sine wave $ w(x) $.</p>
    </div>

    <div class="step">
      <div class="extra right bottom">
        $$
          w(x, t) = \sin(x - t) $$
        $$  \class{blue}{\frac{\partial w(x, t)}{\partial t}}
        $$
      </div>

      <p class="math">For the wave to propagate across a distance, its values have to ripple up and down over time.<br>The <span class="blue">rate of change</span> over <em>time</em> is drawn on top. This is the vertical velocity at every point. Both the wave and its rates of change undergo a complicated numerical dance.</p>
    </div>

    <div class="step">
      <div class="extra right bottom">
        $$
          w(x, t) = \sin(x - t) $$
        $$  \class{blue}{\frac{\partial w(x, t)}{\partial t}} \,\, \class{green}{\frac{\partial^2 w(x, t)}{\partial t^2}}
        $$
      </div>

      <p class="math">But to properly describe this motion, we have to go one level deeper. We have to examine the <span class="green">rate of change</span> of the <span class="blue">vertical velocity</span> of the wave. This is its <span class="green">vertical acceleration</span>. We see that green vectors tug on blue vectors as blue vectors tug on the wave.</p>
    </div>

    <div class="step">
      <div class="extra right bottom">
        $$
          w(x, t) = \sin(x - t) $$
        $$  \class{green}{\frac{\partial^2 w(x, t)}{\partial t^2}} = \,?
        $$
      </div>

      <p class="math">It's easier to see what's going on if we center the vectors vertically. The <span class="green">acceleration</span> appears to be equal but opposite to the wave itself.</p>
    </div>

    <div class="step">
      <div class="extra right bottom" data-hold="1">
        $$
          w(x, t) = \sin(x - t) + 1 $$
        $$ \class{green}{\frac{\partial^2 w(x, t)}{\partial t^2}} = \,?
        $$
      </div>

      <p class="math">But that's just a lucky coincidence. If we shift the wave up by one unit, its opposite shifts down by a unit. Yet its velocity and acceleration are unaltered. So acceleration is not simply the opposite of the wave.</p>
    </div>

    <div class="step">

      <p class="math">What's actually going on is that the green vectors match the <span class="red">curvature</span> of the wave, positive inside valleys, negative on top of crests. Intuitively, this can be explained by saying that waves tend to bounce towards an average level: this is going to pull the value up out of valleys and down from peaks.</p>
    </div>
    
    <div class="step">
      <div class="extra right bottom">
        $$
          w(x, t) = \sin(x - t) + 1 $$
        $$  \class{green}{\frac{\partial^2 w(x, t)}{\partial t^2}} = \class{red}{\frac{\partial^2 w(x, t)}{\partial x^2}}
        $$
      </div>

      <p class="math">
      But curvature is the rate of change of the <em>slope</em>, and slope is the rate of change over a <em>distance</em>. So to describe real waves, we need to relate 'second level' <span class="green">change over time</span> and <span class="red">change over distance</span>, each deriving twice. This is Complicated with a capital C.</p>
    </div>

    <div class="step">
      <p class="math">Let's try this with complex numbers instead. Until now, we had a 2D graph, showing the real value of the wave over real distance. We're going to make the wave's value complex. Mapping a 1D number (distance) to a 2D number (the wave function), means we need a 3D diagram.</p>
    </div>

    <div class="step">
      <p class="math">The complex plane is mapped into the old Y direction (real) and the new Z direction (imaginary).</p>
    </div>

    <div class="step">
      <div class="extra bottom edge">
        $$ w(x) = (1 \angle x) $$
      </div>

      <p class="math">To make a complex wave, we do the thing complex numbers are best at: we make them turn, and make a helix. In this case, our wave function is simply the variable number $ 1 \angle x $ , a constant length with a smoothly changing rotation over distance.</p>
    </div>

    <div class="step">
      <div class="extra bottom edge" data-hold="1">
        $$ w(x, t) = (1 \angle x) \cdot (1 \angle t) = 1 \angle (x + t) $$
      </div>
      <div class="extra right bottom">
        $$  \class{blue}{\frac{\partial w(x, t)}{\partial t}} = \,? $$
      </div>

      <p class="math">To make the wave move, we can simply twist it in-place. Which we now know is the same as multiplying by an increasing angle $ 1 \angle t $. If we plot the complex velocity of each point, at first sight this might not look any simpler than the real wave. But in fact, these vectors are not changing in length at all, unlike the real version. As the wave is pulled by the velocity vectors, both undergo a pure rotation.</p>
    </div>

    <div class="step">
      <div class="extra right bottom">
        $$  \class{blue}{\frac{\partial w(x, t)}{\partial t}} = i \cdot w(x, t)
        $$
      </div>

      <p class="math">At all times, the velocity is offset by $ 90^\circ $ from the wave itself. And that means that described in complex numbers, wave equations are super easy. Instead of involving <em>two derivatives</em>, i.e. the <em>rate of rate of change</em>, we only need one. There is a direct relationship between a value and its <span class="blue">rate of change</span>. The necessary rotation by $ 90^\circ $ can then be written simply as multiplying by $ i $.</p>
    </div>

    <div class="step">

      <p class="math">To recover a real wave from a complex wave, we can simply flatten it back to 2D, discarding the imaginary part. By using complex numbers to describe waves, we give them the power to rotate in place without changing their amplitude, which turns out to be much simpler.</p>
    </div>

    <div class="step">

      <div class="extra bottom edge">
        $$ \frac{1}{2} (\class{blue}{ 1 \angle (x + t) } + \class{green}{ 1 \angle -(x + t) }) = \cos(x + t) $$
      </div>

      <p class="math">In fact, flattening the wave has a perfectly reasonable complex interpretation: it's what happens when we average out a <span class="blue">counter-clockwise wave</span> (positive frequency) with a <span class="green">clockwise wave</span> (negative frequency). By twisting each in opposite directions, the combined wave travels along, locked to the real number line.</p>
    </div>

    <div class="step">

      <div class="extra bottom edge" data-hold="1">
        $$ \frac{1}{2} (\class{blue}{ 1 \angle (x + t) } + \class{green}{ 1 \angle -(\frac{3}{2}x + t) }) = \,? $$
      </div>

      <p class="math">But if we add up two arbitrary complex frequencies, their sum immediately turns into a spirograph pattern that manages to evolve and propagate, even as it just rotates in place. Though the original waves both had a constant amplitude of $ 1 $, the relative differences in angles (i.e. the <em>phase</em>) allows them to cancel out in surprising ways.</p>
    </div>
    
    <div class="step">
      <p class="math"><span class="blue">Neither</span> <span class="green">curve</span> is actually moving forward: they're just spinning in place, creating motion anyway. This is actually what quantum superposition looks like, where two or more complex probability waves combine and interfere. Where the result cancels out to zero, that's where two separate possible states are cancelling out each other, creating <em>interference</em>. That the underlying numbers are complex doesn't prevent them from describing real physics, indeed, it seems that's how nature actually works.</p>
    </div>

    <div class="step">
      <p class="math">This serene display hides a whirlwind of phase. We can plot the velocity of the <span class="blue">two</span> <span class="green">frequencies</span>, and their combination, scaled down for clarity. Once again you can see the power of describing waves with complex numbers, letting you split up a complicated motion into simple, repetitive rotations… into <em>numbers that like to turn</em>.</p>
    </div>

  </div>

  <div class="edge-bottom c"><div class="edge-left"></div><div class="edge-right"></div></div>
</div>

<div class="c"></div>

<div class="g8 i2 m1"><div class="pad">

<h2>The End Is Just The Beginning</h2>

<p class="math">In visualizing complex waves, we've seen functions that map real numbers to complex numbers, and back again. These can be graphed easily in 3D diagrams, from $ \mathbb{R} $ to $ \mathbb{C} $ or vice-versa. You cross 1 real dimension with the 2 dimensions of the complex plane.</p>

<p class="math">But complex operations in general work from $ \mathbb{C} $ to $ \mathbb{C} $. To view these, unfortunately you need four-dimensional eyes, which nature has yet to provide. There are ways to project these graphs down to 3D that still somewhat make sense, but it never stops being a challenge to interpret them.</p>

<p>For every mathematical concept that we have a built-in intuition for, there are countless more we can't picture easily. That's the curse of mathematics, yet at the same time, also its charm.</p>

<p class="math">Hence, I tried to stick to the stuff that is (somewhat!) easy to picture. If there's interest, a future post could cover topics like: the nature of $ e^{ix} $, Fourier transforms, some actual quantum mechanics, etc.</p>

<p>For now, this story is over. I hope I managed to spark some light bulbs here and there, and that you enjoyed reading it as much as I did making it.</p>

<p>Comments, feedback and corrections are welcome on <a href="https://plus.google.com/112457107445031703644/posts/VGzZsTWnCHG">Google Plus</a>. Diagrams powered by <a href="/blog/making-mathbox/">MathBox</a>.</p>

<p><em>More like this: <a href="/blog/to-infinity-and-beyond/">To Infinity… And Beyond!</a>.</em></p>

<p><em>For extra credit: check out these great <a href="http://code.google.com/p/mandelstir/">stirring visualizations</a> of Julia and Mandelbrot sets. I incorporated a similar graphic above. Hat tip to Tim Hutton for pointing these out. And for some actual paper mathematical origami, check out Vihart's latest video on <a href="http://www.youtube.com/watch?v=8EmhGOQ-DNQ">Snowflakes, Starflakes and Swirlflakes</a>.</em></p>

</div></div>

]]></content>
  </entry>
  
</feed>