Sunday, July 16, 2017

OpenSeadragon 2.3.0

We've just released version 2.3.0 of OpenSeadragon!

For those unfamiliar, OpenSeadragon is a web-based zooming library, used all over the world to share high-resolution imagery (classic paintings, ancient manuscripts, medical imaging, etc.). I wrote the original version back in 2007 (holy cow, that's 10 years ago!) when I worked on the Seadragon team at Microsoft. We later open sourced it, and it's been picking up steam ever since then.

This new version brings lots of little improvements along with new features like:
  • Zoomify format support. This is useful for moving over from legacy Zoomify installations, but it also gives us access to Zoomify's tileset creation tools, such as the Photoshop export plug-in, which may mean an easier on-ramp for folks new to zooming technology.
  • The ability to rotate individual images. You've already been able to rotate the entire viewport, but now you can rotate each image within individually.
  • Events to let you know when the current view has fully resolved; as the user zooms deep into your image it can take a moment for all the pixels to load, and knowing exactly when can be useful for certain effects.
  • The ability to preload images before showing them, for smoother transitions from image to image.
  • CommonJS support.
… and much more!

OpenSeadragon is truly a group effort, with 89 contributors so far, not to mention the folks creating plug-ins for it. I'm still kind of stunned at how the OpenSeadragon community has grown!

I oversee the project, but my role is more facilitator than lead. Whenever someone reports an issue, I help them get to the bottom of it, and I review their patches, but I don't do the actual coding for them. This allows me to have a greater impact with the limited time I have for the project, and it empowers more people to become actively involved in shaping the code. I think if I'd personally taken on fixing every bug and adding every feature as they are posted, I would have burned out long ago, and none of the great contributors would have joined.

So, if you're new to OpenSeadragon, come check it out and see all the cool stuff people have made with it! Also, if you appreciate my part in the project, please consider pledging to my Patreon to support my work there.

Congratulations to everyone who's made this release possible! Here's to many more years of awesome zooming!

Sunday, November 06, 2016

Getting to the Bottom of It

In today's world we have many large controversial topics that we keep going around and around on, instead of just getting to the bottom of. I'm sure there are many reasons for this, many of which don't have anything to do with rational discourse, but let's pretend for a moment that a little rational discourse can at least make a small improvement.

Part of the problem with these big topics is that they are so multifaceted, it's hard to hold it all in your head at once. What we need is a big map starting from a simple premise and going down, down, down through all the pros and cons, twists and turns, branching into every subtopic, to fully cover every facet. I'm confident this information is already available; it just needs to be organized.

I'm imagining a website for this. Anyone can start a new topic, e.g. "is global warming real?" Then anyone can add pro or con posts, and anyone can add rebuttals to each, and rebut the rebuttals and so on. Once a topic is in place, you can navigate it by expanding the areas that interest you, until you get to the bottom of it.

In order for this to work, a lot of care needs to go into making sure the structure is clean… No circular arguments, no logical fallacies. All of the misinformation needs to be debunked (not just buried). Wherever possible there should be links to primary sources. Each answer needs to cover a single point. This means there needs to be as much energy and facility directed toward structural work as there is toward content contribution.

So, for instance, redundant answers need to be able to be merged; answers covering multiple points need to be split; points that have been covered elsewhere need to be linked; logical fallacies need to be flagged; etc. Of course each of these actions need to be vettable, so improperly merged, split, or flagged items can be undone. All of this activity needs to be transparent.

Even if a thing like this won't resolve these arguments directly, it would be an invaluable resource for those working to do so.


Saturday, April 30, 2016

Captain America and the Planetary Era

The upcoming Captain America movie has reminded me of a thing I wrote up about Winter Soldier back when it came out but never got around to posting, about the way Planetary Era themes popped up in that seemingly unlikely place.

If you're not familiar with the Planetary Era concept, I highly recommend watching my father's talk: What Time Is It? The basic idea is that various global trends are moving us into a new societal state that is fundamentally different than the empire-driven societies of the last few millennia. Due to our now globally interconnected nature we need to start thinking and working together as one planetary family, with all of the opportunities and challenges that entails. As with any major shift, some folks are already way ahead on this front, while others lag behind.

So anyway, here are my thoughts. If you haven't already seen Winter Soldier, you need to re-examine your priorities, but also be warned spoilers lie below:

So I saw Captain America: Winter Soldier, and I was struck by a couple of Planetary Era themes. I know, you'd think a movie featuring a hero named after a country would be all about Empire Era thinking, and there was plenty that too, but you could see the Planetary Era peeking through.

One thing that really stuck out for me was the way in which the conflict was repeatedly framed as one between order and chaos. That's not unusual in movies of this sort, but what is unusual is that it was pretty clearly stated that order was the side of the villains, not the good guys. Now, of course any healthy society has a balance between order and chaos, but balance isn't a static thing; it's always moving to match the needs of the time. While the Planetary Era may in fact be more orderly in some ways, I'd say it's pretty clear that the Planetary Era themes of diversity, self-organization, and even our increasingly small-world connectivity will certainly look and feel more chaotic to many observers. Rather than clinging to the illusion of order the Empire Era thinking provided, we will be better served by stepping into the apparent chaos of a vibrant interconnected world.

The other thing that struck me was how one of the key aspects of the good guys winning was the public sharing of all of SHIELD's secrets. This too seems more in tune with the Planetary Era's themes of connectivity and self-organization than the Empire Era's rigid hierarchies and us-versus-them thinking.

Perhaps it's a bit much to say that Captain America is leading the charge into the Planetary Era, but it's good to see that even in this unlikely place, ideas that resonate with the new era are taking hold. I'm looking forward to the upcoming movie… We'll see if it continues the trend, or if Winter Soldier was just a comic book outlier.

Labels: ,

Sunday, November 29, 2015

The Importance of Being Ironic

Hipsters and their irony, amirite? What even is the deal?

In the 20th century, the world got way smaller than ever before, but now in the 21st we've taken it to a whole new level with almost instant access from anyone to anyone on the planet. 100 years ago you could easily go months or even years without your worldview being challenged. Not so much anymore, even with the filter bubble effect.

This is actually a good thing… The world has been filled with different ideas and viewpoints for all of history, and it's about time we as a species get around to accepting this fact. It's about time we learned to use our differences to the benefit of all. Just as in natural ecosystems, diversity creates strength, but it also has its challenges.

One of these challenges is holding conflicting ideas in mind simultaneously without feeling the need to pick sides. Sometimes the only way through a complex situation lies on the road to seeming paradox. Becoming comfortable with this cognitive dissonance, so you can freely navigate the multilayered thoughtspace of the world, is a key skill of the 21st century.

And irony, our old friend, is a handy steppingstone to mastery of that skill. What is irony, after all, but the binding together of conflicting ideas? Whether they know it or not, irony aficionados are doing exactly what they must in order to thrive in the hyper-connected world of today and tomorrow.


Wednesday, May 27, 2015

OpenSeadragon 2.0: Forest-to-Tree Zooming

Today we cross a major threshold!

OpenSeadragon turns 2.0 and brings with it full support for zooming multiple high-resolution images in the same scene. Imagine all the pages of an ancient manuscript laid out on your screen and being able to seamlessly zoom in to the smallest detail on any page.

Back in 2008 when OpenSeadragon (then called Seadragon Ajax) was first released — when IE 7 was Microsoft's top browser and woolly mammoths wandered the frozen steppes — smoothly zooming a single image in pure JavaScript was quite the feat! We could only dream about the sort of cool multi-image scenarios Blaise demoed using a native app with GPU-accelerated code in his famous TED talk.

OpenSeadragon doesn't yet do everything shown in that video, but multi-image opens up some exciting new possibilities! Check out these demos for some inspiration:

OpenSeadragon + Rdio: swim across a sea of high-resolution album artwork, discovering new music as you go.

OpenSeadragon + Flickr: hop from image to tag to image on Flickr's most interesting photos of the day.

Chris Jordan Infinite Zoom: kick back and fill your screen with Chris Jordan's giant photo montages.

If you're new to OpenSeadragon, you should also check out all the great work people have done with the single-image version, such as this gigantic panorama of Mont Blanc and this amazingly detailed story-map of Central America.

Now what are you waiting for? Make some cool multi-image OpenSeadragon things!

Labels: ,

Tuesday, February 03, 2015

self = this

I teach JavaScript, and one of the things I've found most confuses people is the self = this pattern (some people even go to great lengths to avoid learning about it). I haven't been able to find a good post on it, so here's my attempt. If you're not interested in JavaScript, feel free to skip this and read some of my other fine posts.

Okay, I'll start with a concise definition and then explain in depth.

Use the self = this pattern whenever you have a function nested inside of an object's method, and you want to access the object's properties.

Assign self in the object's method and use it inside the nested function. Here's how it looks:

var myObject = {
  aMethod: function() {
    var self = this;

    this.aProperty = 'foo';

    setTimeout(function() {
      console.log(self.aProperty); // outputs 'foo'
    }, 1);

So, let's explain further.

Objects with Functions

While you can certainly write plenty of JavaScript without ever attaching a function to an object, I find that organizing my code into objects makes things a lot cleaner. To avoid junking up the global namespace, I usually define a single global object (named "App" or some such) that all of my variables and functions are attached to. To further organize, I'll group related functions and variables into sub-objects attached to the main object.

Most objects will have a mix of variables (called properties when they are part of an object) and functions (called methods when they are part of an object). Here's a simple object with one of each:

var myObject = {
  aProperty: 'foo', 
  aMethod: function() {

Inside an object's methods, there's a special variable, named this, that is a reference to that object. If the method wants to access any of the object's properties or other methods, it does so through that variable, like so:

var myObject = {
  aProperty: 'foo', 
  aMethod: function() {
    console.log(this.aProperty); // outputs 'foo'

This special variable is managed by JavaScript; you don't have to assign it, and in fact you can't. It's assigned automatically for every function. If it's an object method, this is a reference to the object. For any other object, this is a reference to the global variable namespace.

Nested Functions

In JavaScript, you can nest functions inside of other functions, including object methods. This is a common practice when working with user events, network requests, timers, etc.

var myObject = {
  aMethod: function() {
    setTimeout(function() {
    }, 1);

One handy feature of nested functions is that the inner function has access to all of the variables defined in the outer function. This is called closure.

var myObject = {
  aMethod: function() {
    var a = 'foo';

    setTimeout(function() {
      console.log(a); // outputs 'foo'
    }, 1);

The one exception is the special this variable; since the nested function is just a regular function, not an object method, its this is a reference to the global namespace.

var aProperty = 'global';

var myObject = {
  aMethod: function() {
    this.aProperty = 'local';
    setTimeout(function() {
      console.log(this.aProperty); // outputs 'global'
    }, 1);

Putting It All Together

So, we need a way to get a reference to the object to be available inside the nested function. Closure to the rescue! We assign another (non-special) variable, e.g. self, with the object from this. Since this variable isn't overwritten for each function (unlike this), we can use it inside the inner function.

var myObject = {
  aMethod: function() {
    var self = this;

    this.aProperty = 'foo';

    setTimeout(function() {
      console.log(self.aProperty); // outputs 'foo'
    }, 1);


  • There's nothing magic about the word self; you can use whatever variable name you want. However, self is the most commonly used variable name for this pattern, so it's a good choice for clarity's sake.
  • If a method needs a var self = this; line, I always put it at the very top of the method. Putting it at the top isn't absolutely necessary, but I think it's a good convention; that way you don't have to go hunting to see if it's defined.
  • this is only set up properly if you call the method via the object, like myObject.aMethod(). If you instead call the method indirectly, such as when you pass it to another function (e.g. setTimeout(myObject.aMethod, 1)), this won't be set up. To deal with this, we use closures again, like so: setTimeout(function() { myObject.aMethod(); }, 1).
  • It's actually possible in certain cases for this to not be the object or the global namespace. By using JavaScript's apply or call features, you can set this to whatever you want when you're calling a function. jQuery uses this in its event handlers, for instance.

Tuesday, January 06, 2015

Be More Awesome

With education, as with many things, I find it's useful to start from basic principles. There are so many options and philosophies, it's easy to get overwhelmed. A good mission statement can cut through all that, or at least give you a place to start.

So, here's ours:

The goal is for Caitlyn to become more awesome. Our strategy for becoming more awesome is to learn new skills and improve existing ones. The only way to learn/improve skills is through practice. Therefore her job is to practice, and to improve her ability to practice (since practicing is itself also a skill).

Practice can be made more effective with:

  • Instruction and examples
  • Feedback
  • The proper tools
  • A conducive environment
  • Goals and rewards
  • Other skills
  • No doubt many more things

All of these are worth gathering when possible, but none of them are worth waiting for.

That's it.

I suppose one other question worth answering is, "Which skills should we practice?" To be honest, I don't think it really matters that much. All skills are useful, and all can be translated to other contexts, and at any rate the most important skill is how to practice effectively. That said, some of the most important skills are what you might call building block skills; those that many other skills are based on. Reading, writing, and math are the obvious ones, but there are many more, such as time management, teamwork, research, and experimentation.

Anyway, with this simple mission statement, we can focus on what's important while allowing a broad range of variation.

Labels: ,

Tuesday, October 21, 2014

How I Rdio

I've been a fan of Rdio for years now (which, come to think of it, is probably why I work for them). More interesting, though, than the question, “What music service do you use?” is, “How do you actually use it?” Here then, is my answer. For starters, it should be noted that while in general I love Rdio’s UI, there are various things I’ve found to be missing that I’ve gone ahead and built for myself using Rdio’s lovely JavaScript API (which, as it turns out, I also built; that’s how I know how lovely it is). With that in mind, let’s take a tour:

The Tunes Must Flow

I listen to music most frequently while working at my computer. During the workday, I just want to have excellent music playing, and I want to stay in the flow. For me this means whole albums, because each album has a consistency that doesn’t grab your attention with each track change. This also means being able to queue up a whole bunch of albums ahead of time, so I don’t have to stop and fiddle with my music periodically throughout the day. Fortunately, Rdio excels at thinking in terms of albums, and it’s got a fine queue. Every so often, when the queue runs low, I’ll jam a new pile on; I’ve generally got dozens of albums lined up.


When I’m not working, I do like to fiddle with music, as a leisure-time activity. In the evenings I sometimes wander around, checking things out, adding new stuff to my queue or favorites, etc. This is the modern-day equivalent of my old habit of haunting music stores, looking for that next great find. If I'm looking for new music, I'll use Fathom or the social stuff that shows up on Rdio’s homepage feed or the "here are new albums from your favorite artists" feature that shows up in notifications.

Once I’ve found a promising-sounding album, I toss it on the queue for a full listening during the workday. Every few days I go to Music Triage which shows me any new albums I've listened to that I haven't already triaged. Here I quickly add things to my favorites or mark things I didn't like. In this way I make sure nothing falls through the cracks, without having to pay attention throughout the day.


It turns out music discovery these days is so great, I’ve got a new problem: hundreds of albums I’ve discovered and liked enough to add to my favorites but never listened to again because I was off to the next big thing. To listen to music I already know I like, and hopefully connect more deeply with things I’ve discovered, I pull up Collection Random and use it to quickly queue up a bunch of albums. I find this "show me some stuff randomly from my collection" better than any sort of rational sorting… otherwise you end up falling into ruts.

Mid-Queue Interruptions

My next-most-frequent music-listening station is in the kitchen while cooking and cleaning. I usually want to listen to something entirely different there; more upbeat and attention-grabbing. If I think of it before leaving my computer, I push an appropriate album ahead of the queue (in Rdio’s Mac interface, it’s command-clicking the play button on any album). Unfortunately, Rdio’s mobile app doesn’t support such things, so I’ve made Mobile Queue to support reorganizing the queue on my mobile web browser.

So there you have it

…my music “workflow”. Who knows what my music rhythm will be like in a year, but for now this is working great, and I love having the power to customize my interactions as needed.  At the moment, I think I’ve found a good balance between finding new stuff and listening to favorites, with a minimum of ongoing effort (not counting the initial development time, which was fun noodling anyway).

How about you? How do you use your music service?


Tuesday, February 04, 2014

Video Games

I played a lot of videogames as a kid (mostly on the Apple II, and then later on the Mac). At the time, of course, I just thought of it as fun, but now I realize I learned a lot doing so. Problem solving and resilience/determination are high on the list. You can't get anywhere in this world without being willing to try and fail and try again. Video games are great way to instill that instinct.

Of course Caitlyn has been playing games on the iPad for years now, but computer games are a different beast. In the past, we've focused on puzzle games; they make you think, but there's no time pressure, and not much need for eye/hand coordination. That was good for when she was younger, but now she's ready for more of a challenge.

We started with Return to Dark Castle, which is a remake of one of my favorite games from when I was kid. Now we're playing FEZ, which is delightful. Platformers seem like a good point on the videogame spectrum at the moment. They provide exploration and puzzle solving, but with an added level of arcade action, but not too intense yet.

I'm on the lookout for the next game. Any suggestions?

Labels: ,

Tuesday, January 28, 2014

JavaScript & Uncorrected Video

There is this continuing debate that rages in tech circles: If you're going to make an app, should it be web or native? The true answer, of course, is that it depends. I'm a web guy, though, so when I enter the fray, it's generally on that side.

The common perception of web tech is that it's easier, but produces inferior results. You can slap something together in a fraction of the time, but it's not going to have that slick, polished quality you expect from native apps.

It all reminds me of the old film vs digital thing the movie industry went through a while ago. I was working in Hollywood at the time, and there was a lot of buzz about the liberating potential of digital video, but also a lot of criticism of its inferior quality.

Well, to some extent it was inferior, because it was still young, but there was more than that at play. The thing is, when you shoot film, you have to get it developed, and part of this process is making sure the colors came out right, and you get the tonal range you want, etc. Without that color correction step, film looks horrible, unusable. With video (digital or otherwise), you can skip that step; uncorrected video isn't great, but it's not as horrible as uncorrected film. So, since you can skip it, lots of people did, and video got this reputation for having that bland, washed out look. Over time, however, people clued in, and now tons of movies are shot on digital, but they're always color corrected. At this point, film and digital are virtually indistinguishable to the viewer, and what the cinematographer uses is more just a question of personal taste than any technical consideration.

Back in JavaScript land, we have a similar situation. The technology is still young, so it was pretty bad not that long ago. Like digital video, it's liberating; your app can run anywhere, and you can get started with a set of simple tools. The fact that it's easier, however, gives us the uncorrected video problem: people who don't know what they're doing can still create JavaScript apps. They may be crappy apps, but they work. With a native app, there's a higher bar; if you don't know what you're doing, you're likely to not end up with anything usable at all. The native apps that survive generally have a decent level of quality.

But when someone who really knows what they're doing builds a JavaScript app, you can get the flexibility you expect from the web and the polish you expect from native, just like digital video can look great if you bother to color correct it. For me, that's the best of both worlds, though of course your mileage may vary.

My favorite example of a slick mobile web app is (at least on the iPhone). I'm doing my best to walk the talk as well with Driftory and Gimme Shiny, both of which work great on mobile.