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 = [
   {msg:'ok1'},{msg:'killme'},{msg:'ok2'}
 ];
 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

BUT WAIT THERE'S MORE!

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++){
     dots.push({x:random(width),y:random(height)}); 
  }
}

function draw() {
  background(220);
//fancier way of the old for(var i = 0; i < dots.length; i++) 
// { var dot = dots[i];
  for(dot of dots) { 
     ellipse(dot.x,dot.y,20,20); 
  }
}

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() {
  background(220);
  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() {
  background(220);
  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

<?php
/*
    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
                  array_push($joinmatchkeys,$joinkey);
                }
            }
            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/vnd.ms-excel");
            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

Linktarrhea

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.

WYSIWYG? More like WYSIBS.

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!!

hooks

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

https://wave.webaim.org/ - 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

https://kirk.is/2019/07/09/ - 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

https://www.intercom.com/blog/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: http://boringtechnology.club/

...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 https://www.festinosolutions.com/blog/ 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 https://porchfest.info/temp/festinosolutions/ )

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, https://leafletjs.com/ 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

NOTES TO FUTURE SELF:
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 http://www.squared5.com/

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.

sliding horizonal carousel

Colin Lord's How To Create Horizontal Scrolling Containers seems to be a thoughtful guide to making a horizontally scrolling panel, like for a carousel... (pretty good for a first pass, I think I might need something more advanced for the "snap-to" though)

Friday, June 14, 2019

what's old is new again, sort of, a walk down memory lane

At work someone mentioned a legacy code base used XSLT to transform XML. This caused me to recoil in horror: the fierce restrictions XSTL imposes (variables can't actually very, and they are super-tightly scoped, so sometimes you have jump through hoops and restructure to do the same thing with different inputs) made my developer life miserable for a number of months at a consulting gig.

But another engineer said he liked XSTL; in particular he couldn't think of many template things that allowed step by step debugging.

Thinking of other examples of debuggable templating, I remembered old school JSPs - the first generation, where you would jump back into java for your loops and conditionals and what not:

<div class="holder"> 
<!-- actually this might have been before CSS being widely used... -->
<% if(someCondition) { %>
    <b>IT IS SO TRUE!!!!</b>
<% } %>
</div>

Obviously that's a bit ugly, the mix and match of those code snippets, where the first one kind of leaves the open curly brace hanging...

But one cool aspect is the JSP would be converted into the .java code of a servlet (basically all the html got stuffed into println() statements)  and you could inspect it, and I suppose throw a debugger up in there.

Later, you got the JSTL. Here, the concept of "we can build fancy tags and stop having to duck into nasty old Java, so you ended up with code like
    <c:if test = "${salary > 2000}">
         <p>My salary is:  <c:out value = "${salary}"/><p>
      </c:if>
There's a kind of engineer would would say this a big improvement. I am not that kind of engineer. Where as that kind of engineer might see it as beautiful to be able to keep everything in the same mode of tags, I saw it as A. another syntax to learn (to use the tags) and B. another complicated API to use (when you wanted to write your own tags.)

Anyway, fast forward a decade, when more and more stuff is moving into the browsers... and you see Angular going down that JSTL path...  with slogans like "what html5 would look like if it were designed for making apps" (well, some slogan along those lines.) But it had the same two fisted problem of how every tag you make introduces its own syntax, and then the API to make those new tags was, again, very complicated.

And then React came into ascendency. As always, the whole trick to these things is "how do we mix in our DOM and our logic", and the preferred way with React is JSX, just letting the tags sit there (frankly, with later ECMASscript  ` ${coolstyle}` quotes, I don't think JSX is as important as it was at first, but maybe I'm just a heretic. But still, I see a positive similarity in this kind of React and early Servlets - with the exception that React encourages component based thinking, while servlets were page-based, and so each servlet would tend to carry its over head to display the header and wrapping code one way or another. 

I guess overall I think it's odd to have one mode, "tags", both for conditional logic and stuff parsed serverside or in js space (as with Angular) and other stuff with visual or semantic meaning that stays in the DOM. I don't mind lerping things as tags quite as much in React, because it is pretty steady where tags are nouns (i.e. DOM objects or things that become DOM objects) and not verbs (loops and conditionals)

Tuesday, June 11, 2019

trivial invoice tool hack

I've been doing more and more Porchfests, and most have some level of budget for my web design, engineering and hosting - which means I need to send out invoices.

There are several free online tools out there, but all with the little bits of UI weirdness that made me not like them, or they wanted me to sign up or some crap, so I decided to make my own simplistic hacky tool: https://kirk.is/tools/invoice/

The form populates itself from any GET parameters, and there's a button at the bottom to submit the current form and generate that GET/link for the current contents. All the inputs look like regular text, and so you can either make a screenshot and then send that, or put it in a PDF, or maybe print the page to a PDF file (the make link button will hide itself). Plus of course you can save the link with just your info, nothing specific to the client, and use that to start with each time.

Here's the link with the stuff filled in...

Again, no rocket science, but I think the idea of doing everything as a link is a little clever.


Thursday, June 6, 2019

MacOS hint: better cmd-space living through restricting what Spotlight searches

I'm messing with having the Dock autohide. I used to have strong feelings about the utility of the dock vs taskbar but now I realize the Dock is just taking up precious screen space and not offering much value, since I use Spotlight (aka "cmd-space") to launch everything, and usually cmd-tab to get back to open apps.

But I was a little irritated with the sheer amount of crap Spotlight was searching - I never think of it as a "document management tool", just an app launcher, so the plethora of obscure stuff it would present for a text search was a bit distracting.

Bob on The Lost In Mobile WhatsApp crew pointed out you can go to System Preferences | Spotlight and weed that down to something more reasonable:
Even here, the sheer number of options seems to point to some level of categorical confusion. Like, Calculator isn't an app? (oh, I see- I think it's the ability to type in a simple math problem.) Or "PDF Documents" and "Spreadsheets" are a separate category than "Documents".

It's interesting how Spotlight (and its equivalent from the start menu in Windows) is so powerful. In a way it seems like retrograde, like a return to the command line for everything, but it combines the fluidity of typing with the multitasking of modern computers, and the sensible and forgiving autocompletion of, say, a recent browser's Address bar...

Sunday, June 2, 2019

lifehack: sort your todos by benefit

M. Lazer-Walker tweeted:
I don’t remember who gave me the idea, but I’m SO much happier now that I’m framing my task manager in terms of what benefit I’ll get from doing things rather than having an endless list of tasks I “need” to do.
Interesting idea!

Saturday, June 1, 2019

inventory your 32-bit MacOS apps for when life becomes 64-bit only

If you're a Mac user, you may be getting used to seeing warnings like this:
Much like with what happened with iOS, 32-bit apps will no longer be supported.

(For someone with packrat tendencies, this is a bummer! Also I have a pile of indie games that will need special care to run in the future.)

Bob on the Lost In Mobile WhatsApp channel mentioned you can find out what apps are on your system now that might not easily run in the future:
1. Go to "Apple Menu | About This Mac"
2. On the "Overview" tab click "System Report..."
3. In the left column scroll down to "Software > Applications"
4. You can sort on the rightmost "64-bit" column and look for the "No"s.

Wonder when Apple will move away from Intel chips for Macs and what the transition will look like.

Tuesday, May 28, 2019

taking for granted: URL bars and spellcheck

Just a random thought- if I were timeshifted back a decade or two and had to use the browsers of the time: one thing is how much people got used to one input box that is both the URL as well as a search box. Also, it's kind of cool that spellcheck is now near ubiquitous - I guess that extends beyond the browser and into the OS.

Saturday, May 25, 2019

vs code extensions i like

I'm realizing that I really need to get my mojo of making small things in React from scratch (just for practice), I went back to my old Hello, World in Parcel - I was frustrated that my home laptop's version of VS Code wasn't trivial to set up so that it was prettifying like my work machine, and a few guesses at what extensions I might want were R O N G wrong.

So fwiw, at work I've been using:

  • Babel JavaScript
  • ES7 React/Redux/GraphQL/React-Native snippets
  • Prettier - Code formatter
  • GitLens - Git supercharged
And I've grown to love that editor.formatOnSave setting...

Friday, May 24, 2019

css gradients, fun and easy

PorchfestMV, Martha's Vineyard, is the latest addition to my small legion of porchfest websites.

They used one of those "Free Logo" sites (honstly, more like $30-50 if you want to actually use it) and came up with:
Not setting the design world on fire, but serviceable. And the use of color gradients provides an easy visual hook for the website:


To make that bar, I made sure the wrapper div has a style position:relative, and then something like
.colorbar {
    height:100%;
    width:10px;
    position: absolute;
    left:-20px;
    background-image: linear-gradient(green, yellow, purple, red, blue);
}
I avoided ROYGBIV order for now, and might go back to color pick the actual colors the logo uses, but for now it's pretty sharp looking, and easy to code.

Monday, May 20, 2019

weird flex but ok

Guess 'cause I'm still getting over this fever, but it took a few stabs for making a sponsors display like:
(Looking at it now I realized it would have been a decent candidate for grid or even table, though it's nice that flexbox means you don't have to think about lining up with a row-like object)

I admit I'm falling into bad habit of "px" and non-semantic markup, but Oh Well...

(it is sad that it's 2019 and centering vertically isn't an instant no-brainer)

Anyway: markup like this, with "texty" for a sponsor that has no img:

<div class="sponsors">

<div class="texty"><div>Barry's Village Deli</div></div>
<div><img src="/img/sponsors2019/fastsigns.png"></div>
<div><img src="/img/sponsors2019/Justnextdoor.jpg"><div>Just Next Door</div></div>
//...
</div>
And then CSS like


.sponsors {
    display: flex;
    flex-wrap: wrap;
    width: 845px;
    margin: auto;
}
.sponsors > div {
    margin:20px 2px;
    width:270px;
    height:270px;

    display:flex;
    justify-content:center;
    align-items:center;
    flex-direction:column;
    
}
.sponsors > div > div {
    text-align: center;
    width:100%;
}
.sponsors .texty {
    color:white;
    background-color:#2b809e;
    font-size:1.5em;
}
.sponsors img {
    max-width:250px;
    max-height:250px;
}