Thursday, October 17, 2019

Wednesday, October 16, 2019

flexbox reference

Huh. A Complete Guide to Flexbox has lost some of its googlejuice, apparently, with w3school's boring, less visual, and less helpful guide claiming the top spot. So I'm putting the link here for my future reference...

Tuesday, October 15, 2019

filed away

Simon Pitt writing on "Computer Files Are Going Extinct" (or "The Death of the Computer File.doc") had this nice line:
Years ago websites were made of files; now they are made of dependencies.
I would nitpick and say "files plus browsers and maybe a scripting language", but I think Pitt points to  a major sea change. For me, coding is still about files - nouns - and then very well tested trustworthy verbs of the browser itself, and not too many intermediaries. Now coding is so much verb, so much process, and younger programmers put raw DOM into the same "low level stuff I don't need to think about much" level as an older CGI hacker might put assembly language.

The rest of the article was kind of delightful for us old-timers, reviewing how services and streams and what not have replaced quasi-physical files.

And I think that's a bummer. I'm currently reading Barbara Tversky's "Mind in Motion: How Action Shapes Thought". It really emphasizes how there's a physicality to our thoughts, that we turn to metaphors of space and motion all the time to make sense of our experiential data - and with files and folders, there was a physical nature to our filesystems as well. I kind of hate the iPhone's "all your photos as a big stream" approach to life, or using "smart folders" to take a giant heap of information. Folders can be clutzy and coarse grained, but there was a flexibility and sense of stability to them that the modern replacements lack.

this code changed everything

Nice piece from Slate, The 36 Bits of Software That Changed Our Lives (Of course, a casual geek might wonder if that should be 32-Bit, but 18- and 36-bit machines were a thing back in the day...)

Saturday, October 12, 2019

exif continues to be a hot mess

Years ago I found an ImageMagick fix to rotating images shot on iPhones: something as elegant as
mogrify -auto-orient imagename.jpg
did the trick.

iOS recently added an option for web uploads form to use a smaller image size - which can save a lot of bandwidth, since often you don't need an image in its full multi-megapixel splendor. But apparently if you do that (as I tried with the first image I posted on my commonplace blog) the "-auto-orient" I was relying on gets confused. (I repeated the process with the full image it was fine.)

I haven't really researched this yet, but I suspect the Exif information - explaining how the image was shot rotated - is preserved for the smaller image, but the image data already has the appropriate rotation applied.

I know I'm not the only one wrestling with this kind of thing - here's an article on The dumb reason your fancy Computer Vision app isn’t working: Exif Orientation. As it says:
Exif metadata is not a native part of the Jpeg file format. It was an afterthought taken from the TIFF file format and tacked onto the Jpeg file format much later. This maintained backwards compatibility with old image viewers, but it meant that some programs never bothered to parse Exif data.
But worse than that, right now I'm not convinced there's a flag that says "this rotation data is for historical purposes only" vs "this rotation data is needed for proper display of the image" - or if there is, iOS and (my admittedly old install of) ImageMagick don't agree what that is.

Wednesday, October 9, 2019

context is strawberries

Every once in a while I get a notice from my old computer science department telling me I need to reset my password. To be honest, ssh'ing to reset the password is about the only thing I do with the account, and if I was less nostalgic I would just let it go. (Til then: enjoy visiting and see the web like it used to be, in the mid '90s...)

One of the funniest quirks from my old account is that once upon a time I replaced the regular "more" comand with an alias to this little script:


$target = $ARGV[0];

if(-d $target) {
exec "ls -F $target";
} else {
exec "less $target";

That's... kind of charming? Probably I kept typing "more directoryname" when I meant to "ls directoryname" ... so I made "more" into a generalized "view the contents of this", which makes a certain kind of sense but is a little weird...

Monday, October 7, 2019

converting HEIC to JPG on Mac easily

iOS tends to internally store photos as "HEIC". a more modern, space-efficient format. Usually you don't notice, and most ways of transferring files off of the device automagically converts the image into more standard things like JPG, but AirDrop tends to keep it as HEIC. This page outlines how to use the Automator to set up a "Quick Action" to make a copy of the file and convert it - then there will be a context menu option for your files to do the conversion.

I tend to do all temporary file work - from screenshots to where Airdrop puts things - in "Downloads" rather than the Desktop. (I don't know about you but my Desktop usually is covered with windows...) And I then actually go one more step and make it so Automator then drops the " copy" from the filename- I had to wrestle with Automator a bit but this setup finally got me the results I wanted:

Thursday, October 3, 2019

typeface forward

A nice BBC "bitesize" on The history behind some famous typefaces.

I'm sometimes torn between being an engineer and a designer, but my face blindness leaves makes appreciation the nuance of typefaces a tougher road to hoe, so I'm usually ok with serif, sans-serif, some kind of Impact-ish font, and something Courier-ish.

Tuesday, October 1, 2019

Monday, September 30, 2019

awesome inline DOM content editng

Whoa.... (in case the tweet goes away: in most browsers you can go into the console and type document.designMode = 'on'; and then edit text directly in the document. Great for pranks as well as seeing how the page reacts to different text lengths etc.

Friday, September 27, 2019

cargurus and ownership and iterations

From my company's engineering blog, Why devs should embrace project ownership and swift iteration.

The essay is legit, and I would say that if you're A. looking to buy a car, we're the best place to start (CarGurus became #1 in a crowd of sites who will help you find a new or used car in the area by being the first to not be afraid of calling a car overpriced when that was the case) or B. if you want a job in Cambridge - either tech / engineering, or in sales / getting dealers on board (or whatever listings are on it - hit me up, for reals, it's a great place.

Tuesday, September 24, 2019

autocompletely stupid

Could someone explain this to me? I want to look up the town of "Ocean Grove, NJ" (where my mom and my Aunt live... I'm trying to compute its distance to the infamous closed waterpark "Action Park")... I pop open Apple maps on my phone (for some reason on my desktop, Google Maps can't compute the distance.)

So I pop open the map and see...

Ok, typing an "o" looks promising, like it already knows what I might want to say... (well, except for the "oarking" bit, probably an old typo of mine?)

"oc" is even better...

and then "oce" gets me back to the crap I've never searched for in my life.

I'm trying to get a feel for what the algorithm thinks it's doing here... why getting more specific makes it thinks the more-local-to-me-now is now more important than the something-that-I-searched-for-in-the-past...

Monday, September 23, 2019

virus vs virus

I've been thinking about an old post on hacker Andrew "bunnie" Huang's blog, looking at the information density of human viruses. My favorite quote from it:
[on analyzing H1N1 in terms of bits of information] So it takes about 25 kilobits -- 3.2 kbytes -- of data to code for a virus that has a non-trivial chance of killing a human. This is more efficient than a computer virus, such as MyDoom, which rings in at around 22 kbytes.
It's humbling that I could be killed by 3.2kbytes of genetic data. Then again, with 850 Mbytes of data in my genome, there's bound to be an exploit or two.
That's just a neat way of thinking about it... it seems amazing that you can quantize information like that.

Sunday, September 22, 2019

Sunday, September 8, 2019

the 32767px hack

There was a time, a decade ago, where using <table> for layout was reviled as horrendously poor form (and obviously it's semantically messy, and puts too much display information in the html markup) but CSS and proper html couldn't yet simply do what was simple to do in tables. But: Tables were pretty good at letting you indicate how you wanted unknown sized content to sit relative to other content. Not until flexbox and css grid layout appeared, really.

In those days,  doing something like - getting multiple column-y divs to be as tall as each other without knowing how tall their content was tricky, this GIF shows the challenge:

One solution was the Equal Height Columns Hack where you would - I'm not kidding - add 32767 pixels worth of padding at the bottom, but then undo that with the negative equal value of margin.

I was kind of bitter. This kind of thing wouldn't even register as an challenge for a table-based layout - and using a table seemed MUCH less horrifying than this 32767 pixel garbage. (Apparently the number was empirically derived as large but not too large for most browsers). And really, is having an endless pile of nested <div> that much worse than non-data <table>?  But I'm a hopelessly out of touch caveman for thinking so? OK.

I'm over it now, and am delighted CSS provides solid options that are semantically good. But overall I will always err on the side of pragmatic simplicity over semantic purity.

(Here's a similar grumbling blog entry from 2012 - using floating divs was another solution for solving problems caused by abstaining from tables)

Friday, September 6, 2019

letsencrypt is so so good

Just a public note of admiration for Let's Encrypt - getting SSL for a site used to be a weird and costly pain in the ass, but they make it so easy (at least if you have command line access to your webhost)

Monday, September 2, 2019

tightly packing uneven squares via CSS (or js)

Like I've mentioned One of the toughest part of Porchfests is making the printable guide. Although I wish everyone would just use my groovy mobile sites, apparently many people feel more comfortable with a piece of paper - but it's a lot of information to try to pack into just a few sheets.

For JP Porchfest I used a block schedule:

However, Belmont Porchfest tends to have only one band per porch, so this kind of layout wastes a lot of space.

Also... just having band names isn't that appealing to be honest. What if we could get the band descriptions in there as well? Would that make a more appealing display? Let people really get a feel for what they want to see?

I tried using inline-block and then float, and got something like this:
It wasn't quite what I had in mind... I'd really love something to make better use of the space.

This stackoverflow question pointed me to - their default "masonry" layout provided me something like
That seems much more likely to make a better use of space!

the poetry of technology

I found this Hugo Williams poem I blogged a long while back:
I phoned from time to time, to see if she's
changed the music on her answerphone.
'Tell me in two words,' goes the recording,
'what you were going to tell in a thousand.' 
I peer into that thought, like peering out
to sea at night, hearing the sound of
waves breaking on the rocks, knowing she
is there, listening, waiting for me to speak. 
Once in a while she'll pick up the phone
and her voice sings to me out of the past.
The hair on the back of my neck stands up
as I catch her smell for a second.
at the time I wrote how I loved that message she used. but now I'm struck what a beautiful, poetic, dramatic technology we had for... what, 20-30 years? From 1975 to 2005? The idea of screening calls live, the plaintive "if you're there, pick up... please pick up"... we've lost that! Arguably though it's part of the whole trend away from voice...

Tuesday, August 27, 2019

the computers of our lives

Been on a slight computer nostalgia kick.

A while back I made a list of all the computers, PDAs, and phones I had had (but never went to update that for iPads, come to think of it.)

In talking about this kind of stuff on the Lost in Mobile WhatsApp group, I got to thinking about Psions- more popular in the UK than the USA, they were these powerful clamshell baby computers. The Psion Series 5mx was pretty amazing. More so than the PalmPilot I was using around the same era.

Monday, August 26, 2019


I think about this ad, sometimes, about how an iphone/smartphone replaces all these devices...
sometimes the most amazing part is like 3000 songs I love, on me at all times. But a 2001 iPod could do that more or less. And is that more or less impressive than streaming, which turns your phone into a radio, albeit one tuned into a custom radio station just for you....

See also Everything From This 1991 Radio Shack Ad You Can Now Do With Your Phone

Monday, August 19, 2019

the social network graveyard

Why These Social Networks Failed So Badly - myspace and vine are the ones I think are the biggest losses. Along with the blogosphere in general.

Tuesday, August 13, 2019

meanwhile, at a place of business...

Coding for Fun and the Culture of Learning I made an entry for my company's engineering blog, about the fun of old 8-bit computers, the comapany's Peer-led classes, and the fun of programming stuff in Processing and p5.js.

I haven't written post there before this, in part because I'm less comfortable speaking in the authoritative voice I think an enterprise blog would need - my style is much friendlier and "here's what I found out, let me know if it's useful".

Monday, August 12, 2019

the email gun

I just finished the Emily St. John Mandel's post-apocalyptic novel  "Station Eleven". One recurring trope is how non-dark screens for computers or phones are just a memory-- I enjoyed this passage:
"Why did we always say we were going to shoot emails?"
"I don't know. I've wondered that too."
"Why couldn't we just say we were going to send them? We were just pressing a button, were we not?"
"Not even a real button. A picture of a button on a screen."
"Yes, that's exactly what I'm talking about."
"There was not, in fact, an email gun. Although that would've been nice. I would've preferred that."
...I recorded a few more quotes from the book in my commonplace blog.

Email guns are the best gun idea I've heard since the videogame Saints Row IV's Dubstep Gun:

Friday, August 9, 2019

on privacy and adblockers

At work someone posted a piece from the EFF, Adblocking: How About Nah? Talking about the arms race between users who want to see stuff online and resent the privacy violations ad providers utilize and the content providers and the advertisers. "How About Nah?" is the message of a consumer using an Adblocker to the site that wants them to accept the ads along with the content.

My responses were as follows:
Two thoughts: 
First is, one example of an “adversarial interoperability” are botnets that pretend to suck down content and dutifuly view the ads  - or sometimes just watching the ads themselves. If you’ve ever wondered how that Captcha of “I am not a robot” works when it’s just a checkbox - it is tons and tons of data scraping, from if your mouse movement is following human-ish patterns to IP locations and the like. One thing I learned working for adserving platforms is how there is rampant number abuse, and a completely unfair landscape for anyone trying to set up an “honest” ad service - it has to look competitive with completely inflated numbers from the other folks services 
The other is, “How About Nah” has a lot of swagger, but I think a more relevant concept - one the article doesn’t touch on - is “information wants to be free, but rent wants to be paid”. So much of this springs from the 90s establishing how people will embrace crappy and free before clean and cheap. 
I can’t blame people - they are rightfully nervous about how a penny here, a 1/10 penny there might add up. I think the first iPhone succeeded hinged on its plan from AT+T featuring all you can eat data - (which given networks speeds wasn’t all that much :-D)  - but if people made the move from the hell of WAP (infamous for nickel and dime-ing folks) to the real Internet on the go, they needed some reassurance they weren’t going to get hosed on data fees. 
Personally, I have a moderate stance on privacy - instinctively I don’t care that much as long as I know my value is only as a demographic’d consumer, and I don’t like that the conversation doesn’t distinguish between truly invasive privacy stuff (like humans seeing my content I thought was private) and more run of the mill stuff — or from people who take the “it’s my computer and my bandwidth, how dare those hucksters steal it” while ignoring the costs and salaries that might have gone into getting the content up that brought the person there in the first place

Wednesday, August 7, 2019

js: on the conditional deleting/removal of things

Making up my p5 "good parts" guide I noticed that a version of the same problem that haunted me in the Java days (safely removing an object from a collection) is still sort of around-
if I have, say, a collection (either an array or an object I'm treating as a hashmap) of key/value objects and I want to remove particular ones, what does that look like?

Probably the cleanest way in modern JS is to set the array equal to a filtered clone of itself:
let arraythings = [
 arraythings = arraythings.filter((thing)=> (thing.msg !== 'killme') ); 

and if that collection were an object:

let things = {
  'a':{msg:'ok1'}, 'b':{msg:'killme'},'c':{msg:'ok2'},
things = Object.entries(things).reduce((acc, [key, value]) => {
    if (value.msg !== 'killme') {
        acc[key] = value;
    return acc;
}, {});

A coworker helped me with that, better approach that my idea of making an array of keys to kill and doing a foreach. But the reduce approach looks kind of ugly to me. I'm glad this situation doesn't come up very often, but I should probably work on being more fluent with reduce().

It is interesting that neither approach is super Funtional Programming-ish, and in both cases I am reassigning the collection variable (though it always seems weird to me that a const object is just guaranteeing that you're pointing to the same collection, not that the contents of said collection are in any way frozen... that's what Object.freeze() is for.

p5 reference: the "good parts" edition

UPDATE: Ben Moren made a great cheatsheet for beginners [My Mirror Here] that is a good complement to what I have here...

I recently taught a 2 hour class in programming for fun - "After Hours: Making Virtual Toys, Games and Art".  It was a fun chance to show people (both experienced developers and programming novices) how to use Processing and P5.js - and the P5 editor is the best online thing I've seen for learning and sharing.

Both P5 and Processing have great websites with lots of learning tools, and it is awesome how all of their reference pages come with a tiny, ready-to-run example. But, for example, the p5 reference page has dozens and dozens of functions and special variables - I think that is intimidating to a new user who might only need to know 10-20 commands or so to make a lot of cool stuff. I made this page to be just those functions and special variables I use again and again in my decade and a half of Processing and P5.  (Stuff that's marked "BONUS" isn't as crucial but can add in the fun.)

Start up:
  • setup() - make this function and it runs once, at he beginning
  • createCanvas() - call this in setup() to set the width and height of your canvas
  • BONUS:  preload() - if you need to load images etc and don't want to start until its loaded, you should know about making this function...
Your main loop:
  • draw() - make this function and it gets called every clock tick
  • background() - many times your routine will start with this to erase the background (see the coloring things section below, and remember you can even set the transparency value for a cool ghost effect.)
  • width and height - often as you draw it's good to check these special variables and scale things properly
  • BONUS: call noLoop() if you don't actually need to loop - that can save device battery! And then call loop() if you change your mind
  • EXTRA BONUS: frameCount is a variable telling you the current frame number... sometimes good to setup a score time or somesuch if the goal is to stay alive as long as possible...
  • EXTRA EXTRA BONUS: saveCanvas() makes it super easy to save a snapshot of what's on your canvas... just be careful if you're doing it again and again and again inside your draw() loop!
Things to draw:

It's probably easy for non-programmers to forget how weird it is that for x,y coordinates, 0,0 is the top left corner, and 100,100 (or whatever the Canvas size is) is the bottom right... that applies to all of these commands:

Coloring things and pen widths:

The p5.js color system is a little odd but simple and fun once you get it... functions like background() and fill() and stroke() can take 1 argument (a number from 0 (black) to 255 (white)), 3 arguments (3 numbers from 0-255 for Red, Green, Blue), and then for either of those you can add an EXTRA argument to say how transparent it is (0 = it's invisible! 255 = it's fully opaque)
Interact-y stuff (mouse and keyboard):
BONUS Content:
function draw() {
  if(mouseIsPressed) line(pmouseX,pmouseY,mouseX,mouseY);
is like the world's simplest paint program!

Mathy Mathy Math
  • random() - often for generative art, it's nice to have a random number...
  • dist() - Pythagorean theorem - how far apart are two points? (useful to know if two circles overlap, say)
  • map() - if you want to take a number that might be in a range from 0 to 1 and change it to a range from 0 to 100, map() is your friend
  • cos() / sin() - sometimes you need to remember your old Geometry class...
  • BONUS: constrain() - limit a value to a range
External graphics

 Because I'm lazy, I often prefer to make games and toys with the shape drawing stuff, but sometimes it's good to know how to import an image...
  • loadImage() - load the image
  • image() - draw the image you loaded - you can mess with its size and scale and stuff too

BONUS: Changing the frame of reference for where we draw

It's often useful to change where "0,0" is, and/or rotate stuff for some cool effects...
  • push() - remember where we were drawing now
  • translate() - move to where we want our new center of drawing to be
  • rotate() - spin stuff around (PROTIP: you usually want to call translate() then rotate() unless you mean to spin the world
  • pop() - pop back to where we push()d before


These are only the commands I've found myself using again, but P5 does a ton more.. I've barely touched the sound APIs (so many of my games are quieter than they should be) - there's also stuff with microphones, and some mobile-specific stuff as well.  The p5 examples page can be an inspiration in many ways, and teach you basic programming concepts too.

Nerdy Javascript basics...
This page isn't meant to be a programming class, and the p5 examples page can teach you many things, and there are some great youtube tutorials - The Coding Train seems pretty cool.

Obviously, you should know about variables - historically you declare a variable with var (like var x = 10; )though more modern javascript favors saying let x = 10; (for things that change) or const x = 10; for things that are "constant" and don't change.

Also, it's usually pretty easy to set up an array (using [] symbols) of objects (key-value pairs that are wrapped in {}). So you might have an array of, say, circle objects that you "iterate" over (i.e. go over each one) and moving them (maybe drawing them to the mouse?) and drawing them, so they can be seen

One thing that's trickier, oddly, is safely removing stuff from an array. In some cases, you might just want to mark something as "removed" and ignore it... but I'm going to include an example of using the (modern javascript, not p5 in particular) filter command to remove it.

Here's a simple program that just puts 10 dots on the canvas:
var dots = [];
function setup() {
  createCanvas(400, 400);
  for(var i = 0; i < 10; i++){

function draw() {
//fancier way of the old for(var i = 0; i < dots.length; i++) 
// { var dot = dots[i];
  for(dot of dots) { 

So each dot is an object with an x and a y propery: {x:_, y:_ }. Lets make a version of this program where if you click on a dot, it gets an extra "imdead" property, and then skip drawing it. Here's all the code with the new parts in bold...

var dots = [];

function setup() {
  createCanvas(400, 400);
  for (var i = 0; i < 10; i++) {
    dots.push({x: random(width),y: random(height)});

function draw() {
  for (dot of dots) {
    if (!dot.imdead) {
      ellipse(dot.x, dot.y, 20, 20);

function mousePressed() {
  for (dot of dots) {
    if (dist(dot.x, dot.y, mouseX, mouseY) < 10) {
      dot.imdead = true;

So, if we didn't want to check imdead all the time (in this example it's no problem to ignore the dead ones, but either for performance reasons or just for simpifying code we might not want to have to keep checking) we can change that draw() loop to what follows:

function draw() {
  for (dot of dots) {
      ellipse(dot.x, dot.y, 20, 20);
  dots = dots.filter(dot => (!dot.imdead));

we got rid of the imdead check wrapping the ellipse() command, and then used a fancy new js "filter()" that kicked out all the imdead dots. Obviously there are many ways we could have used that filter (like right in the mousePressed() for instance) but in my experience, setting a property like that and then removing the thing later is something that comes up a lot in games I make. (Like maybe the alien plummets to its death before being removed from the screen...)

Tuesday, August 6, 2019

wacky joins of flat json data and dumping to excel

Just keeping this around for my own reference, it might be too esoteric for anyone else.

I use a filesystem based database for my Porchfest work: one folder roughly equals a table, and each json file corresponds to a row, and the filename acts as the key. (and each file is a simple map, where the keys correspond to column names and the values are the column values)

So I have one folder for porches, one for bands, and one for "gigs", which is one to many join table for porches going to bands, along with what time.

My porchfest runner asked if I could dump out the porch / band / what time info to a flatfile (or rather, to a dirty "tab delimited file pretending to be a spreadsheet" that excel lets me get away with)

The format she wanted was a little idiosyncratic: one row per porch, and then each band and gig kind of flattened on the same row-- like if I was doing it on my own I might have done it

so that columns were "clean" and nothing was duplicated (but different rows mean different things) or possibly
where columns are still clean, but information is duplicated...

She asked for something more like

I personally find it less intuitive, since different porches have different numbers of bands, and columns have to be "sort of" duplicated etc, but I kind of get it... I'm thinking too much like a computer and not an excel user :-D

Anyway, here's the code that does it.  I read in all the metadata as GET parameters (though the commented out variables give a feel for the values I'm passing in ), I build a top row as header values, and then if I'm passed a file name I use that dirty old php to excel hack otherwise I just dump the info to the browser <pre> tags

    prints out select contents of three tables assuming a one to many relationship,

   print out will be 
      leftdata rightdata1 joindata1 rightdata2 joindata2
        in practice:
        porch1 band1 gig1 band2 gig2
        porch2 band3 gig3

        $lefttable = "porch";
        $righttable = "band";
        $jointable = "gig";
        $leftjoinfield = "porchid";
        $rightjoinfield = "bandid";
        $leftfields = "address|spacetype|zone|capacity|raindateable|notes|maincontact|email";
        $rightfields = "bandname|email|howloud|needelectricity|bandsize|candoraindate|preferredstarttime|performancelength";
        $joinfields = "starthour|startminute";
        #$leftfields = "address";
        #$rightfields = "bandname";
        #$joinfields = "starthour|startminute";
        $lefttable = $_GET["lefttable"];
        $righttable = $_GET["righttable"]; 
        $jointable = $_GET["jointable"];
        $leftjoinfield = $_GET["leftjoinfield"];
        $rightjoinfield = $_GET["rightjoinfield"];
        $leftfields = $_GET["leftfields"];
        $rightfields = $_GET["rightfields"];
        $joinfields = $_GET["joinfields"];
        $filename = isset($_GET["filename"]) ? $_GET["filename"] : "";    
        $leftguts = getGuts($lefttable);
        $rightguts = getGuts($righttable);
        $joinguts = getGuts($jointable);
        $buf = "";
        $maxjoins = 0;
        foreach($leftguts as $leftkey=>$left) {
            getValuesForKeysInString($left,$leftfields)  ;
            $joinmatchkeys = array();
            foreach($joinguts as $joinkey => $join){ //go over all keys in join table
                if($join[$leftjoinfield] == $leftkey) { //if this is a join
            if(count($joinmatchkeys) > $maxjoins) {
                   $maxjoins = count($joinmatchkeys);
            foreach($joinmatchkeys as $joinmatchkey) {
                $rightkey = $joinguts[$joinmatchkey][$rightjoinfield];
                #$buf .= "$rightkey\t";
                getValuesForKeysInString($rightguts[$rightkey],$rightfields)  ;
                getValuesForKeysInString($joinguts[$joinmatchkey],$joinfields)  ;
            $buf .= "\n";

        $headerbuf = "";
        foreach(explode("|",$leftfields) as $leftfield) {
            $headerbuf .=  "$leftfield\t";   
        for($i = 0; $i < $maxjoins; $i++){
            foreach(explode("|",$rightfields) as $rightfield) {
                $headerbuf .=   "$rightfield\t";    
            foreach(explode("|",$joinfields) as $joinfield) {
                $headerbuf .=   "$joinfield\t";    
        $headerbuf .= "\n";
        $buf = $headerbuf.$buf;
        if(! $filename) {
           print "<pre>$buf</pre>";        
        } else {
            header("Content-Disposition: attachment; filename=\"$filename.xls\""); 
            header("Content-Type: application/");
            print "$buf";
        function getValuesForKeysInString($guts,$keystring){
            global $buf;
            foreach(explode("|",$keystring) as $key) {
                $val = $guts[$key];
                $val = str_replace("\t"," ",$val);
                $val = str_replace("\n"," ",$val);
                $val = str_replace("\r"," ",$val);
                $buf .= $val . "\t";

        function getGuts($tablename) {
            global $dbroot;
            $guts = array();
            $path = $dbroot."/db/".$tablename."/";
            $files = scandir($path);
            foreach($files as $file){
                if(substr($file, 0, 1) != "."){ //ignore hidden files
                    $rowguts = json_decode(file_get_contents("$path/$file"),true);
                    $guts[$file] = $rowguts;
            return $guts;

Saturday, August 3, 2019


Does anyone remember "Sniglets"? new made up words describing common shared experiences? here's one I'm just making up now:
Linktarrhea: when you paste a URL or link into a document or email and it becomes or remains a link but then you can't write anything after the link without it becoming part of the underlined content, no matter how stupid it looks.
So silly that it's 2019 and I'm still fighting with this issue-- any program should know that it's exceedingly rare (and bad design) for a link text to include paragraphs, so pressing return and inserting a line break should clearly end the damn link.

I think of that little six-step dance I do if I experience Linktarrhea and want to get past it: hit undo to remove the characters I just typed and eventually the URL I pasted, type a few throw away letters, move the cursor back to where I wanted the link, paste it again, move the cursor to after the throw-aways and start typing the word I actually wanted to type, then go back and erase the throw-away letters. Cha-cha-cha.


Friday, August 2, 2019

wave your hands in the air! wave 'em like you just do care!

Kottke announced Soli touchless interactions are coming to Pixel 4 and reposted this video from a few years ago:

Seeing the video made me a bit less skeptical about the concept - I think that doing it poorly would be REALLY annoying, but if they made it very definite about when you're in gesture mode... maybe it could work.

Still I think of that Douglas Adams line:
A loud clatter of gunk music flooded through the Heart of Gold cabin as Zaphod searched the sub-etha radio wavebands for news of himself. The machine was rather difficult to operate. For years radios had been operated by means of pressing buttons and turning dials; then as the technology became more sophisticated the controls were made touch-sensitive - you merely had to brush the panels with your fingers; now all you had to do was wave your hand in the general direction of the components and hope. It saved a lot of muscular expenditure of course, but meant that you had to sit infuriatingly still if you wanted to keep listening to the same programme.
For the late 70s, he was thinking years ahead!!


This page on React hooks is also a pretty good reminder/review of the whole redux/dispatch/reducer thing. Sometimes I get rusty on that when I don't work there in a while.

Thursday, August 1, 2019

note to self: less noisy CGI param checking in PHP

Insert usual sorry-not-sorry-for-using-php... there's that old Larry Wall adage how programs that wish to communicate well should "be strict in what they emit, and liberal in what they accept". One place where PHP lags Perl in this is checking for hash values when the key is missing - I find my server logs getting full of "PHP Notice:  Undefined index" when looking in $_GET or $_POST but I have fine default behaviors ready if those values aren't there.

I guess
function getval($key){
    return isset($_GET[$key]) ? $_GET[$key] : "";    
function postval($key){
    return isset($_POST[$key]) ? $_POST[$key] : "";    
function requestval($key){
    return isset($_REQUEST[$key]) ? $_REQUEST[$key] : "";    
Does about as good a job as anything at checking first.

Random aside, and thinking of that Larry Wall quote- I remember way back when, like in the 90s, thinking it was annoying to have to check POSTs differently from GET, since CGI was a nice abstraction over either, and while the internals of how that data is passed might be very different, from the scriptwriters point of view they were kind of the same.

Now, I guess I get it. People get an intuition for what should be GET and what should be POST, so PHP's generic REQUEST (which includes cookies) is kind of an afterthought. It just feels less chaotic to understand what's channel stuff is coming in from.

Wednesday, July 31, 2019

keyboards and power users

Last week I wrote on my blog and facebook:
For years I've been digging using a laptop on my workdesk with additional monitors hanging out above it - I like how so much remains constant whether I'm on at the desk or on the go, and most external keyboards don't make it easy to put a trackpad as close at hand, or rather close at thumb, as a laptop.
However I seem to be in a minority at work, and most of my peers use some kind of external keyboard, either with the laptop closed or perched above as another external monitor.
Back in the day, developers used desktops, and so this was a moot issue, but I feel like external keyboards are more popular among developers now than they were 5 years ago or so. (I might be wrong on this.) And I wonder if it's just the heat issue - laptops, especially these thin ones, run hot, and it's not always the most pleasant thing to have on your fingertips.
Heh, at least for now, developers using external keyboards even with their Macbooks means the famous MacPro touchbar is even more useless, unless Apple makes a special keyboard  that has it. (I got a new laptop with it and have to take care not to accidentally brush its virtual esc key... Douglas Adams warned us about this!)
A talented FB friend of mine, Seneca Menard wrote in the comments
I can't stand laptop keyboards for many reasons: 
1-the keys are not deep thus it's very easy to hit multiple keys on accident plus you get less tactile and audio feedback when you're successfully hitting the keys, which lead to further errors.
2-the laptops usually don't have a numpad, a good set of keys for ins, del, pgup, pgdn, etc and even the arrow keys often get a terrible position and size and so you get user errors when using them without looking.  All of those keys are precious to a hardcore PC user like me that has thousands of hotkeys and so they have to all physically exist, and have a specific position and size. :p
3-the "function" key is horrendous. I almost never use those yet they're then responsible for moving around all the vital modifier keys like alt and Ctrl and whatnot. So my production speed gets destroyed because all the keys I'm dependent on were destroyed (in my subconscious memory of where which keys are) by a key I'll never use! Ughhhhhh.
4-most keyboard designers don't put enough importance into the position and size of high priority keys like enter and backspace. And so these keys that you press more often than any other keys on the keyboard end up getting mis-clicked by the user and thus slow the user down
5-every new laptop gets a different keyboard so you're always going to not be up to full typing speed because the keys are moving around every time you buy a new PC which is quite often
6-most keyboards have poor F-key layouts. I like my keyboard because there's a space between every 4 keys and so I can easily feel my way around them without looking or quickly jump to a key visually without reading the labels.  As if their size and spacing wasn't important enough to me, when I heard the new MacBooks now don't have any physical f keys at all it blew my mind!  Do these people not even use computers for work when they're making design decisions like that?!?!  :(
7-Then there's extra random stuff like how I like a calculator button or a play pause button (and these exist as standalone buttons so they easy to find without ever looking), or how some keyboards have palm rests, or how I like that my keyboard is not built into a flat laptop surface and is instead raised off of my desk so I can easily get my hands into proper typing positions without looking at the keyboard by feeling for the bottom left front corner of the keyboard and then my fingers all fall into place.  Or how some users like having split keyboards for wrist ergonomics, etc etc etc.
I go crazy whenever I'm slowed down by the input device(s) I'm using so that's why I use an ancient keyboard and *only* that keyboard.  It's over 20 years old and still working great and I have a couple of others as backup if it ever did die.
So hopefully that explains why a number of us don't use laptop keyboards.  :)
It was fascinating reading this mini-rant. It sounds like folk like Seneca are really working needing a flowstate, and so being comfortable with the hardware is critical. 

I can only somewhat empathize, being a little more superficial in my interactions - but I know how painful it is when there's overall system lag when using any computer - eventually it builds up and manifests as a kind of physical tension.

smilie when you say that, pard'ner

On my company's blog, Shayan wrote about Text and Emoji analysis on CarGurus text and chat.

Saturday, July 27, 2019

hot hands hot hands

For years I've been digging using a laptop on my workdesk with additional monitors hanging out above it - I like how so much remains constant whether I'm on at the desk or on the go, and most external keyboards don't make it easy to put a trackpad as close at hand, or rather close at thumb, as a laptop.

But I seem to be in a minority, and most of my peers use some kind of external keyboard, either with the laptop closed or perched above as another external monitor.

Back in the day, developers used desktops, and so this was a moot issue, but I feel like external keyboards are more popular among developers now than they were 5 years ago or so. (I might be wrong on this.) And I wonder if it's just the heat issue - laptops, especially these thin ones, run hot, and it's not always the most pleasant thing.

Heh, at least for now, that means the famous MacPro touchbar is even more useless for developers, unless Apple makes a special keyboard with it included. I got a new laptop and have to take care not to accidentally brush its virtual esc key... Douglas Adams warned us about this:
A loud clatter of gunk music flooded through the Heart of Gold cabin as Zaphod searched the sub-etha radio wavebands for news of himself. The machine was rather difficult to operate. For years radios had been operated by means of pressing buttons and turning dials; then as the technology became more sophisticated the controls were made touch-sensitive - you merely had to brush the panels with your fingers; now all you had to do was wave your hand in the general direction of the components and hope. It saved a lot of muscular expenditure of course, but meant that you had to sit infuriatingly still if you wanted to keep listening to the same programme.
 Considering he wrote that in the late 70s, he was amazingly prescient - about these touchbars, and about all these gesture interfaces...

Tuesday, July 16, 2019

making the ipod and the iphone

The Daring Fireball podcast "The Talk Show" briefly mentioned Pixo - they made the OS that all the classic iPods ran. I can't find many videos focusing on the OS in action, alas, but it had such a beautiful look, and was so elegantly merged with the famous iPod clickwheel that it's almost hard to believe it wasn't from Apple itself.

It's easy to forget how amazing "1,000 songs in your pocket" was. Certainly that was something my PalmPilot wasn't doing then! And when the main competitors of the time were a skipping CD-man with like 12-20 tracks, or a linear tape system with the same kind of limitations where you can't skip tracks, really... it's stunning. (Also stunning is how relying on local music storage of any kind indicates you're a bit of an old fogey...)

Researching that, the super-crude prototypes for the first iPhone also make for some interesting reading... both are as astonishingly ugly as the iPod was beautiful!

Also: here's a cool but old  video about "The Future of iPod". I love these attempts to predict the "next" iPod or iPhone, made way back when.  Some are clearly parodies, but others are serious, and some more or less land on the simplicity the first iPhone offered - and then there are ones that presume the scroll wheel was a critical identity element, so they throw in a virtual onscreen version.

WAVEing to accessibilty - WAVE is a useful - and VERY fast - tool, showing possible accessibility issues on the site. You can get a browser plugin for chrome or firefox.

It's a little unfortunate that it doesn't have a sense of relative importance of various transgression - a slight "boy who cried WOLF!" effect is definitely present - but still, it's a great free starting place. (Especially in this age where public companies have a legal responsibility for good a11y, and are at risk for lawsuits when they are out of compliance.)

(Heh, and for one of the first times, I find it easy to empathize with this kind of thing-- my 40-something year old eyes are slowly developing low light contrast issues and losing a bit of the close up focus they used to have...)

Monday, July 15, 2019

not overthinking: block specific AJAX Urls in chrome needs no special tools

Just recording a moment of duh for myself --

at work a page that is broken on my developer box (with an Ajax request failing) pointed out that the way we were displaying Ajax errors might be borked, but I wasn't sure if the error reporting borkage was "my fault" -

I assumed I would need a plugin/extension/app like Postman or Charles "Tamper Chrome or what not to recreate the error state when I was, but actually Chrome's Developer Tools Network tab handles it easily enough - just right click on the specific call and "Block request URL"

Tuesday, July 9, 2019

on blogs - a few thoughts on how blogs kind of encapsulate many different types of personal (and public) data recording grew into something sort of interesting. To me. (Which is a pretty low bar to meet)

Sunday, July 7, 2019

run less software  as I get older (and so therefore dumber and wiser (hopefully/probably not too much of either) I really appreciate the idea of reducing cognitive load...

Same energy:

...see also Joel on Software's old article Fire and Motion, both the parts about how some mornings it's tough to get going, and the importance of not being distracted by Flavor of the Week technologies...

Tuesday, July 2, 2019

note to self: google map's api got expensive and using openlayers as replacement

A while back Google decided it was being too charitable with usage of its Maps API so it cranked up the fees (the most common figure is "by 1400%") to monetize it better.

So I found usage on Porchfest days was costing me a couple hundred bucks for the month. Whoops!

This page has some alternative APIs. Unfortunately, the most obvious candidate to replace it, openlayers doesn't have the friendliest documentation, and gets pretty complex - it's a tool built for serious power-usage, and the examples page is flooded with tons of semi-esoteric things you can do - frustrating when you just want to throw some porch locations on a map and have a little info box pop up when you click one.

The example on seems to pretty much cover it (though the way it's the only thing at that URL gives me pause - I posted its working example at )

Of course going down this route  will point out the hundred little things I learned to do acceptably well in Google Maps and need to relearn - details like icon sizes and default zoom levels and what not.

FOLLOWUP: Googling a bit more, comes up a lot - it seems to cover that "map with marker icons that open informational popups" sweetspot pretty well...

MORE FOLLOWUP: Yeah, Leaflet really is the sweet spot for this kind of thing! The only thing in my usecase that wasn't covered by the first two quick start tutorials is having numbers on the icons... this page on simple numbered markers with leaflet has the solution I like best. I think the only changes might be adding a line-height: 25px; to the CSS - and I might double back and do a 2x icon for it.

Monday, July 1, 2019

jony ive out, and monitoring the monitor situation...

Gruber was one of the first to note that Jony Ives was leaving Apple and his opinions influenced my views.

I agree with him that, that while he has a singular vision (and was interesting seeing his relationship with Jobs as a bit of Lennon/McCartney) to the extent that he was responsible for thinner at all costs including this keyboard mess, it's good to move past that.

The other thing is how I never would have guessed that the "we will still partner together" - but I think I have to play wait and see given the coverage I've seen elsewhere.

When Jason Snell was on Gruber's podcast, they got to talking about the Pro Display XDR and its $5000 price tag and infamous $1000 stand - and for the tiny but prestigious audience Apple is courting, that is probably a good deal.. but they pointed out that there's another market that's underserved right now - there's no good, medium-cost Apple-centric desktop monitor, either for people looking to dock a laptop or for Mini users.

They mentioned Apple stopped that collaboration with LG -  and also disabled the old ability for an iMac to be chained in as an external display (ignoring its own built in computer, so to speak) And that there's a division with what the majority of PC folks want - that gaming generally looks for fewer pixels or at least always greater refresh speed, and what a programmer or designer would be hoping for...

Sunday, June 30, 2019

MacOS: crop an MP4 video with MPEG Streamclip

My SO wanted to crop a screen recording video where it was a fullscreen browser, but the interaction was on a small part of the screen.

There are probably multiple ways of doing this but the most straight forward seemed to be good ol' MPEG Streamclip, a free Mac + Windows program from

I right clicked to open the MP4 video with Streamclip, selected "Export to MPEG-4", and there is a cropping section:

Turned out better not to mess with Frame Size (leaving it at "1920 x 1080 (Unscaled)") and then making sure the Cropping's final select was "Destination". I had to guesstimate the cropping numbers a bit, so I used "Preview" to check the work, and then interrupted that and did "Make MP4"

(Note: the version of MPEG Streamclip I use is threatening me with the "won't work with future versions of MacOS" warning (i.e. it's 32 bit vs 64) and it looks like its been a while since it has been updated, so Caveat Emptor or whatever Emptor is for someone grabbing free software.)

Tuesday, June 25, 2019

cognitively loaded

Forget monoliths vs. microservices. Cognitive load is what matters.

I do think many engineers forgot about "cognitive load".

There's also a difference of opinion if brining in a tool kit is more likely to reduce the cognitive load ("hey look, this does the work for us so we don't have to learn it!") or, thanks to the Law of Leaky Abstractions, increases it (we still have to know what the toolkit is doing, and how it's doing it, all the way down, in case something goes wrong.)

For the user experience too... stuff like Fitt's Law gets all the attention from the stopwatch brigade, because the millisecond increase is easy to measure - but stuff like cognitive load tends to be trickier to track.

Friday, June 21, 2019

conditional classnames

at work we're switching from classNames to clsx for making a conditional-driven classNames string. Said to be much lighter weight. In both cases it seems weird how much the library lives the "be liberal in what you accept" mandate, like this example from classNames:
classNames('foo', { bar: true, duck: false }, 'baz', { quux: true }); // => 'foo bar baz quux'

Tuesday, June 18, 2019

rant: MacOS modifier key hell and stupidity with the task switch

Rant I posted in the Lost In Mobile WhatsApp group:
Man, why are MacOS modifier keys such a hot ball of flaming fetid dumpster garbage? I was idly searching "is there anyway to get cmd-tab to act more like clicking on the dock and open a window if there is none"  (and that's a big design fail in MacOS I'd say- returning to the app from a quick cmd-space Spotlight command also does the logical thing of opening the main or a blank window if nothing is open... 
According to StackExchange you can "Cmd-Tab to the app, but then, before releasing Cmd, press Alt. Then release Cmd while holding Alt. The window should pop open." but that understates the complexity... "alt" is actually not its own damn key, it's an (ironically) alternate option for the option key - you have to hit "fn"! So, holding down fn-option-command-tab, and then release things in the right order to get it to work... what a mess.
And in exploring Keyboard preferences to see if maybe there's a way of fixing the shortcut, I see I can also "Tun Docking Hiding On/Off"  is  ⌥⌘D ... trouble is.... ⌥ is not even labeled on the modern keyboard, it's secret, old school code for "option".  What kind of esoteric nonsense is this? It's like a secret handshake just to understand what the preference means.
People suggested HyperSwitch, a cousin of the HyperDock app I've grown to dig (mostly for its window wrangling keyboard shortcuts)... that does the Right Thing of opening a new finder window or restoring the Calendar app window if I happened to close the window itself - which, again, should be a BLEEDIN' OBVIOUS thing to do especially since the dock and spotlight do just that, and the usecase for "switch to no window" is ridiculously small. Also HyperSwitch adds option-tab that cycles through windows instead of apps... I used to prefer old school Windows task/window centric approach but now I find it faster to use Mac's app-centric point of view.