Pat David Avatar

Displaying a Big HTML Table

tl;dr - To speed up loading a large HTML table on the client, start with the table CSS set to display: none;. Once the document is ready, set it back to display: table;. This reduced my client-side render time from 60 seconds to 6 seconds on a table with ~400,000 cells.


Libre Graphics Meeting 2019 Website

I’m really looking forward to the upcoming Libre Graphics Meeting in Saarbrücken, Germany next year!

For those unfamiliar with the meeting, it’s an annual gathering of Free/Libre software projects and artists. There’s an amazing community of users, developers, and hackers that attend every year to talk about all sorts of cool topics like fonts, photography, painting, performance, and many other things that start with the letter “p” (printing? pontificating? pixls?).

The “pixls” is a bit of a joke, but in reality I’m cautiously optimistic that we’ll get a chance to assemble quite a few nerds there. Friends from both darktable, RawTherapee, and many more actually live in or near Saarbrücken and I am super excited to see them!

Wanting to help the organizers, out I took a stab at designing a website for them. They use Wordpress for the site, so I had to figure out theming in Wordpress, but I had some fun doing it.

In the end they didn’t use what I cooked up, but I personally really liked it. Rather than let it languish in obscurity, I figured I would at least share it here:

Libre Graphics 2019 Website Theme

LGM 2019 Website Front Page

I’m a huge fan of The League of Moveable Type’s fonts, and their League Spartan is an awesome, bold, loud font that demands attention (like me?).

LGM 2019 Website Call for Participation Page

I made some decent progress on the overall design before I realized it wasn’t going to be used, so there’s still some rough edges that needed to be finished. I also generated a static version of the site I had setup on my host (no need for yet another Wordpress site hanging around the internet).

This is one of those cases where I kind of liked the overall course the design was taking and will keep it in mind to cannibalize for future projects.

Filed under: LGM, HTML

Tact Filters

Being a part of a community of smart folks means that inevitably there will be personality clashes. I was reminded of this recently, and found myself needing to remember to be empathetic.

Thomas Watson Sr, discussing the early IBM motto, “THINK”. (wiki)

darix was kind enough to link me this wonderful post from Jeff Bigler over twenty years ago that is still quite relevant today:

Tact Filters

Jeff Bigler

All people have a “tact filter”, which applies tact in one direction to everything that passes through it. Most “normal people” have the tact filter positioned to apply tact in the outgoing direction. Thus whatever normal people say gets the appropriate amount of tact applied to it before they say it. This is because when they were growing up, their parents continually drilled into their heads statements like, “If you can’t say something nice, don’t say anything at all!”

“Nerds,” on the other hand, have their tact filter positioned to apply tact in the incoming direction. Thus, whatever anyone says to them gets the appropriate amount of tact added when they hear it. This is because when nerds were growing up, they continually got picked on, and their parents continually drilled into their heads statements like, “They’re just saying those mean things because they’re jealous. They don’t really mean it.”

When normal people talk to each other, both people usually apply the appropriate amount of tact to everything they say, and no one’s feelings get hurt. When nerds talk to each other, both people usually apply the appropriate amount of tact to everything they hear, and no one’s feelings get hurt. However, when normal people talk to nerds, the nerds often get frustrated because the normal people seem to be dodging the real issues and not saying what they really mean. Worse yet, when nerds talk to normal people, the normal people’s feelings often get hurt because the nerds don’t apply tact, assuming the normal person will take their blunt statements and apply whatever tact is necessary.

So, nerds need to understand that normal people have to apply tact to everything they say; they become really uncomfortable if they can’t do this. Normal people need to understand that despite the fact that nerds are usually tactless, things they say are almost never meant personally and shouldn’t be taken that way. Both types of people need to be extra patient when dealing with someone whose tact filter is backwards relative to their own.

Copyright © 1996, 2006 by Jeff Bigler.
Permission is granted to redistribute this text in its entirety, provided that this copyright notice and either the URL for the page ( or a link to it is included. All other rights reserved.

Filed under: Tact

The Exorcist

Demon: What an excellent day for an exorcism.

Fr Karras: You would like that?

Demon: Intensely.

Fr Karras: But wouldn’t that drive you out of Regan?

Demon: It would bring us together.

Fr Karras: You and Regan?

Demon: You and us.

The most chilling exchange in the movie. Particularly if you’re familiar with how it ends.

The lede image to this post is the inspiration for the scene of Fr. Merrin arriving at the house, René Magritte - The Empire of Light, II.

Filed under: Quote


Unless you know me well personally, you may not realize that I’m a nerd for ghost stories. Big time.

That particular topic is for another time, though. This post is to celebrate the spooky season with one of my favorite ghost stories, Smee by A.M. Burrage.

Set during a Christmas Eve, it’s a delightful look at an innocent game of hide-and-seek called “Smee” (a portmanteau of “It’s” and “Me”) that includes an unexpected player for the evening. As with the best short ghost stories, the supernatural is casual and softly haunting; not as vulgar jump scares or “gotcha!” moments of cheap thrills. (The best of this post-facto type of uneasiness is likely Afterward by Edith Wharton.)


Good Design

With deference to Dieter Rams (and courtesy of Vitsoe), his 10 principles for good design. This list is worth revisiting every now and then.

Good design is innovative

The possibilities for innovation are not, by any means, exhausted. Technological development is always offering new opportunities for innovative design. But innovative design always develops in tandem with innovative technology, and can never be an end in itself.

Good design makes a product useful

A product is bought to be used. It has to satisfy not only functional, but also psychological and aesthetic criteria. Good design emphasizes the usefulness of a product whilst disregarding anything that could detract from it.

Good design is aesthetic

The aesthetic quality of a product is integral to its usefulness because products we use every day affect our person and our well-being. But only well-executed objects can be beautiful.

Good design makes a product understandable

It clarifies the product’s structure. Better still, it can make the product talk. At best, it is self-explanatory.

Good design is unobtrusive

Products fulfilling a purpose are like tools. They are neither decorative objects nor works of art. Their design should therefore be both neutral and restrained, to leave room for the user’s self-expression.

Good design is honest

It does not make a product more innovative, powerful or valuable than it really is. It does not attempt to manipulate the consumer with promises that cannot be kept.

Good design is long-lasting

It avoids being fashionable and therefore never appears antiquated. Unlike fashionable design, it lasts many years – even in today’s throwaway society.

Good design is thorough down to the last detail

Nothing must be arbitrary or left to chance. Care and accuracy in the design process show respect towards the user.

Good design is environmentally-friendly

Design makes an important contribution to the preservation of the environment. It conserves resources and minimizes physical and visual pollution throughout the lifecycle of the product.

Good design is as little design as possible

Less, but better – because it concentrates on the essential aspects, and the products are not burdened with non-essentials.

Back to purity, back to simplicity.

These principles are available licensed under CC-BY-NC-ND 4.0 from Vitsoe.

Filed under: design

Ubuntu SSH Ads (motd)

Much to the chagrin of my friend darix, I run an Ubuntu server at home for various things like Nextcloud and Plex. (Chagrin because it’s not openSUSE… yet.) I had installed the previous Long Term Support (LTS) version, 16.04 because I just need the thing to run and be solid. The time had come for a new LTS release and I figured I might as well sit down and get upgraded to 18.04.

Mostly you hear from people when things go wrong, and this time I thought it’d be nice to say something from a positive view. I upgraded over SSH and everything went super smoothly. Awesome! There was one small hiccup with SSH not being enabled when I rebooted the first time but it was quickly sorted out.

The funnier part was some weird news updates being shown in my Message of the Day (motd) when I logged into the machine. (update: user on Mastodon found this LWN article about it by Jake Edge back in 2017.) I think I’ve seen a few people calling them “ads”, but really what happens is the motd scripts pull news from a URL to show you. Here’s what mine looked like:

Welcome to Ubuntu 18.04.1 LTS (GNU/Linux 4.15.0-32-generic x86_64)

Filesystem      Size  Used Avail Use% Mounted on
/dev/sda2       913G  745G  122G  86% /

 * Read about Ubuntu updates for L1 Terminal Fault Vulnerabilities


 * Check out 6 great IDEs now available on Ubuntu. There may even be
   something worthwhile there for those crazy EMACS fans ;)


 * Canonical Livepatch is available for installation.
   - Reduce system reboots and improve kernel security. Activate at:

0 packages can be updated.
0 updates are security updates.

The issue some folks have is with the first couple of bullet points (and a link? Really?).

Taking a look in /etc/update-motd.d/50-motd-news and right at the top of the file Dustin was kind enough to tell us he insisted on transparency for this:

# This program could be rewritten in C or Golang for faster performance.
# Or it could be rewritten in Python or another higher level language
# for more modularity.
# However, I've insisted on shell here for transparency!
#                                                                     - Dustin

Remove the News

Also at the top of that file is the clue on fixing this for your installation. The key is to modify the file /etc/default/motd-news and change ENABLED=1 to ENABLED=0:

# Enable/disable the dynamic MOTD news service
# This is a useful way to provide dynamic, informative
# information pertinent to the users and administrators
# of the local system

Et voilà!

Filed under: ubuntu, linux, ssh

Goodbye Google Analytics

I was outlining some ideas for an article I want to write concerning online privacy and connections to the recent Facebook fiasco. Part of the outline had me talking about the “middleman” layer that many big services like Facebook and Google created through their free offerings like ads, webfonts, comments, and social sharing options. I started mentioning this in a post from last year. I won’t go into that detail here (yet), but something that really struck home for me was the use of Google Analytics.

For those not in the know, Google Analytics is a way to track users on a website for reporting and insights. We used to do this manually by analyzing the logs from the webserver. Then Google made it trivially easy to get great looking reports of the data automatically by simply including a small piece of JavaScript in our pages. It can even show this data in real time. Which is, as my friend Alex Prokoudine points out: “just jerking off” (*at least for small projects like this blog).

Dashboard view Dashboard view users

What this really boils down to is (and I’ll cover this in more depth in the article I alluded to): I’ve given Google direct access to all of my users and their browsing habits on my site.

Faust und Mephisto, Stich von Tony Johannot
Ok Faust, simply add this tiny snippet of JavaScript and you’ll get all of these wonderful analytics! Easy-peasy!

It’s insidious because it’s dead-simple to set up, and it gives you a nice little jolt of satisfaction watching your traffic flowing in (trickling, in my case). Mesmerizing. Cunning. Dirty.

This is not exactly news to many, but I figured it might be for a few people. If it helps raise awareness in even one person of what’s really going on then I’d say this post has done its job. I’d rather not give Google a hand tracking users across the entire web.

I’m done with Google Analytics.

So long, and thanks for all the fish.

Matomo website

There is a Free Software option for user analytics that we use over on PIXLS.US called Matomo (previously, Piwik) that I will continue to use here. This means that I control all of the data and don’t allow any middleman to see what you’re up to (well, me and one of the pixls sysadmins, andabata, who runs the service for us). I will also start manually analyzing my server logs offline when I need more accurate statistics to look at.

Stay tuned for something a little more in-depth where I ramble on more about tracking and the sneaky middle layer of snooping that these services have built up in the name of convenience and apathy.

Filed under: Google, privacy

Switching Themes

I got the bug recently to finally do something that’s been annoying me for some time: set up a dark theme for this website that is user selectable and that will persist.

Normally I prefer the white background (default) that I originally designed but I understand that some prefer a dark background. When it’s in the evening and I want to write/edit a new post (like I’m doing right now) so do I. When it’s daytime, though, I prefer to be back on the white background.

The Dark (Style)

Creating a dark version of the site is relatively easy. I originally styled things sparingly here to keep it light and simple.

The Dark by Lemony Snicket - I love reading this book to my kids (you should hear me do the voice for the dark).

With plenty of thanks to past-Pat for being proactive there aren’t many rules to change to make a dark theme work. Here they are:

body, hr:after {
    background-color: #222;
    color: #ccc;

h1, h2, h3, h4, h5 {
    color: #eee;

code, pre {
    background-color: #111;

That’s pretty much it. I set the body background-color and color to dark versions (the hr:after is for the new horizontal rules I added a few days ago).

The headings are slightly brighter than the body text (just like how they’re slightly darker in the light theme) and the code blocks use a darker background. The link text colors were tweaked from looking at the Solarized color scheme by Ethan Schoonover so they were already on the path to being used in either light or dark versions and didn’t need modification.

I went with a more neutral dark gray background vs. the slightly blue-tinted Solarized background because I didn’t want the background to clash with any photos I share.

Applying the Styles

The application of these styles was pretty straightforward. I had all of the main styles for the site load first (in the document <head>), where I set the dark theme as part of my default styles. Immediately after these default styles, I added a new <style> element that actually set the colors to the light theme version.

I did it this way because later I will talk about the JavaScript I use to switch the styles as well as add persistence for whatever a visitor chooses. This way if a user has JavaScript disabled they will get the light theme by default.

The pseudo code for my styles now looks like this:

    ... default styles for everything
    {dark theme rules}

<style id='lightcss'>
    {light theme rules}

This will make sense a little later.

Inverting Colors

As an aside, while looking around at others works to see if I was missing something, I came across an awesome method for creating a dark version of a website. It’s in the blog post A Theme Switcher over on the Inclusive Components site and it’s an elegant use of the invert filter.

The post is well worth a read, but the general gist is to use the invert filter to invert all of the colors on a site. They explicitly give a background color to the :root html element, and then trickle it down to all other elements on the page through the * selector (so you don’t end up with white text on a white background). A little selector magic to avoid images and videos and you have a very tersely implemented theme inversion!

Here is the final CSS they used:

:root { 
   background-color: #fefefe;
   filter: invert(100%);

* { 
   background-color: inherit;

img:not([src*=".svg"]), video {  
   filter: invert(100%);

Wonderful! I almost went this route as well, but my colors are not quite set to be how I’d like them as straight inversions. I will keep this in my bag of tricks for later though!

Theme Switching

I now needed a way to allow visitors to use this new theme. A small bit of JavaScript at the end of my pages takes care of enabling/disabling the default light theme colors:

    document.getElementById('theme').addEventListener("click", function() {
        light = document.getElementById('lightcss');
        if( light.disabled == true ){
            light.disabled = false;
            light.disabled = true;
            localStorage.setItem("theme", "dark");

I attach an event listener to a link on the page (see the “Theme” button in my header). Targeting the lightcss style element, we can toggle it through the disabled attribute. This might seem odd if you’ve done this before.

Normally you’d probably consider setting the dark/light rules and applying them to a class. Then you’d toggle them by adding/removing the classes. While this works great, it tends to be slightly problematic when used for applying stored user preferences…

The Persistence of Memory

You’ll notice in the previous JavaScript snippet the lines that set/remove a localStorage parameter theme. This allows me to remember what theme the user wants.

Persistence of Memory by Salvador Dali.

Another small piece of JavaScript in the <head> allows me to check for the setting:

if( localStorage.getItem('theme') == 'dark' ){
    document.getElementById("lightcss").disabled = true;

This could be done with cookies as well but many folks might have them turned off for security reasons.

This helps get around the problem of using a class to switch styles. If the setting is for the dark theme, it needs to be applied before the page has a chance to load (and show a flash of the white theme). Using the disabled attribute we can set (well, unset) these styles before the page has a chance to render.

Give It a Try

Whew! That was a mouthful. Well, go ahead and give it a try if you’d like! In my header under my name and main site links you should now find a “THEME” link that will swap things around for you. Let me know if anything seems wonky.

Filed under: HTML, CSS, theme

Installing Adobe Digital Editions on Linux with WINE

This post is a little more esoteric, but in the spirit of helping others from the future we’re gonna dive into this.

Wisdom of the Ancients
I’ll stop posting relevant xkcd comics when Randall stops making relevant xkcd comics…

I picked up an Amazon Kindle Paperwhite over a year ago, and I have been tearing through books at an alarming rate. In the course of finding more books to read I did what I’ve always done for as long as I can remember: visit my local library.

Side note: Please, please, please support your local libraries. It’s like supporting net-neutrality in meatspace!

I immediately ran into a bit of a disconnect between my rapidly advancing digital life and the reality of digital books.

The problem lies in how we can borrow and checkout digital books. Currently, there are mainly two big systems in place for digital library lending, Bibliotheca’s CloudLibrary and Rakuten Overdrive. As is often the case in modern computing, anything not Apple or Microsoft is relegated to the rubbish bin. As a Linux user, this is not ideal for me.

Neither of these software solutions have a Linux option.

Wine Is Not an Emulator

Luckily for Linux users, there’s the project to run Windows applications on various POSIX-compliant operating systems: Wine.

I won’t go into using CloudLibrary, as it’s not the focus of this post. It actually installs just fine all on it’s own in Wine so it’s a non-issue.

Overdrive is the real problem here. Rather, it’s use of Adobe Digital Editions (ADE) as the means for checking out books. (The actual interface for Overdrive at the library is a website.) For other purposes that I won’t go into here right now, I need to be able to use an older version of ADE. Specifically ADE 2.0.1.

I spent some time researching if the application would work over at the App Database for Wine. The instructions there didn’t really work for me, but there were some other posts in places like the MobileRead wiki where I was able to gain some more insights as well. Overall it took a good few hours of searching and playing to finally arrive at a working installation of ADE 2.0.1 with Wine on Linux.

If you’re still reading this far I can only assume that you need to get this installed as well. Let’s do it!


I have successfully installed ADE 2.0.1 on a fresh installation of Ubuntu 18.04. You’ll need to be comfortable in your shell to do this, no graphical installer for these steps (sorry).

To start, you’ll need a couple of downloads for later:

I just downloaded these directly into my home directory for easy reference later.

You’ll also need to install Wine. I just went ahead and used the package from my distro: wine-3.0 (Ubuntu 3.0-1ubuntu1). You’ll also want a helper script for setting things up automatically in Wine called winetricks, and I also needed cabextract to be able to extract files from a Windows .cab file.

In my case I just did:

$ sudo apt install wine-stable winetricks cabextract

Wine installs everything into it’s own directories that you tell it to (referred to as a prefix). You can easily tell it which prefix to use by setting this in your shell before you run a command. In my examples below I will be installing ADE into it’s own prefix in my home directory called ~/.adewine.

  1. Set up a new 32-bit prefix with Wine.
    You need to setup a fresh install into it’s own prefix, and tell Wine to make it a 32-bit environment:

     $ WINEPREFIX=~/.adewine WINEARCH=win32 winecfg  

    You can also tell Wine to use this prefix for all subsequent commands:

     $ export WINEPREFIX=~/.adewine
  2. Now you need to install corefonts and windowscodecs:

     $ winetricks -q corefonts && winetricks -q windowscodecs

    At this point, I originally found some errors where Wine wasn’t picking up the windowscodecs.dll being installed. I was seeing a bunch of errors like:

    000d:err:module:import_dll Library windowscodecs.dll (which is needed by L"C:\\windows\\system32\\winemenubuilder.exe") not found

    I found that I could fix this by manually copying them to the correct location:

    1. Extract all the files from ~/.cache/winetricks/windowscodecs/wic_x86_enu.exe
       $ cd ~/.cache/winetricks/windowscodecs/
       $ cabextract -d tmp wic_x86_enu.exe
    2. Copy windowscodecs.dll and windowscodecsext.dll into ~/.adewine/drive_c/windows/system32/:
      $ cd tmp/
      $ cp windowscodecs* ~/.adewine/drive_c/windows/system32/
      $ cd ~/
  3. Install the Full .NET 3.5SP1 installer you downloaded previously:

     $ wine dotnetfx35.exe
  4. Finally, you can install Adobe Digital Editions 2.0.1 normally:

     $ wine ADE_2.0_Installer.exe

Et voilà!

That’s it! If there were no errors you should now be able to run Adobe Digital Editions 2.0.1:

$ WINEPREFIX=~/.adewine wine .adewine/drive_c/Program\ Files/Adobe/Adobe\ Digital\ Editions\ 2.0/DigitalEditions.exe
Adobe Digital Editions 2.0.1 in Wine

Update (Oct 2018)

Reader Frei pointed out some results he got from using Manjaro/Arch (rolling release 10/2018):

err:winediag:gnutls_initialize failed to load libgnutls, no support for encryption

So he installed lib32-gnutls before to get it working.

He also had a problem with getting the manual download of dotnet working (it didn’t complete), so he managed to get things installed with:

winetricks -q dotnet35sp1

Filed under: Linux, Wine, ebooks

← Older Posts