Skip to main content

My virtual bookshelf

A picture is worth a thousand database-entries

A few years ago I decided, wherever possible, to not buy any more books. It's not that I don't want to read any more, oh the contrary, it's the huge, space-consuming amount of paper on the shelves that you have to pack and unpack every time you move. So eBooks are OK and since this decision my favourite way to read books.

So over the course of time a lot of reading material has accumulated. I bought one or the other Humble Bundle, downloaded the classics of the Gutenberg Collection into my personal collection, hoarded a lot of cheap out-of-date editions of basic computer knowledge and besides collecting all available Datenschleudern, I also saved a lot of scans from interesting magazine articles before I disposed them. Over the years, about 1000 "works" have accumulated.

If you work with Linux like I do, you will probably naturally use Calibre for administration. Using it I can copy the books back and forth between my PC and the readers, add metadata or read the books directly. Basically, I am navigating in a long list of books all the time. And I am constantly losing track.

How much easier would a bookshelf be

While searching in this list I often had the thought how much more intuitive this task has been when I stood in front of my bookshelf. I knew that my fantasy corner was where the neon-green edition of Lord of the Rings shone on the shelf, and the programming books were standing at waist level and grouped around the thick "C from A to Z" book. Visually striking books formed a kind of mental scaffolding in which the brain could navigate very quickly. The eye is still the broadest input channel of man. Long lists do not make sufficient use of this characteristic.

So I was looking for a plugin for Calibre to display my collection as a kind of bookshelf and to my own astonishment I did not find it.

When my partner barricaded herself at home for a weekend to finish her master thesis, I did the same for a small coders retreat to work on an implementation. The result was very satisfying shortly afterwards:

SVG as the weapon of choice

SVG, Scalable Vector Graphics, has a number of advantages for me as the tool of choice

  • It can be displayed with any browser, so I only need to generate the graphical representation, and don't have to take care of the displaying myself
  • I can insert active links directly into the graphic, which are then processed by the browser. This way, for each type of eBook (epub, mobi, pdf) the viewer that the OS provides, is used. I do not have to care about that either. I click on the book and it opens in the reader.
  • As the name suggests, the graphic is scalable, so I can zoom closer to the shelf and actually see more details.

Interesting problems with the implementation

I implemented the whole thing in Python, the complete code is on my codeberg

Different sizes and colours

When I look into my bookshelf (I still have about 1.5 meters of it) then I notice for example the Death Note series. The books are all smaller than the others, and they have the same spine. I wanted to do something similar for my virtual books.

I solved this by giving the books a uniform color and size depending on the publisher. I did this in the simplest way I could think of:

hex = hashlib.md5(self.publisher.encode('utf-8')).hexdigest()
h = int(int("0x" + str(hex)[-2:], 16)/2)
col = "#" + str(hex)[-6:]
pubcol = "#" + str(hex)[-8:-2]
hex = hashlib.md5(self.series.encode('utf-8')).hexdigest()
sercol = "#" + str(hex)[-8:-2]

link = SVG.SubElement(svg, "a",
                      href=self.formats[0],
                      type="application/epub+zip")

linktitle = SVG.SubElement(link, "title")
linktitle.text = self.title

book = SVG.SubElement(link, "rect",
                      fill=col,
                      stroke="black",
                      x=str(position),
                      y=str(bottom-192-h),
                      width=str(self.shelfspace()),
                      height=str(192+h))
book.set("stroke-width", "0.5")

publisher = SVG.SubElement(link, "rect",
                           fill=pubcol,
                           stroke="black",
                           x=str(position),
                           y=str(bottom-16),
                           width=str(self.shelfspace()),
                           height=str(16))
publisher.set("stroke-width", "0.5")

I simply generate an md5 hash from the publisher's name (cryptographic security is not important here) and use e.g. the last byte of it to calculate the height, or take the last 3 bytes as color value for the spine. I then do something similar with a small square at the top of the book to distinguish series.

Different book width

Another visual feature that makes it easy to find books on the shelf is the different width of the books. My first naive approach was to say that my widest book on the shelf should simply be 10 cm wide, and all others proportionally thinner according to the number of pages. But that went quite wrong. My widest book has about 4100 pages, my smallest books have only 2 to 5 pages. Apart from the fact that these scans of articles would then hardly be visible on the shelf, it would also be impossible to label their spines.

Therefore a book has to be at least 10mm wide. I gave the books a cover with this minimum width, and looked for a method to increase the thickness of thin books very fast, but with increasing number of copies, the thickness grows slower and slower until finally 90mm are reached. Plus binding I am then again at 10cm. Out came the following:

def shelfspace(self):
    return sqrt(1-(1-(self.seiten / Book.maxPages) ** 2)) * 90 + 10

Since this is not immediately obvious, here is my sketch:

nil

As you can see in the lower right corner I had briefly thought about doing this using sine and cosine, but when I drew the radius it became clear that I was dealing with a right angle, and there is always the beloved a²+b²=c². If you look at the arc, and the number of sides increasing from left to right, you can see how the thickness increases almost vertically at the beginning and then slowly increases to the maximum thickness. While it makes a big difference whether a book has 50 or 150 pages, it's almost irrelevant whether it has 1900 or 2000 pages. In the end it is 90 mm plus 10 mm for the binding.

What else I want to do

Currently in work is to sort the books thematically. I first need a thematic order, and for this all books have to be tagged according to their content. With 1000 books this will take quite some time, then I will group the tags by categories, and when filling them I will make sure to do this in the right order. On the shelves there should be small labels on which you could read which topics start where. Big themes will also get a sign at the top of the shelf similar to a bookstore.

The book spines should be more realistic in the sense that they imitate the cover a little bit more. I want to create a strip from the left edge of the cover (a few pixels wide), which will be used as a spine.

A few graphic gimmicks, like books leaning at an angle or small stacks of books will certainly give more orientation.

2020-04-13