- Grab a computer (at least the iOS app doesn't seem to expose these settings)
- Log on to betterment.com
- Go to the Portfolio → Betterment Portfolios page:
- For each of your portfolios, walk them through these steps to change your allocation to the Socially Responsible Investing strategy:
- Click the Edit Portfolio link:
- Click the Betterment SRI → "Review Strategy" button, which will walk you through next steps to eventually change to that investment strategy. (If there isn't a blue button next to it, but is for each of the other strategies available, you're already done – good work!)
- Scroll through the page and click "Review and refine" at the bottom:
- Same thing, and click "Continue":
- Clicking "Finish Setup" changes this portfolio's allocation:
- This confirms the money in your portfolio no longer helps killing people – keep going until you run out of portfolios:
Webby thoughts, most about around interesting applications of ecmascript in relation to other open web standards. I live in Mountain View, California, and spend some of my spare time co-maintaining Greasemonkey together with Anthony Lieuallen.
2018-03-26
Betterment betterment
2018-01-17
Is DevTools open?
If you follow the web world, you have hopefully read this recent post on how we need to protect people against attacks on user data. On a mostly unrelated note, I wrote some assertions that I wanted to throw errors when I'm not debugging things, but console.log
them when I am, and then trip a debugger
statement. It's not really api detectable what mode I am in, but the openness of browser DevTools is a fair proxy, for me, at least, and the examples it linked to gave me a shim for crafting myself the window.isDevToolsOpen
setter bookmarklet I wanted:
javascript:((i) => {
Object.defineProperty(i, 'id', {
get: () => { window.isDevToolsOpen = true; }
});
setInterval(() => {
window.isDevToolsOpen = false;
console.debug(i);
}, 1000);
})(new Image)
Maybe it'll come in handy for someone else. These things rarely work forever; this post was written in the days of Chrome 63.0.3239.84 and Safari 11.0.2 (13604.4.7.1.3), when it did, whereas it already doesn't in Chrome Canary 65.0.3322.0, for instance. It gave me a useful time window of really convenient debugging a piece of complex code, which might require a full-blown browser extension at a later time. (Feel encouraged to comment if you made one.)
2017-01-20
Tesla
I changed my game plan for getting my first car when the year drew to a close, and decided to join the last batch getting the free supercharging for life (-time of the car) deal, rather than 4000 kWh/year (~1,000 long distance miles), as it's evolved to for later customers. My initial plan was to postpone getting one until I had my green card – but that will probably keep dragging out a few years.
If you, too, are signing up for a Tesla (model S or X) before March 15, 2017, do it via a referral code (mine, ts.la/johan22, for instance) for a $1,000/€1,000 rebate. I grabbed mine from an acquaintance, and am paying it forward similarly. If it's later than that date, by the time you read this, search around a bit with google or on facebook; you'll probably find one.
2016-12-15
Control Freak
After Google Chrome discontinued native user script support in the shipping default build, I ran TamperMonkey for a while, a pretty full featured extension similar to Firefox's Greasemonkey for my user scripting.
And then at some point, I stumbled upon Control Freak, a minimalist solution for quick hacks and tweaks with no support for sharing those hacks, but swift access to making a script for the current page, domain, or the whole web.
And despite being really fond of sharing the tools I build, I liked it, and have stuck with it since. But I will at least make one little hack available for it for making it easy to do on.js style scraping from chrome devtools by way of a Control Freak user script.
For now, I resort to digging up the link to the current .user.js version from https://git.io/on.js and pasting that into the "lib" field, and then write myself a little scraper definition – here is one for https://tumblr.com/dashboard that just parses out basic metadata from the posts fetched in the initial pageload:
scrape( [ 'css* .post_info_link' , { node: 'xpath .' , data: 'xpath string(@data-tumblelog-popover)' , link: 'xpath string(@href)' , author: 'xpath string(.)' } ]);
From then on, each time I go to that page, my chrome console will expose the on
function on the page, the scraper
definition above, and the array of scraped
items (per the css*
contextual selector, digging up an array of 0 or more parsed .post_info_link
elements from the page, digging out the four sub-bits for each).
If I want to actually do something useful with them in the script, that's easy sleight of hand, too, but oftentimes, I just want programmatic access to parts of the page. I can be bothered to write up a scraper definition once, if it's easy to save it and reap the benefits of it on later visits, and this lets me do just that, and evolve it over time, with a minimum of fuss.
If the syntax looks foreign to you (it probably does), it's a json literal coded version of an unholy mix of regexp, css, and xpath syntax idioms, for scraping a templated web page into a javascript data structure you devise the format of yourself, a later evolution of ideas I posted to greasemonkey-dev in 2007 / 2012.
You can think of it as a more powerful version of document.querySelector
, document.querySelectorAll
, and document.evaluate
, and lots of boring iteration code to create the objects/arrays you seek, rolled into one, with a unified/bridged syntax.
Some base-bones examples:
on.dom('css? body') = document.querySelector('body') = document.body on.dom('css* img') = document.querySelectorAll('img') ~ document.images (but as a proper Array) // or, equivalent, but with xpath selectors: on.dom('xpath? //body') on.dom('xpath* //img')
The ? and * qualifiers borrow regexp semantics – ? either you finds one node or produces a null, * always produces an Array of 0 or more elements, whether the expression matched or not. For cases where you don't want the expression to yield anything unless you had one or more matches, similarly nothing and + force a match for the selector:
on.dom('css h1') = document.querySelector('h1') on.dom('css+ img') = document.querySelectorAll('img') ~ document.images (but as a proper Array) // or, equivalent, but with xpath selectors: on.dom('xpath //h1') on.dom('xpath+ //img') = devtools' $x('//img')To create an object with some named things in it, just declare the shape you want:
on.dom( { body: 'css body') , images: 'css+ img') });Let's say you want all links in the page with an image in them:
on.dom('xpath+ //a[@href][count(.//img) = 1]')But instead of the
img
elements, we want their src
attribute, and the href
attribute of the link. This is where context selectors come into play (an array literal with the context selector first, followed by the object structure to create for each item):
on.dom( [ 'xpath+ //a[@href][count(.//img) = 1]' , { href: 'xpath string(@href)' , src: 'xpath string(.//img/@src)' } ])For a page with three such elements, this might yield you something like:
[ { href: '/', src: '/homepage.png'} , { href: '/?page=1', src: '/prev.png'} , { href: '/?page=3', src: '/next.png'} ]The context selector lets us drill into the page, and decode properties relative to each node, with much simpler selectors local to each instance in the page. If we want the
a
and img
elements in our data structure too, that's an easy addition:
on.dom( [ 'xpath+ //a[@href][count(.//img) = 1]' , { href: 'xpath string(@href)' , src: 'xpath string(.//img/@src)' , img: 'xpath .//img' , a: 'xpath .' } ])
All leaves in the structure ('<selectorType><arity> <selector>'
) you want to drill out sub-properties for, can be similarly replaced by a ['contextSelector', {subPropertiesSpec}]
this way, which makes decomposing deeply nested templated pages comfy.
Put another way: this is for web pages what regexps with named match groups are to strings of text, but with tree structured output, as web pages are tree structured instead of one-dimensional. And I think it makes user scripting a whole lot more fun.
2016-11-22
Unchoking
const wget = async(url) => { try { const res = await fetch(url), html = await res.text(); return (new DOMParser).parseFromString(html, 'text/html'); } catch (e) { console.error(`Failed to parse ${url} as HTML`, e); } }; wget('/').then(doc => alert(doc.title));This already works in a current Google Chrome Canary (57). Sadly no javascript console support for
doc = await wget('/')
; you still have to use the "raw" promise API directly for interactive code, rather than syntax sugared blocking behaviour – but it's still a lot nicer than things used to be. And you can of course assign globals and echo when it's done:
wget('/').then(d => console.log(window.doc = d)); doc.title;Querying a DOM with an XPath selector and optional context node is still as ugly as it always was (somehow only the first making it to the Chrome js console):
const $x = (xpath, root) => { const doc = root ? root.evaluate ? root : root.ownerDocument : document; const got = doc.evaluate(xpath, root||doc, null, 0, null); switch (got.resultType) { case got.STRING_TYPE: return got.stringValue; case got.NUMBER_TYPE: return got.numberValue; case got.BOOLEAN_TYPE: return got.booleanValue; default: let res = [], next; while ((next = got.iterateNext())) { res.push(next); } return res; } }; const $X = (xpath, root) => Array.prototype.concat($x(xpath, root))[0];...but for the corresponding css selector utilities (
$$
and $
respectively), we can now say document.querySelectorAll()
and document.querySelector()
, respectively. Nice-to-haves. Like the lexically bound arrow functions. I guess web page crafters overall rarely if ever use XPath, and that it is an XML vestige we should be really happy that we have at all, through happy accidents of history, even though it needs a bit of shimming love to become lovable.
2015-10-27
I recently left Groupon, and I am as surprised as people who know me about where I am heading next. :-)
Starting Monday, I'll be doing Facebook bootcamp.
Interviewing for various companies was interesting. My litmus test proved pretty useful, and was a great answer to the standard question about what kind of impact I'd like to have, which I otherwise rarely have anything in particular to say about. This time, I could assess cultural fit, apples to apples, as everybody and their grandmother seem to have this issue.
At Google, I got the impression each time I brought it up with an interviewer, that this was Somebody Else's Problem, and likely hard to do anything about without ending up on exactly the right team, or working against the grain of bureaucracy.
At Facebook, everybody I talked to got really interested, lit up when they got that localStorage
is a handy cross-window message bus, not just a storage mechanism, and the interviewer working closest to teams poking at that kind of stuff started scribbling profusely in his notebook. A little later, after describing new hire procedures, he mentioned that I probably could end up getting to fix that myself during bootcamp.
Sold! :-)
2015-09-23
Notifications on the Web
Hey, web developers!
Have you ever clicked that GMail, Google+, Github, Facebook, or other web site icon or browser tab title that says you have some unread notification waiting for you, only to find that nope, you don't? Because you already saw it, in another open browser tab?
Yes, you have. You, and millions of other people that suffer this every day. In 2015. There is a very simple cure, shipping since 2009. Yes, even IE8 has it. Try opening this example page demonstrating it in two or more browser tabs or windows, and click the buttons in it, and observe how they all update together, instantly. For convenience, the example describes how to do it, too.
It's easy, and supported everywhere. A localStorage event listener is all you need.
Now please sneak this into your next site update, if you love your users. Notifications that lie to us make us all sad.
2015-01-02
To 2015
"Im starting this new year off right. One of my goals is to start every day this way. What are you're resolutions?"
I think the above share, from Lindsey Stirling (a capable violinist and dancer, more or less synonymous with the genre "dub-step violin") is a fairly good example of living the human condition today.
2014 is over and lots of people are thinking about their lives, how their year was, and what changes they want to make in 2015. I think this one might mean something like "spend more time in or with the community my roots trace back to" or "sharing my privilege with others less fortunate". Along similar lines, many have been conditioned to think about and commit to "losing weight", and are looking at the zillion offers reaching back, that monetize on that aspiration.
It is much easier to look at something specific that craves to be at the top of your attention (a religion, clawing for ownership of your world view, an i-Device, glowing at you the moment you turn it on, locking in your attention at a small rectangle of light, or the mass and shape of your body vs an edited "ideal" reality broadcast at us from every magazine cover, ad and media outlet everywhere), and follow the mechanism of the institutions of power docked to and evolved around each topic.
Lindsey does. I do. You do. Not in all the same ways, and for the same reasons, but the human aspect of it binds us, and our environment tugs us into belief pockets often heavily insulated against reality, growing into better understandings of our ways, expectations, or succeeding at what we are trying to achieve. Usually not with any malicious intent – much or most of these just evolved into what form they take today, without any top-down understanding of the topic or surrounding setting, least of all corrected for present understanding of the world, the human metabolism, or cultural ideas about food, eating, snacks, and beverages.
From my own understanding of the world, I spot the heavy shadow of ties to outmoded beliefs and world views in this picture, as well as other deeper qualities on human to human compassion that, to this day, are as valid as they were 2000 years ago, and as heavily scoped to put all attention on your choices as an individual, and not to fix the system which benefits the current reign of actual power, a surrounding, heavily self-reinforcing deception of humanity.
Our addiction with glowing rectangles is a similar dead end, Bret Victor convincingly demonstrates, in his talk "The Humane Representation of Thought", we can easily keep optimizing those rectangles, however deep we want the rabbit hole to eventually go, but we have a thousandfold greater sensory range as a species that is largely untapped, in how we live, work, think, and communicate, and the more of our attention we put on our rectangles, the less of it we put on bridging the gap.
As for weight, chasing a very easily quantifiable number and trying on various randomly selected, well-funded choices for altering it is much easier than learning to strive for health, about nutrition, foods the human digestive system and its culture of bacteria and otherwise have evolved and been adapted for together, and eating and life patterns that line up with good health, and that have little to nothing to do with the real-time feedback of all addictive cues our outdated rewards system has been conditioned to to work well on a savannah we no longer roam on.
Keeping a good working world and self model, is a chore in 2015, as we are all islands in a vast sea of less well calibred and scrutinized working ones, as evolution in idea space isn't served by the dying of ideas, the way evolution on the individual level is (or in evolutional times was) favoured by the dying of the individual, or sometimes species, for over-population type collapse problems. A bad idea coupled to a large virality and power/money dynamic is immortal – no matter how poor it is, and no fighting of the idea can kill it. We also lack the provisions as a society to fight its causes, and its survivability at the funding or power level – vested interests generally have the power to self sustain and insulate against this kind of change.
When people think about creating alternate ways of governace, even worse ideas than what we are living today tend to surface, such as Rand:ian contingents that cling to seemingly self-serving anality that doesn't get added value from breadth and community. There largely isn't much teaching our culture how to build strong systems out of a breadth of different-shape pieces, perspectives, cultures and people. We have conceptual tools like the scientific model which helps narrow down idea space into better and better models of how stuff works, but no translation function to make the world adopt that understanding into practices that serve humanity.
Thus consequences of even very simple innovations in technology like the invention of the condom, have hardly even touched how human sexuality and relations work, both of which, more or less, are as feudal today as they were a thousand years ago. Ownership of sorts is wired very deeper in mammals, and experiments with different foundational beliefs, while heard of, such as flavours of polyamory, are not wide-spread. All sorts of cultural and behavioral echoes from long honed traditions work to keep these islands apart.
Exposing world models to scrutiny may be one of the more important traits and skills to living at choice and in knowledge community these days. Both for spreading good ideas, and for gathering community of people who would rather live in an evolving belief community, than a vested one. To you, and to 2015!
2014-08-23
Byword
I have long want a beautiful, full-screen essay writing program on osx, but somehow never got around to digiging it up. I know Writeroom exists and even bought it for iOS at some point, but I never ended up using it. Today I looked around again – and as I have been using a lot more Markdown at work, I decided I'd like some catering for that too.
It turns out Byword fits the description snugly. It comes with some minimal preferences, a dark and a light theme, no bloat, and when your needs are more specific than the preferences afford, you can still escape-hatch through defaults write
to get things like a line width of exactly 80 characters, monospace by setting your font to Menlo 18pt and issuing the command:
defaults write com.metaclassy.byword BywordTextWidthCurrent 800
in a shell and restarting it. If, like me, you find you are happier with a solid than blinking caret, there's are Stack Exchange solutions for that too, for various versions of osx; here's the one that sufficed for my current 10.8.5 machine, setting the blink period to upwards of 32 millennia:
write -g NSTextInsertionPointBlinkPeriod -float 999999999999
2013-02-21
Groupon
My startup just got acquired by Groupon (where I shall be used for Good, not Evil :-).
I started this Monday. So far, it is a little disorienting to work in what is best described as a huge confederation of startups. It should hopefully get better once I am chipping away at making stuff better.
2013-01-18
Beliefs as Era Indicators
The future is already here — it's just not very evenly distributed.A common Gibson quote, but have you reflected on how many levels it applies to? It is not just about technology, for instance – which might be a typical first understanding of the message. Let me portray how it applies to beliefs, and how it may be an even more useful picture to carry in your mind.
If you were to travel back in time to the renaissance, Terminator style, not a thread on your body, you would be from the future. But almost nothing about you would set you apart much from anyone else of the era – your strange dialect and manneurisms, relative lack of calluses from not having done much as manual labour goes – but nothing that would place you in another time, rather than from some other place – except what is in your head.
All sorts of things you believe (and more importantly don't believe) set you apart as a member of a different age. Some living in those days share some of your beliefs about how the solar system is arranged, for instance (the future was unevenly distributed back then, too, after all), but most don't, and would find you a strange eccentric full of funny made-up ideas, uneducated about how things are. You would probably find that it makes it easier to live peacefully by not insisting on convincing others about what you know and hold as true, and find people laughing at or dismissing you much less, the less you do, and the more you just blend in with the beliefs of the era. Getting on with life just takes precedence over distributing your ideas across the population of the era.
It is nice to live peacefully; most people probably prefer it, both then and now. And much like what you would behave like, to live in the past, most people we ever meet in life behave, today, rather than taking every opportunity to challenge other people's beliefs you know or have reason to believe different from yours. And consequentially, we live in a patchwork of partially overlapping belief worlds, medieval to far future, every moment of every day of our lives – much more than we would see in technology, say.
Discovering, exposing yourself to new beliefs (especially the high-impact core beliefs that affect your perception of yourself and the world), and challenging and upgrading those you currently hold, is the quickest way from now to the future. It always has been, whatever time you live in. Since the future is not evenly distributed, your future is one different from everybody else's, so you can zoom right past all your peers without anyone even knowing or noticing. Many of the people you meet daily in life live in places you will never set foot in, both having solutions and problems of times long gone, times yet to come, or futures you will never reach, as seen from your own time line.
While one might assume that most of our beliefs about ourselves and the world are derived from facts, they are generally not. More typically, they are based on other beliefs instilled by social upbringing and education, our perceptions or best guesses about experiences we have had and made – and very rarely subjected to any rigorous challenges. If I ask you how many senses you have, chances are very large you might come up with the number five without even for the briefest moment considering to make an inventory of your biosensory inputs – because someone taught you that, at some point. You would not challenge me if I remarked that you have a very good sense of balance stemming from an ingenious set of tubes in your inner ear, because this belief is a piece of trivia that is about language – a (today) dated agreement about what constitutes a sense, in the English language's traditional classification of nouns – not because you believe that your body lacks this sensory equipment. But unchallenged, it might never have occurred to you that this number five is a piece of random inherited baloney that we keep passing on to coming generations. Many – or hopefully most – of our beliefs are replaceable like this, when we realize they are incorrect, incomplete, irrelevant, just unhelpful, or worse, downright deceptive.
When people talk about life-long learning, I think they mostly talk about new skill sets, and rarely new beliefs. I used to think of it that way, before pondering what things I have learned over the last decade of the greatest worth to me as a human, in my everyday life. While some of that is certainly life skills like love languages and how-to type knowledge, most of it are things like a revised understanding of nutrition, that sugar is a drug, high-fructose corn syrup (the main ingredient of soft drinks and typical candy) is technically a kind of poison and how they affect me and my perceptions of the world when ingested, how clothes confine and isolate people both from themselves and each other, notions about how to assess and change what I believe and why, the importance of being wrong, in order to discover what is right – and many, many more. Picking up new skill sets, in terms of life impact, rank way down on the scale, by comparison.
Love and respect your teachers, and listen with indulgent curiosity to people expressing opinions you don't believe in. Because, apart from your imagination, that is your best source for new useful beliefs. And if you are more extrovert-wired, it may be the richer source of the two. And don't just listen for the substance of it, ask why and try to understand, as best you can, the motivations behind them, when it is better than "because someone told them". All learning worth having should come with explanations, and conversely, suspect all knowledge you hold that you can't derive from something. When you can't derive why something is true, you will rarely realize when that knowledge expires because of changes in the world, new discoveries and technologies with consequences to the old knowledge.
I do not rank myself a great teacher, in part from having little patience to pick gentle language or stick mostly to neutral ground, which are great skills I appreciate in people I do rank as such. My own style of communication is largely weaned among slightly autist-leaning programmers that charge at things passionately and pick and choose selectively from what is flung around, narrowing in on beliefs via what proves useful, while rarely or never interchanging belief with identity. The best are often wrong, and often change their mind, as soon as they discover they were wrong, and in such a culture, charging on strongly and having people point out your errors when spotted narrows in on better ideas far quicker than stepping lightly and covering your trail in qualifiers and disclaimers. While strongly conflict averse myself, I find merit in this efficient way of prestige-less shortcuts to arrive at better truths, and will note that it immediately breaks down out of context, if applied in contexts where skins are less thick, people more married to their beliefs and less selective about their take-aways.
If I note as one of my relative-future beliefs that I observe to be ill distributed around me (surveying my nearest grocery store's dairy section is more than enough data needed for that) that "light" products, for the most part, substitute healthy fat (that play a part in some pleasant sought-after taste or texture) with unhealthy processed sweeteners trying to replicate the same, to keep up with beliefs about fat that shot to the sky after some spectacularly bad science in the early 1980s, and causing more problems than they solve, I may be dispensing useful data, but not particularly good teaching. If I link to Robert Lustig talking about the same, I am somewhat better, but still not very good: I have not particularly rigged you to be interested in the subject, and chances are slim that his explanations seek you out at a time you are interested in them.
Especially as my motives for raising the example is mostly to self-indulgently lament living in a future, steeped in a contemporary medieval world not yet based on said future. When, one or a few generations from now, everybody believing in today's common notion about dieting and counting non-qualified carbs, and everybody passing on such beliefs to future generations – once they have all died out, my future will likely slowly get more evenly distributed, but until then, I can only nurse, co-create and choose local clusters of future in a sea of past. The more you live in the future, the more everything that surrounds you is all locked in the past.
While my tone may deceivingly marry "future" with value judgments as "good" and "past" as "bad", my intent is more in line with "more useful" and "less useful", to myself and other members of that world. You are more adapted to your surrounding era by holding its beliefs, however unaware of what might be incremental or radical improvements to its status quo by believing otherwise. I am fortunate to live in a time, with a mentality and income where, upon discovering that I don't see clearly as far as others, and diagnosing the cause as astigmatism, I could patch my lenses via some LASIK surgery in 2012, instead of changing my behaviour and wearing external optical patchwork every day of the rest of my life. Many either don't have that option, or worse, have, but never find out, even if they would have chosen it and been successful attempting it.
The core point I have made above is that beliefs, more than most things, define who we are, what we do and what comes out of it, how we feel about ourselves, what paths we choose, what we aspire to, worry about, avoid, and why; whom we share what with and when, and that we are at choice about them to the extent we choose, and never more. They can be pliable or rigid, set in stone or actively eager to get replaced, iterated, and superseded. And they exist in a multidimensional soup of other beliefs, most unreflected upon by those holding on to them. Some are benign, many illicitly planted, some detected, many not. Lots of beliefs are comforting but disastrous, like denying climate change, choosing a set seeming to incur less responsibility. Some are chosen to have an easier time coping – and sometimes believing falsehoods can help you live with a higher quality of life than you would have with a greater awareness, if knowing makes you brood or get plagued by guilt.
Life is a set of choices, but choices are downstream from beliefs. Upstreams from beliefs are whatever processes of multiplication and elimination you instill. Make them good ones. Live long, and prosper! :)
2013-01-01
iOS app search engine definitions
iOS apps iapp https://www.google.com/search?q=inurl%3Ahttps%3A%2F%2Fitunes.apple.com%2Fus%2Fapp%2F%20%s
(after tweaking "us" to whatever country code your apps come from)
It would be neat if the iOS app store had its own search page somewhere on the web so we didn't have to rely on Google's rather poor rendition of the search results, but I haven't found one.
The more json minded of you might also add an accompanying "iappjs" version, for the heck of it:
iOS apps json iappjs https://itunes.apple.com/search?country=us&media=software&limit=200&term=%s
2012-12-31
Surprise is data
2012-11-25
View rendered source bookmarklet
I haven't made a bookmarklet from scratch in a long while, but after looking at this codepen I wrote a while ago to demonstrate copying one javascript document into another, it occurred to me that it could easily become a modern "view rendered source" bookmarklet, that might even work on iOS devices and the like, where a view source feature is sorely missing.
Here is the result: view rendered source
On clicking it, you get a "view source" iframe with the source of the current document, in whichever state it was when you clicked the button, and when you click (or tap) that it goes away again.
I have yet to try it on an iDevice myself, but I have high hopes. To my surprise, manually entered bookmarklets like this one refuse to run in a modern Safari, though smaller ones that load their js payload over the wire instead seem to work, so I might make a gist of the code too, if that is what it takes.
Enjoy!
2012-10-18
Absolute url from a relative url and base_url
Today I needed a javascript url resolver to get absolute urls for urls mentioned in css files - resolved against the url of the stylesheet, not the current page. Fortunately, your browser already implements this natively, it's just not exposed in DOM APIs, so it needs a little DOM cleverness to coax out the functionality:
You can play around with it a little here, to see that your browser supports it, too. You should even be able to use a relative URL as the base_url
parameter, which should get resolved against the page url -- which here is the jsfiddle url shown, as that demo is running in an embedded iframe, rather than on this blog itself:
It of course won't work in node.js, but hopefully it'll be useful to something you or others are doing, too. Use as you like; it's all public domain / MIT licensed goodness, whichever you fancy.
2012-07-06
Facebook and the Human Mind
Facebook has created a niche where its inputs are humans (expressing human behaviour) and where its outputs are massive amounts of highly measurable data about said humans, and all the ways they affect, relate to and interconnect with each other, and other entities of the world. Both that data, and the behaviours that end up amassing the data, are highly profitable -- and exponentially more so, the larger its body of subjects. But let's look closer and deeper at the human behaviours it promotes, where it gets interesting:
I think I see the fundamental component of life itself, as it applies to behaviour: any behaviour which increases the likelihood of recreating itself, increases the rate of repeating itself, and increases the accuracy of the reproduction of itself, is (from a natural selection point of view,) more fit. Whenever this mechanism randomly shows up in nature (which only needs to happen once in a universe, as the seed mutates, and down the line reproduces the incredible wealth of complexity some unironically like to call "creation"), we call it Life. This behaviour, then, is a form of life that lives, reproduces and mutates in humans, a second order life form Richard Dawkins and others call memes. And Facebook operates as an incubation chamber for this life form and its hosts, a catalyst of the set of behaviours that drive the reaction. Facebook is an echo-chamber for memes -- creating, spreading and remixing memetic life, provisioning it with an ever increasing body of fertile human minds to live (and evolve) in.
Let me establish some vocabulary to help clarify this post: a Facebook operator, as I use the word in this post, refers neither to any Facebook employee or direct partner, but to anyone using Facebook's behaviour measurement tools monitoring what transpires in the system, what type of stimulus (under what circumstances) is how successful at driving engagement. I believe these tools are available to anyone creating or owning a Facebook Page, but otherwise for the most part invisible to everyday users of the Facebook medium itself. For reuse of already established Facebookian language, where I write "activity", you may think "meme", if you so prefer, or a Facebook Page post of some sort, be it a video, picture, link, or just a piece of text, as this is their chosen granularity of measurement / reporting.
What a Facebook operator sees is essentially how successful they are at producing and catalyzing user behaviour that spreads, as expressed by per-activity virality and reach factors, measuring reproduction rate (fecundity), number of subjects (how many humans an activity got exposed to), and on aggregate what shape those flows took; if it funneled through a few people that caused large multiplication factors, or through a large number of people that each produced a tiny multiplication factor, say.
Philosophically, I find the emerging picture telling a story of our culture's present state, and future direction, as the phenomenon seems strongly self-reinforcing. The story I see emerging behind and throughout the data is to drive humans to addictive-compulsory behaviour in general, and the dopamine cycle of attention-deficit disorder in particular. Dopamine is released in response to an action as a biochemical reward. Facebook, in a substantial way, operates like an enormous (human-focused) Skinner box, set on tightening the dopamine hit cycle, optimizing that loop to tighten it as much as possible, and delivering as successful stimulus as possible -- where the measure of success is to what extent each activity creates a dopamine blip in the body of people they get exposed to and to what extent they then choose to pass it on to their peers.
This phenomenon is not isolated to Facebook in particular, they just happen to be at its leading edge. I am sure Google Plus, Twitter and others in this field very much want to do the same, but just don't yet have (or expose) the same refined measurement tools to its operators.
We should not be surprised that Facebook, a company embracing "The Hacker Way" at its core, is first to deliver on this at scale; more than any profession I know, hackers embrace and understand the mechanics of tight cycles, and optimizing your development cycle (by adopting whatever tools, processes and mechanisms you can create, imagine, or buy for money, that shortens or streamlines it), and how massively it pays off in rate of progress. In Facebook's case, I see them doing the same to the smallest unit of activity in their Skinner box, and outsourcing the optimization to any external, willing, self-interested entity which helps them turn a profit for free. (Google is attempting to do the same to the web at large via Google Analytics, for that matter, but have a substantially lower degree of control, and higher granularity of measure.)
I am less interested in (and would kindly discourage any comments to that effect) all sorts of judgmental aspects, condemning Facebook (or any other entity) or similar for its role in this. Facebook, Twitter, Google Plus and others are, for better or worse, a replacement for the mainstream media journalism of the past, and in all the above I believe I have outlined the same mechanisms it has been rehashing over the decades it has existed, only at a much higher pace, due to the now far tighter and more highly instrumented cycle. The tabloid genes that thrived in their old, leakier Skinner box, is thriving and evolving in and to this shiny new Skinner box and shows up in all its most pre-digested forms, following laws of least-common-denominatorialist journalism, largest-emotional-impact journalism and similar -- all far more attuned to impact-on-subject than any other measurement of fitness or higher order utility such as whether the content serves its recipient in some way, informing them, making them happier, or more successful, as opposed to just more entertained and engaged with the medium.
I believe it's important to realize that it's somewhat useless to moralize over what transpires in the system or what people "ought" to be doing instead with their time, or what operators ought to be broadcasting into other people's spans of attention, as what we see emerge here is just a mirror image of what behaviours the human mind is conditioned for and dutifully reproduces, as evolution taught her, and which served her well in the few hundred thousand years to date, in the kind of circumstances she lived under throughout most of that period.
More usefully, I think, is to think of how we choose to expose ourselves to these kinds of tools, knowing that we are humans with very similar biological underlying programming, and to assess what values we derive from their use, how much and in what formats, under which circumstances and in which company (online, here) we subject ourselves to these media. Especially, you might benefit from thinking of it as you would a recreational drug, as its impact on you at a biochemical level is similar, as implemented by your biological hardware and social software.
And, much like our judgment is impaired by drugs and we do things that we at the time don't see the consequences of, down the line, our use of these online social systems, to varying (but substantial) degrees hides the future impact of our actions from us, as the visibility of our actions is multiplied million-fold from what they used to be in the pre-internet days of humanity, from which we still draw most of our genes and behaviours. Especially as this activity data trickles to places your typical drunken stupor was safely insulated from just a decade ago, and as power centers wielding all sorts of control over our lives, have started taking an interest in this data haven, and what bits of trivia about us they may draw upon to influence decisions concerning us. This is entirely invisible today, where we used to have at least the semblance of control in the age when the people we saw around us, were a decent hint to the reach of what transpired at any point in time.
Mind your step, be wary of what values you derive from what you invest your time and attention in, and maybe occasionally stop for a moment to think about how you prefer to engage your mind and connect with yourself, other humans and the world of thoughts and ideas. You get to choose all of the above, as long as you actually do choose, as opposed to following your unthinking hard-wired programming.
2012-06-24
Github TV ad
This is a transcript of the Github TV ad. If you haven't seen it, think "Apple commercial", Jonathan Ive's dreamy narrative, British accent, the whole jive, and you'll have it about right. Okay, you're set; cue soft music:
Here at Github, we like to fork the best ideas in the valley and spin them a little different. You might not have heard about our philantropic branch (because we don't like to brag), but it's actually the secret behind some of our most groundbreaking new innovations.
Github Biolabs is how Tom Preston-Werner, in late 2007 and before we were even founded, re-imagined Google's "20% time". We started Github as an incubator, here at the San Francisco asylum for mad scientists, and some of our most brilliant employees began their career doing 20% time with us, as a precursor to their first parole leave. None has left us yet.
[Cut to a person lying in the grass under a huge tree wearing a light visor, skimming through and tweaking its genome via Minority Report style gestures. Chromosomes fly by in the air above, zooming in and out, as she unfolds, marks, cuts, re-folds, pans, pulls, merges, rebases, pushes and splices sequences into the tree. She is wicked fast.]
At Github Biolabs, we have four saps on tap, an expansive green-house, and a beautiful arboretum. It is here that our scientists, using recombinant DNA splicing techniques, in this social open source setting, literally Create New Apples.
[Cut back to narrator, taking a bite out of one. Fade to white. Fade to logo:]
2011-10-08
Death and IE
Quoting that post's quote of Steve Jobs' 2005 Stanford commencement address (worth watching, if you haven't),
Death is very likely the single best invention of Life. It is Life’s change agent. It clears out the old to make way for the new. Right now the new is you, but someday not too long from now, you will gradually become the old and be cleared away.
This very strongly applies to web browsers, and as it turns out, the best thing a browser version can do (besides getting things right in the first place) is dying even more quickly than it came. I wonder if this enlightened insight might have sprung with Mozilla, if only Firefox could have kept its inaugural name, Phoenix, which it now fully embodies, but at least now, this gift has finally been given to web developers:
You need not bother writing applications (or perfecting layouts) to high-fidelity for old browsers, for their time is short and their better ancestors replace them quickly.
Assuming your deployment domain is web pages. As browser add-ons go, if you still maintain one of the old style XPI design your work burden has shot through the roof instead, unless you have near zero UI footprint, happen to only need and use APIs that time proves to remain stable and host it on addons.mozilla.org, where they bump its maxVersion every few weeks for you when it still seems to work. Failing either, you have to manually update and test it seven times per year. In Chrome, this has premeditatedly been addressed by not promiscuously offering any APIs it can't support in the long run, at the cost of limiting how much add-ons can do. Firefox, in this regard, remains the only browser where add-on authors can innovate 100% of the browser, and in this regard it has filled an important need in the browser eco-system (and still does). It is both its greatest feature and its greatest burden. Hopefully this will be mitigated to some extent with Jetpack and the new add-on builder.
Returning to the paramount topic of graceful, benevolent, rapid-evolution-supportive death, Microsoft's IE does not yet get it. Like a third world nation befallen by sudden prosperity, it has doubled its reproduction rate while keeping its mortality rate constant. As noted in mentioned posts, this does not bring just better browsers faster, it brings over-population, and makes web development unsustainable. Or, as Paul Irish put it in the first post: it pollutes the browser market.
In this respect, the conditional comments and many "backwards compatibility simulation" modes IE bring you are not mainly tools helping web developers make sites work on Internet Explorer, but a huge extra burden of work forced onto web developers, to cope with IE's broken release process. This is Microsoft's job, not ours, and we should be outraged with them for forcing it on us.
It is not the rotting corpses of IE6, IE7, IE8 or IE9 that all need to die to give space to IE10, which still smells fresh, it is a broken release process that needs to get addressed and brought up to date with best browser practices of the decade. It is the reinvention of sudden death, not just the gift of new life, that must come to Redmond, too. I applaud the IE team for making it their business to get up to speed with the web's evolution, but it's less the whats than the hows that are important to get right now. SVG is great, but sort out the release process before taking on, say, webGL. It can wait. Fixing the world's hard problems like over-population is harder than running after the latest ooo-shiny! - but the alternative is systemic collapse.
For a browser, it is better to live a great but short life and go out with a boom, than it is to burden its extended family with a never-ending old age in an insufferable early-set rigor mortis. However you feel about Steve Jobs he lived and died in this way, never holding back, never growing stale of mind nor action, and the world was better off for it.
2011-09-29
Running an old rails 2.3.8 with rvm
Uninitialized constant ActiveSupport::Dependencies::Mutex (NameError)...when you try to start the server. Here's the recipe I came up with:
- Install rvm.
# Install ruby 1.8.7 and downgrade its rubygems to 1.5.3: rvm install 1.8.7 && \ rvm use 1.8.7 && \ rvm gem install -v 1.4.2 rubygems-update && \ rvm gem update --system 1.4.2 && \ update_rubygems && \ echo 'Okay.'
# Install all the gems you need, for instance: rvm gem install -v 2.3.8 rails && \ rvm gem install -v 3.0 haml && \ rvm gem install -v 2.1 authlogic && \ rvm gem install -v 1.0 dalli && \ rvm gem install -v 0.2 omniauth && \ rvm gem install -v 2.7.0 erubis && \ rvm gem install -v 1.3.3 sqlite3 && \ echo 'Gems installed!'
- If needed, run
rake db:setup
in your rails tree to set up its databases. - Done!
rails/script/server -p your_port
is ready for action.
2011-07-05
Optimized SVGs at gist.github.com
For me, SVG has something of the magical flair I first found in HTML in the nineties, back when it was the new bleeding edge New Thing, but I argue that it's even more fun than HTML was. The W3C SVG specs are not prohibitively difficult to read, and of course you have much greater graphical freedom than structured documents can afford you (duh!).
Like Sam, I try for something presentable in a kilobyte or less (uncompressed, though modern good browsers are as happy to render SVG:s delivered with
content-encoding: gzip
, of course, as long as they are otherwise correct and delivered with an image/svg
or image/svg+xml
content-type), and to never enforce a fix width in the svg tag itself – so they just beautifully grow to any size you want them to be, with no loss of quality.Which is the other main beauty of SVG, besides being fly-weight, standardized, widely supported and still growing broader support in all the main-stream browsers. Over the last few years, the SVG front has been progressing happily and now is very practically useful already, for at least those of us that care most about Chrome, Firefox and Opera (I get the perception that Opera's often rather exceptional lead on the SVG support front is largely or even solely the work of Erik Dahlström, but I might exaggerate a bit).
Anyway, this weekend, I had fun turning the Laughing Man (from Ghost in the Shell: Stand Alone Complex) that @elmex vectorized at some point, into a 1000 byte version of my own, also featuring the gentle rotating text seen in the anime series (YouTube), via declarative animation (so there is no javascript involved here).
Edit: I initially missed an excellent opportunity here to plug Jeff Schiller's scour, which is an ideal first step when you start from an SVG source file. Be sure to run with -p something-large, as its defaults are being lossy about precision, which cuts needed decimals from some input files. With -p 99 you'll be on the safe side. Experiment with low single-digit numbers if you like (the current default – 5 – is often good), but make sure things are still looking right, or you may just ruin your file, rather than optimizing it for tiny foot-print. Broken images don't get extra credit for also being small!
The result is in this gist (if you install this user script, you can swap between
*.svg
text/plain source code image/svg rendition) or to the left, if your browser renders inline SVG:s properly in HTML content (I recommend the gist over view source, as Blogger inserts linebreak HTML tags unless I strip out all newlines first).What surprised me, when I made this user script, is how far standards support has come in modern browsers: in order to inject the
<svg>
tag I create for the rendered version, I had to feed the source through DOMParser
(as setting .innerHTML
is lossy from treating the SVG content as text/html
, not text/xml
), and the few lines doing that, just magically worked in all of Chrome, Firefox 4 and Opera 11 (de-jQuery:fied, to make more sense outside of the script's context) with no special extra effort on my part:// turn the raw SVG source string into an XML document:
svg = (new DOMParser).parseFromString(svg, 'text/xml');
// import it into an SVGSVGElement in this document:
svg = document.importNode(svg.documentElement, true);
// and insert that element somewhere in the document:
document.body.appendChild(svg);
To me, that's a rather clear sign SVG is ready for prime time now.While github reports having no plans on serving
*.svg
gists with an image content-type (they don't want people using gists for image hosting, I guess, even though it's sad you can't easily preview without saving to disk or using my hack above) I still think the light-weight gist community oriented sharing is good for this kind of thing. Others happily forked the octocat SVG I similarly format converted a while ago from the github About page, and milligramme made this much spacier version.I gather all my SVG play in a svg-cleanups repository on github, if anyone wants to get inspired the fork or follow way, and occasionally tweet about it. If you find this kind of exercise as much fun, I love hearing about it; here, on Twitter, github, or elsewhere. I believe it's good teaching and learning for the web as a whole, too. Any logos, trademarks and the like above are property of their respective owners.