Monday February 22, 2010

12:41 PM: Clothing and Hairstyles of Carolingian and Renaissance France

This book (see the “View the book” sidebar) is a nice reference for French clothing and hairstyles during the Carolingian and Renaissance periods.

It’s interesting to flip through even if you don’t read French, and I can think of a few artists for whom it might provide useful visual reference and inspiration.

Tuesday February 16, 2010

12:46 AM: We Are the Champions (Tatar)

I think this Tatar rendition of Queen’s “We Are the Champions” is charming and beautiful:

Без чемпионлар

Wednesday February 10, 2010

01:41 PM: duizhao (對照)

Every once and a while I find myself posting a snippet of translated text somewhere. If I actually want to format the original and translated passage in parallel and have everything line up, it can be a real pain. For forums which support it, the most foolproof way to do this is HTML tables, of course — and (in my opinion at least) it actually represents a proper semantic use of tables. But writing out the HTML for this by hand can still be pretty tedious.

Prompted today by a friend who does song translations as a hobby, I finally sat down and made a little lunchtime hack to assist in the process. It’s pretty much just a JavaScript-driven HTML form where you enter the original and translated text, and it automatically formats them into an HTML table as you type, breaking things up by line. You get both the raw HTML and a live preview.

Anyway, such as it is, I’ve got a github project set up, as well as a live copy posted here on this site. (It doesn’t require any server-side things, so feel free to save a copy of the page locally.)

If you’ve ever needed this sort of thing, may it serve you well.

(Note: tested only in Firefox 3.5 so far; YMMV with other browsers.)

Sunday February 07, 2010

01:23 PM: Toto, A Minimalist Blogging Engine

Since I’ve got minimalist blogging engines on the brain lately, in addition to Whisper and Jekyll, I thought I also ought to call attention to Toto.

Toto is dynamic, rather than a static site generator, but it’s very straightforward and simple. It works very nicely with Heroku (which is pretty awesome, by the way).

12:06 AM: CouchDB Backend for Hx

Just for grins I had a go at implementing a CouchDB backend for Hx today. It’s pretty straightforward; the document id in couch becomes the entry path in Hx, and the actual entry Hash is taken directly from the document JSON. Configuration of a CouchDB source is also quite simple:

filter: Hx::Backend::CouchDB
options:
  couchdb_server: http://localhost:5984/
  couchdb_database: entries

The filter options could actually be omitted entirely in this case, since the example represents the defaults if the server and database options aren’t given explicitly.

Here’s a stripped-down version of the CouchDB backend to demonstrate what an Hx backend looks like:

class Hx::Backend::CouchDB
  include Hx::Source

  def initialize(source, options)
    @couchdb_server = options[:couchdb_server] ||
                      "http://localhost:5984"
    @couchdb_database = options[:couchdb_database] ||
                        "entries"
  end

  def edit_entry(path, prototype=nil)
    begin
      text = get_document(path)
    rescue HTTPError => e
      raise e unless e.code == 404
      raise Hx::NoSuchEntryError, path unless prototype
      text = prototype.to_json
    end
    text = yield text
    entry = JSON.parse(text)
    entry['updated'] = Time.now.xmlschema
    entry['created'] ||= entry['updated']
    put_document(path, entry.to_json)
    self
  end

  def each_entry
    listing = JSON.parse(get_document('_all_docs'))
    for row in listing['rows']
      path = row['id']
      entry = JSON.parse(get_document(path))
      for field in %(created updated)
        entry[field] = Time.parse(entry[field] || "")
      end
      yield path, entry
    end
    self
  end

  def get_document(id)
    # ...
  end

  def put_document(id, body)
    # ...
  end
end

Some error handling has been removed, and I’ve elided the definitions of get_document and put_document for the sake of space. The main points of interest are:

  • CouchDB.new, which takes an upstream source (ignored by most backend sources) and an options hash
  • CouchDB#edit_entry, which passes the raw (backend-dependent) entry text to its block, and replaces the entry in the database with the returned result, updating the 'updated' and 'created' fields.
  • CouchDB#each_entry, which iterates through all the entries provided by this backend instance, yielding path, entry pairs. In Hx, entries are just Ruby hashes, nothing more.

Those three methods are really all any Hx backend needs to provide.

Tuesday February 02, 2010

09:24 PM: The Delectable Giant Tortoise

The Giant Tortoise went three hundred years without getting a Latin classification — perhaps in large part because it was so delicious that researchers kept eating their specimens.

QI: Giant Tortoise

I feel strangely hungry now.

07:15 PM: Life Below 600px

In Life Below 600px, Paddy Donnelly suggests that we can benefit by embracing scrolling in web design. In particular, presenting content incrementally (rather than all at once in the initial screenfull) can afford better opportunities to build reader interest.

Actually, even back in 1997, Jakob Nielsen remarked that scrolling was much less of an obstacle for users than it had once been:

The change from 1994 is that scrolling is no longer a usability disaster for navigation pages. Scrolling still reduces usability, but all design involves trade-offs, and the argument against scrolling is no longer as strong as it used to be. Thus, pages that can be markedly improved with a scrolling design may be made as long as necessary, though it should be a rare exception to go beyond three screenfulls on an average monitor.

(Also, client-side imagemaps are okay now. In case you were anxious about that.)

I think it’s probably fair to say that the situation for scrolling has also improved further since then, particularly now that support for scroll wheels on mice is so widespread in browsers.

Of course, there are some caveats. When we talk about scrolling in this context, we really mean vertical scrolling. It is what users are accustomed to, and it is what input devices like mice generally support.

You also still have to make sure that the first screenfull your readers see is enough to capture reader interest. Lastly, if your content is primarily visual (rather than textual), scrolling can be a serious inconvenience. But more on that in another post.

Sunday January 31, 2010

04:46 PM: Webcomic Navigation and Layout

I’ve been doing a great deal of thinking about navigation and layout for webcomics lately. I think I will have some concrete thoughts of my own to offer in a little while, but for now, I’d just like to put up some links that I have found particularly helpful or at least thought-provoking on the topic.

My preliminary thoughts:

  1. Readers hate scrolling; the comic should fit the browser window.
  2. If you must make the reader scroll, limit scrolling to one axis.
  3. Of the two axes, scrolling vertically is better since we have convenient UI like the mouse wheel for doing it.
  4. If the browser window is too small, downscaling can help, but good-quality image resampling is still somewhat hard to come by in the browser.
  5. Even with good-quality resampling, downscaling is still useless below a certain size, since important features will become illegible.
  6. When downscaling isn’t an option, it is probably best to chop the page into smaller units which can be shown individually when the browser window is too small to show them all at once.

Unfortunately there seems to be a big gap between these points and actual webcomic practice.

In a future post I think I’ll have more to say about why scrolling is really bad for comics, at least ones which weren’t carefully designed specifically for an “infinite canvas” presentation. (And even then…)

12:11 AM: Now Powered by Hx

I looked at a number of different software packages to replace Hobix, but none of them really appealed. The best two candidates were Jekyll and Whisper, but Jekyll is a little too heavily focused on blogging specifically (a problem with Hobix too, though it wasn’t quite as bad), and Whisper isn’t a static site generator.

So, in classic NIH tradition, I eventually wrote my own static site generator, Hx. It’s pretty raw and not especially fast (though it does the job faster than Hobix), but it’s pretty straightforward and flexible. It has no particular baked-in knowledge of what a “blog” is: it’s just a graph of generic filters which enumerate pairs of paths and documents. Throw in liquid templates, and it’s enough to do everything I need for now.

For the curious, here’s what the hx-config.yaml for this site presently looks like:

---
require:
  - site-hacks
options:
  base_url: http://moonbase.rydia.net/
  template_dir: templates
  output_dir: site
  lib_dir: lib
  links:
    - url: http://twitter.com/mentalguy
      name: "@mentalguy"
    - url: http://inkscape.org/
      name: "Inkscape"
    - url: http://lastbus.rydia.net/
      name: "The Last Bus"
    - url: http://rydia.net/
      name: "rydia.net"
  default_author: mental
sources:
  entries:
    filter: Hx::Backend::Hobix
    options:
      entry_dir: entries
    only: mental/blog/**
    sort_by: created
    reverse: true
    cache: true
  blog: # alias for editing
    source: entries
    strip_prefix: mental/blog/
  indexes:
    source: entries
    filter: Hx::Listing::RecursiveIndex
    cache: true
  front_page:
    - source: indexes
      only: index
    - filter: Hx::Listing::Paginate
      options:
        page_size: 10
  sections:
    - source: indexes
      except: index
    - filter: Hx::Listing::Paginate
      options:
        page_size: 40
  feeds:
    source: entries
    filter: Hx::Listing::RecursiveIndex
    options:
      limit: 10
    only:
      - index
      - mental/index
      - mental/blog/index
outputs:
  - source: entries
    filter: Hx::Output::LiquidTemplate
    options:
      extension: html
      template: blog-entry.liquid
  - source: front_page
    filter: Hx::Output::LiquidTemplate
    options:
      extension: html
      template: front-page.liquid
  - source: sections
    filter: Hx::Output::LiquidTemplate
    options:
      extension: html
      template: section-index.liquid
  - source: feeds
    filter: Hx::Output::LiquidTemplate
    options:
      extension: atom
      template: atom-feed.liquid

I won’t be doing any serious public releases of Hx for a while since I’m still smoothing out a lot of rough areas and there are sure to be incompatible changes in the process, but you’re welcome to poke around on your own.

Saturday January 30, 2010

11:34 PM: Worm Grunting

Worm Grunting is the art of luring earthworms to the surface, typically using a stake in the ground to create vibrations which simulate the digging of their natural predators.