Sunday, December 29, 2019

19 years a blogger

Yesterday I did a reconfiguration of some of the internals of my long-running daily blog kirk.is and I wrote up the history of the site - all 19 years of it - along with a description of the changes. 

Saturday, December 28, 2019

processing tips, and mourning java on the clientside web

 I follow the tumblr For Your Processing and they linked to an old 25 Life Saving Processing Tips article - I should really get better at knowing when to use noise() not random().

Playing with processing vs it javascript cousin p5 and trying to replicate a "persistence of vision" toy (a Disney's Frozen branded fan that used a strip of LEDs to make animations as the fan spun) I was reminded how much faster the Java stuff could run vs the p5 - Processing can easily get into 4-digit FPS, a p5 thing seems to max out around 60. (So in the case of replicating persistence of vision, it was better to use that "draw a semi-opaque rectangle over the last frame instead of erasing it" trick the Tips article mentions.)

But of course, Processing sketches can no longer be readily shared on the web, which is deeply sad. While I know applets never looked liked a piece of the native web, always a bit janky in terms of colors or fonts, I will never quite understand why the applet sandbox couldn't be made as safe as the javascript sandbox and so was always considered a security risk - javascript is so tightly woven into the browser, it feels like it would be tougher to get make sure it was secure and isolated from any weird overflow attacks or what not.

Similar argument for Flash, come to think of it. Flash artists were doing things decades ago that dynamic web people still struggle to replicate. (Of course when it came to UI for a site,  Flash was usually a lesson in "just because you can doesn't mean you should")

So many artists who want so share the work often have to export to GIF (which is kind of hilariously old and inefficient come to think about it - plus the format heavily favors things where the art can be made to smoothly loop. Anyway low-sugar-eye-candy is one of the best of that form...

Friday, December 27, 2019

macos finder - actions | sort by vs group and what is a popsicle anyway?

So I learned two small things yesterday.

I posted this rant in the Lost In Mobile WhatsApp group:
Man, one thing I HATE about MacOS - and almost gives creed to the "I should try linux, it's configurable" but I'm sure that would be its own death by 1,000 paper cuts-- if I group by "Date Created", then PLEASE SORT BY TIME CREATED. Don't put half the dang files in an ALPHABETICALLY sorted sublist called "Previous 7 Days", for pete's sakes
Eventually comments from BobD in the group got me poking around more, and I realized that while I tend to always use "Group" for changing the file arrangement in the "as Columns" view I prefer (the one where ancestor directories are columns on the left), there are different options for sorting under "Action".

I guess I never noticed that because "Group" has its options one level higher than sorting under actions, this:
versus this:

And also for arranging by "Name", Group and Sort By are identical - but the Date and Size stuff gets that behavior of sorting by name under each Group heading. (Which seems like a very odd design decision to me, usually the groups headings aren't that fine grained or useful distinctions per se, and the user has clearly expressed an interest in a date or file size characteristic....)

Another thing I learned yesterday - for USA English, at least in the Northeast, most people say a "popsicle" can only be something icy on a stick, probably frozen juice - that if it's cream-based, that's an "ice cream bar" or "frozen yogurt bar" or whatever, but not a "popsicle". (Of course we're talking about the genericization of a trademark term, ala Kleenex or Xerox anyway.)

It's weird to be in my middle age and not having picked up on this common distinction. In thinking on why I'm more likely to find the distinction so arbitrary and confusing (to me, a sweet frozen treat on a stick can be called a "popsicle", and some of the dictionary definitions and many google images support my looser use of the term) I think back to an idea I've written about here before:
I don't care so much about the interior lives of things; people and computer objects alike should be judged on what they do, not what you think they "are". 
So in this case, the interface (the interaction, the verbs) for a "popsicle" and an "ice cream bar" are identical: go to the freezer, unwrap it, enjoy a sweet treat while holding a stick, discard the stick - and so it seems daft to have to do a composition analysis of the noun to know what word I need to use (one friend with a dairy sensitivity mentioned some cream-y things might be "popsicles" if the creaminess was gelatin rather than dairy based, and so the distinction has a particularly utilitarian aspect for her.)

It does seem odd to me then for common usage in my area, we have no term covering all "frozen things you eat from a stick". There's "frozen novelties" (which sounds like some kind of coy euphemism) and "frozen treats" but both of those include things like ice cream sandwiches.



Wednesday, December 18, 2019

browser diversity

Coworker terribledev writes
Microsoft has been working over the past year to re-write edge off of its own Trident rendering engine and UWP app platform and instead building edge off Chromium (the platform chrome is built on). The windows community has dubbed this version of edge “credge” or Chromium-Edge smashed together.
You can download the chromium edge onto your mac today, by downloading the beta release.
https://www.microsoftedgeinsider.com/en-us/download?platform=macosThe release of chromium edge will happen the first quarter of next year, and will even probably drop in January.
On windows, This new version will have a built-in IE11 compatibility mode.
I suspect many enterprises will adopt edge, due to its IE11 compatibilities, and the fact that it will be getting the latest updates.
I guess in general it's good to be avoiding the risk of monoculture in browsers, but of course the amount of testing needed goes up as well...

Tuesday, December 17, 2019

fork'in great!

So far, Fork, "a fast and friendly git client for Mac and Windows", seems to be much lighter on its feet than Sourcetree, but has a similar UI.

Wednesday, December 4, 2019

disabling a line of eslint in jsx

In my company's ongoing a11y efforts, there's an interesting paradox where we have a scrollable information div (scrollable via "overflow-y:auto;" css)- the Axe inspector says a scrollable div should be focusable - so I put tabIndex="0" on it so it could have the keyboard focus and allow arrow key scrolling. But we have an eslint rule: "jsx-a11y/no-noninteractive-tabindex" - the overflow scrolling isn't enough for eslint to consider it "interactive" I guess.

I don't know if there's a semantically better way of saying "this panel is interactive but for scrolling only", but to ignore the eslint I had to use the slightly odd in-tag comment JSX of
<div
    // eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex
    tabIndex="0"
    className={blah blah blah}
>
(Thanks to my coworker "Terrible Dev" Tommy for getting the syntax right)

Monday, December 2, 2019

next generation web styling

Some very cool stuff but man - the footprint of what you might need to know to read someone's CSS and JS is getting larger and larger.

You can see the stuff they talk about online at a.nerdy.dev/css-at-cds/

Friday, November 29, 2019

netflix ui and the culture of video binging

LIFEHACK: You can disable "autoplay" on Netflix, but the option is rather hidden - you have to login to the account via the web, click "Account" under the top right corner menu, scroll down to "Playback settings", and then uncheck "Play next episode automatically" and hit "Save". (Possibly you have to repeat this for other profiles in your account.)

I deeply resent "Play next automatically" being the default - partially because many shows throw in awesome music behind the credits, but mostly because of how it promotes and presumes a kind of mindless binge culture. And also, why not put it into the settings on the device? (Actually, being a per-device, per-account setting might be a useful option for many folks.) 

From a UX perspective, it's so suspicious that Netflix tucks it away in the web only - and then invented that semi-humiliating "Uh, are you still watching, or are you like asleep or dead?" dialog if you don't use the remote for a while, as if to make up for the crappy default binge mode UI. It feels like a gross way to drive up hours-watched metrics but at a terrible cost of human autonomy (I'm sure they have some user data about a big hunk of their base who like it, but damn man - I hate when tech so blatantly shoves us around for benefit of its corporate masters, very low-key user hostile.)

(Thanks to Sami (of OxGadgets) in the Lost in Mobile site's WhatsApp group who pointed out the setting and its web-only nature... so glad I griped about this issue there!)

Monday, November 25, 2019

easily remove unwanted objects

I was looking for software that could extract individual photos from a single scanned image file - someone suggested Photoshop Elements - their webpage shows off other tricks the software can do. Now I'm no marketing genius, but you wonder if their design team could have come up with a better example of "Easily remove unwanted objects" than "Your other child"...


will .org become a cash grab?

Internet world despairs as non-profit .org sold for $$$$ to private equity firm, price caps axed.

This disturbs me greatly. Organizations get very vested in a particular identity; there's a real hostage-taking situation possible here.

only connect? nah, bro

Apple Is Designing for a Post-Facebook World - I'm thinking about this in terms of the IDGuy video on the Apple Watch I posted the other day, the one complaining about watches bossing us around and making our schedules of human activity for us. I find that even more pointedly here in this link:
A similar thing happens with updates to how iOS handles photos. For years, Apple has been able to spot the faces of friends and family in photos you took on the iPhone. But now, if you view a photo with friends inside, Apple will suggest you message it to those same friends. That’s because the OS is thinking in terms of relationships. It knows you very well want to share a photo with friends in it with those friends.
I really find that kind of thinking intrusive... and I'm not a huge privacy guy. Like I could imagine a human assistant doing that kind of thing, but until we had some thing with a human-seeming touch, rather than just a neural net clever algorithm, it's going to be "Guess-What-I-Mean", pushy jerk of a UI, and not worth the privacy we give up for it.

Friday, November 15, 2019

when technology controls us

On his excellent Lost in Mobile site, Shaun posted Industrial Designer Critiques The Apple Watch and this video:

I cued it up to the section Shaun urged special attention, the designer kind of protesting about folks having to be told what to do to manage their health via a watch. On Lost in Mobile's related WhatsApp group, Bob said:
So I listened to the last 5 minutes. For those who didn't listen, and Shaun please correct me if I'm wrong, my main take away is that many of today's products tell you what to do and therefore absolve you of your responsibility to do things on your own. He likened it to being governed and controlled as in Orwell's 1984 combined with HAL 9000 in 2001: A Space Odyssey.
In a way he's correct, but I could argue the same thing about almost any technological advance. A car absolves you from having to get about by horse and buggy which in turn absolves you from having to walk a long distance. Are we better or worse off. As often is the case, some of both.
My response was:
Fun topic! I too only caught the last 5 minutes. First tangent: he bookends his main complaint with a smaller one of how he'd prefer a watch to just be a watch - none of this extra functionality with its implication of obsolescence as new functions show up. But that reminds me of that old comment about smartphones - that we had essentially reinvented the pocket watch! So the mere act of telling you what the time is is not as crucial as it was...
I remember in college (in the 90s) sort of consciously deciding NOT to wear a watch, in part because I wanted to avoid the regimentation IDGuy talks about.  Most classrooms had clocks, or I could ask someone, and just have a more human and organic sense of time (which is why I still think about making clock faces that put the time into fuzzy words not precise numbers)...
I think that my avoidance of watches mirrors his avoidance of smartwatches, that watches are kind of more proactive -- to use some jargon it's more of a "push" technology that proactively alerts or commands the user than a "polling" technology that has the user decide to check the device. And that issues of healthy moving and eating are better left to people making their own habits.
I'm not sure how many people take the step counting and the diet advice (is that what they do?) and "stand up now" THAT seriously? Like is it mostly aspirational for most people, like a usually short lived regimentation that hopefully modifies their internalized habits?
Also the whole thing reminds me a bit of anti-GPS arguments, that we're all more dependent on devices and less capable with maps. And it will not be good if kids grow up helpless without GPSes, just the way it's kind of sad many kids don't read time off of traditional clockface... but on the other hand, I was terrible at navigating w/ my car for the decade of driving before I got my early GPS... I didn't magically gain capabilities, I just puttered along, and was often stressed.
But like with teaching math to kids, the sweet spot is getting people to use technologies that augment rather than replace cognition - never skimp on the steps of teaching estimation, even as you still let them use calculator more often
Interesting stuff! Let me know if you want in on this gem of a WhatsApp group and I'll see what I can do :-D

quickly disable javascript in chrome devtools

Something I've had to lookup:
In chrome, if you're already using devtools a lot, the quickest way to disable to Javascript is hit cmd-shift-p (or ctrl- on Windows I guess) to bring up the tools Palette and then start typing "Javascript", the command shows up in the menu and you can then easily select it. This only applies to that current devtools window I think.

Tuesday, November 12, 2019

ui gripe list: ikea

I just want to say, IKEA has a very odd sense of taxonomy. I really don't think most of these things should be showing up for a search for "bookcases", do you?

Sadly, none of the filters under "More filters" are something like "actually show me the damn book cases, thanks."

I know IKEA is kind of apathetic about selling online - event the print catalogs are more of an aspiration-provoking encouragement to get you into their so-well-furnished ratmazes, and maybe get some tasty meatballs to boot. Still - this is ridiculous. I just want to look for a bookcase!

UPDATE: maybe because my first search was for "bookshelves", I got stuck in this place? Doing a search from here for "bookcase" got me much more sensible results - but the UI really made it look like I had drilled down to bookcases already...

Wednesday, November 6, 2019

easily finding the big ol' files on a mac

Since Sierra in 2016, MacOS has a pretty easy way to find the big files on your computer- Apple menu, About This Mac, Storage (tab), Manage... (button) - Large Files is just a flat list of the big ol' files, and "File Browser" can tell you just how much you're using for that Dropbox folder or whatever... anyway I found this more straight forward than some other disk usage tools I've tried before - getting rid of old iOS app backups and moving website snapshots to an external disk freed up like 100Gb on my 500Gb Macbook Air!

Monday, November 4, 2019

okta enters my ui gripe list

Both my manager's manager and myself were confused by authentication program Okta's dialog, after we had entered our Okta password (which incidentally, was a little underbranded, I didn't realize I had to enter my new Okta password and not one of the other company affiliated ones.)

That kind of looks like a code has arrived at the device, then you would enter it and hit "Send code" (as in back to the server) but no - you hit "Send code" first, it goes to your device, and then you enter it under "Enter Code", and then hit verify. It's just weird for it not to go in left to right reading order....

Friday, November 1, 2019

halloween 8-ball

This year my sweetie Melissa wanted to be a Magic 8-Ball:

I wrote a little webpage we stuck on an old iPhone so she could have a working answer window...

You can see (or even use) the webpage here: 
You can tap or click and it will random select one of the 20 authentic 8-ball responses

My first thought was to have her wear an iPad, and have me make up a triangular version of the answers, but she was concerned about the comfort, and I'm into being time efficient, so we decided to go with a simple square display on an iPhone (we wrapped the phone in electrical tape and attached a ribbon to it.)

I thought about using P5 but realized CSS could do the trick, using an animation fade in from black, and then just poking at the CSS (including using vw font-size units) and javascript until I was happy with the result. Then we added it to the homepage of the iPhone, which gets ride of much of the browser cruft (there's still a small battery indicator, or maybe we covered that part with the electrical tape?) 

Thursday, October 31, 2019

jump start for voiceover (macos + ios) and the rotor

Doing a lot of a11y work at work, and so I made this page to help people over the hurdle of using a voicereader...

Getting started with VoiceOver can be daunting at first - for people used to traditional screen/keyboard/pointer device/touchscreen, the amount of audio information coming in can be overwhelming, and even the most basic of touchscreen operations - for example, entering the passcode on the lock screen - will require extra steps.

iOS:

Go to Settings | General | Accessibility and set VoiceOver to On.

Doing so fundamentally changes the UI. A "What Widget Has The Focus" is added. Previously, tapping an onscreen button clicks it. Now, doing so highlights the button and the voice reads the caption. To click the currently highlighted button, you double tap the screen.

On the VoiceOver screen,. you can select "VoiceOver Practice" this screen will let you get a feel for how flicks - left and right to select previous and next and double tap for activate are the most important, but two and three finger flicks have various meanings as well for where the voiceover will start reading.

You may be tempted to just use the touchscreen as more or less normal, just switching tap to highlight, double tap to activate, but it is probably better to get practice using the flick navigation as well.

MacOS:

Go to System Preferences | Accessibility - VoiceOver is in the sidebar, so click Enable VoiceOver in its panel. You might try VoiceOver Training here as well.

PROTIP: the default "enable VoiceOver" keybinding is cmd-f5. However, MacPros with the touchbar might find this fiddly - you might consider going to System Preferences|Keyboard|Accessibility and setting "Turn VoiceOver on or off" to cmd-5.

Back to the main VoiceOver dialog - open "Open VoiceOver Utility". Here you can select the "VoiceOver modifier". Personally I prefer Caps Lock to the shift/command/option bundles, but it's a personal preference - but one you need to be comfortable with for activating the rotor. From here on in we will follow the convention of calling this key VO-

The Rotor is started with VO-U

The Rotor is the primary method of navigation for a certain part of the CarGurus audience. For the current webpage, It collates all the page parts of certain category into a flat list - for example all the links on the page, or all the form elements. (Under VoiceOver Utility | Web | Web Rotor you can see the categories you can have the rotor serve up) Firing it up on a webpage, you primarily navigate with arrow-keys: left and right to switch cartgory, up and down to select items. Hit enter to jump the focus to that item, and then VO-a to start reading there.

Apple has a page on VoiceOver Basics - I also found Deque University's VoiceOver Keyboard Shortcuts very handy.

After using the Rotor for a while, you may start to understand the purpose of many of the WCAG guidelines - providing structure that these tools can leverage, even apart from the visual display of the webpage.

Thursday, October 17, 2019

2 steps forward, 1 step back for photos in iOS 13

So two distinct improvements in the iOS camera app, and then one drawback...

I take a lot of short videos (basically B-roll for my "One Second Everyday" project) and one problem is that the indicator of "now recording" was always a bit too subtle, a little red dot next to the recording time at the top... (a time indicator that would stay at 00:00:00 for the first whole second of shooting, and so not providing timely confirmation that recording had started.)

The new one, a large orange coloring for the whole background of the recording time,  is much more clear:


Also, taking advantage of their new "pro" cameras triple lenses, they simplified swapping between lenses and their corresponding zoom levels through good old-fashioned on screen buttons:


Honestly, the new .5 zoom wide angle for their advanced models is a ton of fun, maybe even cooler than the night mode feature... it's often nice to get more of the background in a shot for context, and the subtle fish-eye effect can be visually appealing as well.

You can also do the old "pinch to zoom" to zoom-in or -out, and holding one of the zoom buttons brings up a dial interface - it's interesting how powerful breaking the abstraction of continuous zoom is... and empowering, since the user needs to learn that you can't use night mode with the ultrawide lens, for example. (You notice that fairly quickly if you're using the buttons to swap lenses, since the phone darkens the image to approximately what the end result will look like.)

The "one step back" is the EXIF issue I griped about earlier, how selecting a smaller size for upload changes the relationship of the EXIF rotation data and the image data itself.

hello world, the glory and the hubris

Slate continues a really excellent series on computer programming, Hello, “Hello, World”

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 https://www.eecs.tufts.edu/~kisrael/ 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:

#!/local/bin/perl   

$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 http://isotope.metafizzy.co/ - 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

20-in-1

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 = [
   {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;
        }
?>