Thursday, August 30, 2018

lifehack: browser or mac voice synth as PT coach timer

Giving computers a voice has been since the 80s - I remember S.A.M. (Software Automatic Mouth) on my Commodore 64, SBTalker on PCs, and Macs have had it for a while - in OSX you can open up a terminal and type "say whatever".  (Plus there's a variety of voices and accents you can download and have fun with)

It turns out most browsers, including on iPhone, have the same ability:

I thought of this when thinking I'd like a soundtrack to do some Physical Therapy exercises against - a simple, flexible, countdown timer, so I can do the simple reps of "do this, hold for ten seconds, repeat" and not lose track.

I started developing this idea on the Mac using the "say" command - that had the option to insert "[[slnc 1000]]" commands to pause for one second (1000 ms), but it turns out that special command wasn't universal across browsers on different OSes, so I ended up having to build a better UI and managing the time gaps manually - you can see the results here at my Customizable PT Reps Vocalizer

Tuesday, August 21, 2018

organic "is this what you were looking for?" by google lyrics

It's a little sketchy (in a "is this a monopoly abusing its power" kind of way) that Google made song lyric lookup a built-in, don't-have-to-go-the-site-providing-the-information. One thing I noticed as a little irritating is that it always shows you like a lot of lyrics, 1/3 to 1/2 but makes you click to see it all. I guess this has two advantages: one is you can still see the actual web results beneath when the page loads, but more importantly, if someone expands the preview it is a confirmation that the result was a good hit for the search terms used. (I understand this is UI/UX/SEO 101 stuff, but still I hadn't framed it that way for myself before.)

Monday, August 20, 2018

10:1 rule

I've often wondered why programmers are so bad at estimating the time and effort needed for a task. (An old Product Manager friend of mine used to say "ask a programmer for a pessimistic estimate and then multiply that time by two.) Yevgeniy Brikman offers one possible explanation: The 10:1 rule of writing and programming. Looking at git checkins for both his books and some popular software projects, he sees each line being rewritten at least 10 times, statistically speaking! And an update at the bottom of the article points out forum talk saying similar guesstimates of 10:1 apply in film, journalism, music, and photography.

Tuesday, August 7, 2018

manipulating pdf files (and playing jr. sysadmin)

I've never been much of a sysadmin, despite being my own webmaster for decades.

For a sheet music library sideproject I've been on, I've wanted to do three things with PDF sheet music files: split (for when the user uploaded a single file with different pages for all instruments), merge (to put them back when a page might overrun it) and extract contents as text (to try and take a guess what instrument the page is for)

The first thing I found was PDFtk Server. To download it, I had to figure out which linux version I needed; Red Hat or CentOS, Linux 5 or 6, 32-bit or 64-bit. The first two were answered with
cat /etc/redhat-release
(CentOS 6) and the last with
uname -m
(64 bit unsurprisingly)

At that point I could follow the install instructions using a lot of "yum".

The man pages for pdftk indicate the syntax is a bit un-Unix-y. The call splitting "burst", and the syntax is
pdftk BIGFILE.PDF burst
which by default makes pg_0001.pdf etc.

Merging is called concatenation but that's sort of the default behavior for pdftk, so combining page 1 and 2 for example would be
pdftk pg_0001.pdf pg_0002.pdf output COMBINEDFILE.PDF

You can try using a regex to extract text data, but I wanted a proper command. I turned to
pdftotext which is part of poppler-utils -
yum install poppler-utils

That gave me pdftotext and I could do
which would make WHATEVERFILE.txt, or I could do
pdftotext WHATEVERFILE.txt  -
which would dump to STDOUT.

So, one weird thing is I thought poppler-utils was supposed to come with pdfunite and pdfseparate which mean I could have skipped the pdftk stuff, but for some reason those weren't included, and since I already could do what I needed with pdftk, I decided to give it a miss.

Sunday, August 5, 2018

idiosyncratic css hacks for idiosyncratic blogs (photogallery previews)

I've been running my blog for almost two decades. It's a good example of the old DIY web, powered by a bunch of flatfiles in folders and with a mix of Perl (later PHP) to keep it all together. I love the control handcoding everything offers me and what a deep archive of my life online and off it represents (it's awfully cool to be able to dredge up a photo or half-remembered quote from any browser at any time) but I have to build all its features more or less from scratch, including image uploads, its by-month archive, its tag cloud, and its "On This Day" view.

I've grown to like having my photo galleries in-line as blog content, vs making up special pages for them - for instance I did a series of best digital photos of the year going all the way back to 1996. (One lesson I learned too late is that since its emerged as my main archive, I should have always been putting fullsize versions of photos as links behind the resized-for-web versions. I do that now but stuff from the old days is often hard to find in its full pixel glory...)

So I have these pages, but sometimes when a big gallery page shows up in an archive view, it's disruptive, you have to scroll and scroll and scroll to get through it all and onto the next thing.

I didn't want to have to do a lot of work to update the existing galleries, so I made up the following HTML to paste into each set of photos (followed by the closing div)
  <h2 style="cursor:pointer;" class="hidden-gallery-clicker" onclick="var gal = this.nextElementSibling; = == '' ? 'none' : '';">Click for Photo Gallery</h2>
<div class="hidden-gallery" style="display:none;">

That should be pretty clear to any web coder, the gallery was hidden, and then there was a "Click for Photo Gallery" header that would toggle the visibility of the Gallery.

But... that kind of stinks from a UX standpoint, and general visible appeal. Having a little <h2> tag is not much of an affordance at all the terrific photo content awaits.

I realized I'd prefer tiny thumbnails of all the photos, hiding any extra caption information, but when you open the gallery, all the images show up at their browsable size.

And like I said, I didn't want to have to go hand edit a lot of HTML to do this, so whatever trick I used needed to be pure CSS and maybe a smidge of JS that could live in a single place. But my HTML for the photos was sloppy, I had lots of text nodes outside of separate divs or span tags.

Here's the CSS I came up:
.minishot-gallery {
  border:1px dashed #999;
.minishot-gallery * {

.minishot-gallery a, .minishot-gallery figure {
.minishot-gallery img {
  display: inline-block;
.minishot-gallery .minishot-gallery-caption {
  display: block;

.minishot-gallery-caption {
    display: none;   
.minishot-gallery .minishot-gallery-caption {
    display: block;   


The "display:none" for * was the first step, and changing the font size to zero does a good job of hiding the freefloating text. Images are loaded but displayed at 100px high with width to match. I had to let <a> tags display since most of my images are wrapped in those (the link to the true fullsize versions) and then I was also messing around with using the oddball <figure> tag.

The HTML snippet is then:
<div class="minishot-gallery" onclick="if(this.classList.contains('minishot-gallery')) { this.classList.remove('minishot-gallery'); return false; }">
<h2 class="minishot-gallery-caption">Open Photo Gallery</h2>

That's just a bit of JS saying to remove the minishot-gallery class (which in a neat step gets rid of all the weirdness) I had to be careful with the return value so the first click would open it, but clicks on the images afterwards wouldn't be eaten. There were a lot of nooks and crannies to this kind of hackery, but I'm pleased with the end result.

Of course, I'm mighty pleased with my "best of" photo galleries anyway! Maybe it's too bad I now spend most of my photographic creativity on "One Second Everyday"...

UPDATE: I realized for large galleries this was poor UI - if you click on an image later in the set, you expect to see the image you just clicked on, not the top of the gallery.  So the script now uses "scrollIntoView()" (and I got rid of the inline onclick)

<div class="minishot-gallery">
<h2 class="minishot-gallery-caption">Open Photo Gallery</h2>
document.addEventListener("DOMContentLoaded", function(event) { 
    var galleries = document.querySelectorAll(".minishot-gallery img");, function(el, i){
        el.addEventListener("click", function(event) {
            if(el.classList.contains('minishot-gallery')) { el.classList.remove('minishot-gallery'); return false; } return true;

    var elements = document.querySelectorAll(".minishot-gallery img");, function(el, i){
        el.addEventListener("click", function(event) {
            let gallery = el.closest('.minishot-gallery');
            if (gallery) {
And obviously you might consider putting that script tag somewhere so it's just called once, even if there are multiple galleries open.

the economic transitions of putting stuff on the web

Two related pieces: one is Alex Singh's tweethread on the transition of the web from independent hangouts to the walled-gardens most of us hang out in today:
Over the past 25 years, the web appears to have transitioned from a primarily nomadic culture to a mostly agrarian one, mirroring the Neolithic Revolution 10,000 years ago.
(The whole thing is just like five tweets)
The second is Nick Heer on The Bullshit Web, where a 1998 modem-based system might download a basic news article in ten to twenty seconds, and a 2018 article over a blazing fast connection might take about the same amount of time - and how Google is offering AMP as a system, but it's getting a huge benefit of keeping eyeballs in Googlespace merely by offering a forced respite form the extraneous file crap.
I take pride in keeping up my idiosyncratic blog over almost two decades, and how the side projects I'm on are largely clear of the BS. I know I tend to build too much from scratch, and other developers get some amazing results using Ruby-on-Rails-style packages, but that is kind of the path to the BS Web as well... the people who are on the "Buy, Always" side of "Make vs Buy" may or may not have good understanding of the tech they employ... I'm too uptight about being left stranded if I make a bug in the infrastructure and don't really get how the magic is working to diagnose it well.

(UPDATED: a more backend look at the Bullshit Web in The Cost of Javascript and some techniques to diminish the impact)

Friday, August 3, 2018

the crushing equalizing of modern social mediums

Mike Monteiro writing on dealing with his depression and social media:
Like a ton of people, I have to deal with it [...] One of the warning signs for me is when I can’t tell the difference between a big problem and a small problem. My brain stops prioritizing. Every problem comes at me at exactly the same size. This is depression taking away a major coping mechanism. And that’s exactly what was happening on Twitter. Every outrage was becoming the exact same size. Whether it was a US president declaring war on a foreign nation, or an actor not wearing the proper shade of a designated color to an awards ceremony. On Twitter those problems become exactly the same size. They receive the same amount of outrage. They’re presented identically. They’re just as big as one another. Twitter works like a giant depressed brain. It can’t tell right from wrong, and it can’t tell big from small. It needs help.
I was struck with the parallels to this criticism of Facebook:
The problem, says Lanier, is that there is nothing special about humans in this information system. Every data point is treated equally, irrespective of how humans experience it. “Jew haters” is just as much an ad category as “Moms who jog.” It’s all data. If Group A has a bigger presence on Facebook than Group B, so be it, even if Group A is trying to demean or organize violence against the Bs. Of course, the reality is that humans are all different, and cannot be reduced to data.
Or as it also says, "To Facebook, the world is not made up of individuals, but of connections between them."

Tremendous problems. I'm not sure what a way of addressing the problem of scale (this is a minor thing, this a large thing) or morality would look like without being censorship. If there's any piece of UX that could help this.