Sunday, December 31, 2023

signals and the danger of the new hotness

Like this video shows, Signals are fairly promising, fair cool technology from Preact, don't go plowing in - if you naively look to examples from the Preact page, you'll quickly find cases that (including the initial example with the counter!) that don't work the same in React - I think React is back to using useSignal vs just the Preact-y raw signal aspect...

Wednesday, December 27, 2023

the ux of social media platforms

A piece on Usenet's the September That Never Ended mentioned Google Groups will be dropping Usenet support, which makes me sad. I haven't really used Usenet for twenty years, but for the ten years before it was really important to me, and I liked seeing it was still going on and to be able to look up the odd half remembered post.

Usenet really had a good vibe; the idea of bring your own client and use it across a variety of topic rooms, each forming their own community was great - my favorites were,,, and comp.sys.palmtops.pilot. (A friend of mine has a conspiracy theory that Usenet was too distributed and uncontrollable and so was repressed by the Powers That Be in favor of more centralized forms of social media...)

It made me think about social media forums I've lived in over the years. Each tends to encourage a certain style / length of post, has different types of message continuity (threads, etc), makes it easier or harder to recognizing recurring authors, and has different styles of if you rely more on following people or sipping from the main firehose.

I had a weirdly geeky urge to categorize what I've most used over the years... (my current favorite in blue) These are all based on my judgements of how I or most people use it:
Forum Post Lengths Crowd Size Author Identity Follow or Commons text vs image
Usenet Long Many Medium Groups .Sig Commons (per Group) Text
Livejournal Very Long Friends List Avatar Follow Text
Blog Comments Short Private-ish Name Commons Text
Slashdot Short Large (Geeks) .Sig Commons Text
Atari Age Forums Medium Medium-Small (Gamers) Avatar + .Sig Commons (in Topics) Text
Facebook Medium-Short Real Life (+Algorithmy) Name + Avatar Follow (+ Algo) Mixed
Twitter Very Short Very Algorithmy Avatar Follow (+ Algo) Mixed
Reddit Short Many Medium Channels Username Commons (per Channel) Text
Tumblr Medium Medium ("Mutuals") Avatar Follow Images
WhatsApp Short Private Avatar Commons Text
Slack Medium-Short Private Avatar Commons (in Topics) Text
Discord Short Private Avatar Commons (in Topics) Text
Facebook connects me with a wider range of people from all parts of my life, and despite the privacy concerns and what not, I appreciate how easy its been to share as many photos as I'd ever want, and get them in front of folks, abeit in a haphazard way.

I love the community of tumblr and it's my favorite source of stuff to repost on my blog - but I haven't figured out how to get "followed", so it's mostly a read-only medium for me so far.

Slack closed-garden is my favorite community types now - if you find the right bunch of people (that balance of people who post a lot, and maybe some people who mostly lurk but chime in) it's fantastic. (On paper Discord has the same potential, and is a bit more hip, but somehow the UI for threading and private messaging is horribly confusing, and the whole things gives me Reddit-ish "I can't follow things" vibes.

Thursday, December 21, 2023

two flavors of hacks

Earlier this year I put up a website for people in my community street band to store group photos. I used the metaphor of a "shoebox", something you can throw stuff into without worrying about the hierarchy or structure. 

The first pass I put up this summer was super barebones, just a form to upload a batch of images and give the batch a date and attach some other metadata (event descriptions / people shown, if it's "best of", etc) Then the gallery was just all the raw photos on a giant page.

The band threw a holiday party, and at the last minute decided to have a projector with photos showing. I hadn't really set things up for a slideshow - but my buddy's Window laptop had a slideshow feature, so hack #1 one was that by saving the big page as "Webpage, Complete" we had a single big folder with all the images, and could then let the local viewer do its thing.

Seeing the photos from ten years of activist and community banding were emotionally resonant, so I was inspired to push a little further... I used Imagick to build a simple resizer for thumbails in PHP:

        $image = new Imagick($originalFile); 

        $d = $image->getImageGeometry(); 
        $w = $d['width']; 
        $h = $d['height']; 

        $max = 150;

        if($w > $max || $h > $max){
            if($w > $h) {
            } else {


Ran that against the existing images and put it into the upload flow. 

Once I had that, I made a new thumbnails gallery - much better download times...

But I thought I could do better with the slideshow aspect - so enter hack #2, I liked the idea of a 2 dimensional display of all the thumbnails (I took the squareroot of the image count to figure out how many to display across) that would pick an image at random, pan across the other images to it, and then zoom in. I made it as a p5 canvas webapp. (All the source is embedded in the page) It runs a little timer and just goes from image to image, or you can override that with the arrow keys. Plus it displays the metadata from its batch upload, so you have a sense of the date and event that the Windows slideshow lacked.

When things settle down, I might refactor and make a more reusable version of this. I think panning across a bunch of small thumbnails is really evocative.

Wednesday, December 20, 2023

the ux of security vs convenience vs human fallibility

TL;DR: iPhone owners should get the iOS 17.3 update when it's available and activate the "Stolen Device Protection" - and always be suspicious when you have to use your passcode among strangers!

Earlier this year there were WSJ reporting about an iPhone scam - it turns out if someone has the passcode to your phone when they steal it, they can easily reset your Apple account password - at which they can lock you out of all your stuff, as well as blocking the device from "Find My" and stopping you from wiping your data from the phone remotely. 

(So the scam at a bar might be to offer to take your picture with your phone, then quietly use the button presses that would require you to unlock the phone with passcode, not just Face ID, so the scammer would have the code when the snatched the device.)

One might question why Apple would allow the mere 6 or so digit passcode to authorize a general password reset, but it turns out that a LOT of people are very bad at remembering their account password (which doesn't get used all that much.) So Apple kept it in as a reasonable backdoor for restoring access, since the number of people losing their critical password dwarfs the number of snatched phones.

Daring Fireball reported on an upcoming fix from Apple and I'm impressed by the nuance of the workaround... with Stolen Device Protection enabled, if you're away from a familiar location (home or work) the device will require face or touch id and then wait for an hour before resetting the main password.

I guess a shoulder-surfed passcode and stolen phone still allows plenty of shenanigans, but it's nice that this more egregious form is being quashed.

Friday, December 15, 2023

Care with trailing slashes for POST to index.php'd urls

Because PHP is so tragically unhip (and it's nice to decouple your webapp's URLs from any particular technology) I use a few different strategies for routing across PHP projects. 

I've rolled my own router before with some .htaccess trickery, but the easiest way is to set up paths via folders, i.e. using "index.php" to allow the path to be, say "/action/login/" and not "/action/login.php".

One gotcha with that - if you POST to that kind of URL and fail to include the trailing slash, your server may redirect your POST to a GET of the URL with the slash - dropping the POSTed data

Thursday, December 14, 2023

history of webhacks

A History of Weird HTML Hacks. I always scratched my head about non-semnatic tables being SUCH a nono (especially when some of the CSS hacks for the "Holy Grail Layout" were just ridiculous)

 (Catching up some newsletters etc hence all the small updates)

webdev advent calendar

 On the devlishly good site HTMHell, a web dev advent calendar

(Reminds me of my recent efforts to look cool by reviving my old ed emberley animals advent - that is still some great looking stuff IMO.)

One gem already from day 3: you don't have to nest buttons (and other elements) within their form! This might come in very useful on my porchfest site, where I have a secondary form on the main page for handling uploaded band images...

(Looking into the "future" (from this blog entry point of view) Day 15: The Ghosts of Markup Past is a fascinating way the html world could have gone.)

the new hotness of css

CSS got some new cool stuff in 2023 - I really appreciate when functionality is added to browsers core, in a cross-platform standards based way. (I've always trusted the stability of browsers more than any given flavor-of-the-month web tool.)

I am glad to see "subgrid" solving a problem I've long wondered about with card  based layouts; how do you get card subsections to align with their siblings (or rather, cousins) 

Also the text-wrap stuff seems great for preventing orphans in layout and getting better line balance in general...

Wednesday, December 13, 2023

making a minimal multiplayer online game via Rune

I just found out about a cool game platform called Rune - it lets you play online multiplayer games with voice chat.

And in so many ways it's an indie game dev's dream come true: Rune handles the game state management and reconciliation and voice chat and social and provide a simple, framework agnostic js api.

Their Quick Start page has a nifty bootstrap:

npx rune-games-cli@latest create
npm run dev

Not only does that spit out a basic React template, but using VITE the terminal pops up a QR code that you can scan with your device (if it's on the network) and test on-device.

But, what if you're not coding in React? Their tic-tac-toe example uses barebones JS, but has a fair amount of tic-tac-toe-ness logic you'd have to remove to start from scratch. So here is my minimal quick start:

A Bare Bones Game

Lets make a simple game: the first player to click their button ten times wins! The UI will be a single button, and then your score and the other player's.

A minimal Rune game can consist of just 3 files: an index.html file to hold the structure of the page, a logic.js that syncs game logic across players, and a client.js that runs on each page to handle events and update the game display.

Here is the HTML for index.html

<!DOCTYPE html>
    <ul id="scoreboard"></ul>
    <button onclick="clickNow()">hit it</button>
    <script src=""></script>
    <script src="logic.js"></script>
    <script src="client.js"></script>   

That is just an unordered list for our score and the opponent's, a button to press, and then we include the Rune js and then our custom code.

Next step: logic.js. This can be as simple as a single call to Rune.initLogic(args)

Barebones arguments are minPlayers, maxPlayers, a setup() function that initializes game state, and then a plain object of actions the clients can call.

    minPlayers: 2,
    maxPlayers: 2,
    setup: (allPlayerIds) => {
      const game = { scores: {} }
      for (let playerId of allPlayerIds) {
        game.scores[playerId] = 0
      return game;
    actions: {
      incrementScore(payload, { game, playerId }) {        

Setup returns the game object. In our case, we just need a plain object for mapping playerid=>score. Our only action is "incrementScore()". We will actually ignore any orguments passed in (to that payload placeholder), and just increment the score for the playerId.

Finally we can write our client.js. The critical line here will be a call to Rune.initClient(args) - at minimum, args should have a value for "onChange", alerting the client that the game state has updated. We only need three things:

function clickNow (){

function onChange({ game, yourPlayerId, players }) {
  const myScore = game.scores[yourPlayerId];
  const otherPlayerId = Object.keys(game.scores).find(key => key !== yourPlayerId);
  const otherPlayerScore = game.scores[otherPlayerId];
document.getElementById("scoreboard").innerHTML = `
      <li>My score: ${myScore}</li>
      <li>Their score: ${otherPlayerScore}

Rune.initClient({ onChange })

So clickNow is our click event handler that calls the incrementScore function we set up in logic.js. We then create our onChange handler that has some useful information: the game state, this player's id, the list of players, etc. 

We can immediately look up our own score via the game.scores object, since we know our player ID. But for a two player game we have to do a little "find()" logic to get the score value that is NOT ours. Once we have that, we will fill in the guts of our scorebard with two list items.

Finally, we call initClient with our onChange handler.

We're ready to give it a whirl!  If you don't have a server handy, from the command line you can just naviagate to the folder with 3 files and run
npx serve@latest

And boom! Rune has a nice side-by-side mode that pretends to be two (or more) phones at once:

Congrats, you made a multiplayer game! You can click either player's "hit it" button and see the display advance for both players.

But wait... we want first to TEN, this just lets you play forever...

Winner Winner Chicken Dinner

So how do we tell Rune the game is over? We just call Rune.gameOver(); That takes a single object that can be a map of playerIDs to player score... conveniently we have that handy, since it's the same structure of game.scores - so we can just add this line to incrementScore , right after we ++ the relevant score:
      if(game.scores[playerId] >= 10) {

Now Rune automatically constructs a little results screen for us:

So there's a game over, but that's not much of an accolade, it's not instantly clear who won. But, if instead of numeric values, we can set each player ID key to "WON" or "LOST". and Rune will record those results. Lets just write some easy to follow code to make a new onject mapping players to the end is the entire incrementScore function :

    incrementScore(payload, { game, playerId, allPlayerIds }) {        
        if(game.scores[playerId] >= 10) {
          const results = {};          
          for (let playerId of allPlayerIds) {
            results[playerId] = game.scores[playerId] >= 10 ? "WON" : "LOST";

Now we get a result like 

That's much more clear. (In the real Rune UI it will be fancier.)

Two's Company, Three is More Fun

But, a two player game might be a little meh. Lets kick it up to 4 players!

We'll start by updating our scoreboard logic in client.js to show the player names, since pretty soon it won't be just "us vs them":

Lets just replace the line updating the innerHTML:
  document.getElementById("scoreboard").innerHTML =
      .sort((id1,id2)=> game.scores[id2] - game.scores[id1])
        return `<li>${players[id].displayName}: ${game.scores[id]}</li>`})

We'll use some of those nifty js array functions - first we get the keys of the players object, then we sort by looking up on each playerId current score. Then we build the list item for each player, and join it all together to make the guts of the scoreboard.

One final improvement: it might be nice to highlight the players name within the list, so they can see where they currently are in the standings. Lets just do a simple conditional color style inline. Here is our new onChange, removing some of the old unneeded Us vs Them logic:

function onChange({ game, yourPlayerId, players }) {
  document.getElementById("scoreboard").innerHTML =
      .sort((id1,id2)=> game.scores[id2] - game.scores[id1])
        const itsAMe = id===yourPlayerId;
        return `<li style='color:${itsAMe?'red':'black'}'>${players[id].displayName}: ${game.scores[id]}</li>`})

The only difference from before is setting itsAMe if it's the current player in this item, and using red to highlight it if so.

Ok, one FINAL final improvement to the client - lets add a little CSS to the index.html to get a sans-serif typeface - we're not barbarians:

    body {
        font-family: sans-serif;
    button {
        display: block;


So now all we need to do is tell our logic.js that maxPlayers is 4, and handle what happens when a player joins or leaves.

    maxPlayers: 4,
Is a simple enough change...and just like we passed an "actions" object to Rune.initLogic() we can pass an events object with handlers for players joining or leaving.. all we need to do is add and initalized their score as they join, and remove it if they go.

  events: {
    playerJoined: (playerId, { game }) => {
      game.scores[playerId] = 0
    playerLeft:(playerId, { game }) =>{
      delete game.scores[playerId];

Here's the result... I admit I enjoy the character names Rune uses:

So, tadah! You should now have a solid base to know how to wire up your game to Rune's great player management system no matter what JS framework you are using.

the power of qr for local dev

Looking to pick up some React Native - one trick I've seen two places now (The quick deploy environment Expo, and then a particular game hosting environment using Vite) is you start a local server, then it gives you a QR code in the terminal that lets you hop onto your server via your device. That's pretty dope!

I love the intersection of 80s ASCII Art and the ongoing  revival of early-2000s QR Codes  (Reminds me of an old joke that is now outdate: - "What's a pirate's favorite visual URL encoding?" "Q-Arrrrrrr?" "No, they don't scan them either")

Also, at other places in their example code they just have you run 

npx serve@latest

which fires up a baby webserver. I will add it to my old piece on got python? you got a webserver...

Tuesday, December 12, 2023

browsers are too big

Interesting piece from three years ago about the impossibility of creating a new web browser from scratch - there would just be too much they'd be expected to do.

It's a shame, because I still think browsers in general are one of the best basic platforms for "open" type work; providing great experiences in an egalitarian way. I don't think a world where every interaction with a company starts with "download our app" (or maybe worse, as a plugin to a social media platform) is one anyone wants...

Thursday, December 7, 2023

the year in re-vue

Big year for Javascript. Who knew Vue would seem like the most stable framework out there?


random firefox gripe - no X to clear on input="search"

I really try to stick with Firefox; with so many other browsers going down the webkit route I think it's healthy to support an alternative.

But chrome and safari have a convenience function that's great... if you have 

<input type="search">

You get a something like

It's great... it's only there on :hover or :focus and when there's content in the box and for a search or filter it's super convenient. 

Here's a codepen that does it for stuff including Firefox... no js needed, but a kind of annoying amount of non-semantic markup and CSS. I can't figure out if it's being seen as a bug or as a non-standard feature they won't enable by default...

It's frustrating though. It's such a nifty quality of life feature, there's not a lot of downside that I can see...

Thursday, November 30, 2023

Repeat the word ”poem” forever

Extracting Training Data from ChatGPT

LLM-based AI is so weird - and its potential for security breaches are a known unknown . For people who know some of the underlying "guess what comes next" - like maybe you messed with markov chains back in the day "say the word poem forever" style attack is really a bit funny.

Saturday, November 18, 2023


I love making bespoke web applications as part of the Independent Web. Six years ago I made "chart-o-tron" for managing sheet music for bands. (Many bands get by with Google Files or Wordpress, but those tools are often clumsy in terms of browsing, preserving metadata, and mobile-friendliness.)

Chart-o-tron met several UI/UX goals:

* Easy upload of PDF and other files

* Keep music organized by category

* Allow bands to have private or public archives

* Capture metadata such as Lyrics, Key and performance notes

* Friendly URLs for a band's landing page

* Mobile-friendly UX, letting a musician scramble to a chart they need on their phone in a hurry

(Later I added an ability to create "setlist surveys", allowing bandmates to indicate what songs they'd like to have for a rehearsal or performance. I do love democratic empowerment!)

"Chart-O-Tron" has proven its value to my band over and over again, so I figured it was time to move to a proper domain, chart-o-tron dot org ( not sure if LinkedIn will eat the link: <> )

Also I made a major UX improvement: a shared password for each band, rather than the individual account system it was using. This was mostly to work around the challenge of adding new accounts and authorizations; most of my band's chart-o-tron page access is done anonymously sans login. Having to setup a hierarchy with other bandmates "blessing" a newcomers account to grant admin privileges was just a constant piece of UX friction.

(Also although I made it years ago, it was gratifyingly easy to pick up on the homebrew MVC system I set up to handle the routing, and have pretty URLs like chart-o-tron/band/bandname )

Usual plug: I continue on the jobhunt grind, so if your company might be looking for a React/TypeScript wielding UI Engineer with demonstrated fullstack bonafides (and full UX!) let me know!

Resume: <>

Portfolio <

Wednesday, November 15, 2023

end of coding, film at 11

The New Yorker had a piece by James Somers, A Coder Considers the Waning Days of the Craft about how GPT4 is empowering non-coders to solve coding problems. Many parts of my career path echo his.

He points out how once upon a time Squarespace and other tools empowered non-techies to make websites just by clicking around, and a set of medium-low-effort, sometimes high-paying work went away. There are some interesting challenges to bringing that same egalitarian nature to programming - many of them have to do with deployment and environmental context. There are some obvious risks to allowing half-baked code on your server! Some of those to the host can be mitigated by proper containerization but I wonder what ChatGPT would suggest for from scratch deployment for the non-programmer...

Somers mentions thow we seem to be cracking the long-pondered "natural language programming" problem - of which COBOL was one of the first attempts - citing Dijkstra circa 1978 and the imprecision of human languages.

But it feels like that equation changes somewhat with AI. As coder you're rarely solving unique challenges, you're solving problems very similar to what many people before you have, and LLMs are uniquely empowered to draw from that. They don't truly model the problem in their head, and so have all kinds of limitations, but they are able to get to "DWIMNWIS" ("Do What I Mean Not What I say") in a way previous systems have not.

Near the end of the piece Somers sounds a hopeful note for the programmer:

"Computing is not yet overcome. GPT-4 is impressive, but a layperson can't wield it the way a programmer can. I still feel secure in my profession. In fact, I feel somewhat more secure than before. As software gets easier to make, it'll proliferate; programmers will be tasked with its design, its configuration, and its maintenance. And though I've always found the fiddly parts of programming the most calming, and the most essential, I'm not especially good at them. I've failed many classic coding interview tests of the kind you find at Big Tech companies. The thing I'm relatively good at is knowing what's worth building, what users like, how to communicate both technically and humanely. A friend of mine has called this A.I. moment "the revenge of the so-so programmer." As coding per se begins to matter less, maybe softer skills will shine."

Here's hoping! For folks caught on the outside of the current boom-to-bust cycle, these sea changes are frightening. But right now, where I've had ChatGPT write me some simple one page apps, but also fall on its face on some similar problems, I'm optimistic I'll at least be able to ride out the rest of my career doing this kind of thing, with ChatGPT as an ally instead of a foe. But, my previous advice to young people: "uh, I dunno, maybe try programming? It always worked for me" seems more precarious than ever. 

Tuesday, November 14, 2023

lateral thinking with withered php

 If you were ever a fan of Nintendo, you might have heard of 枯れた技術の水平思考, "Lateral Thinking of Withered Technology" (sometimes given as "Seasoned" instead of "Withered") - Yokoi Gunpei's philosophy of steering clear of the "state of the art" but looking to what can be mass produced cheaply. Here is a great look at how he put it into practice ...

I think there are some parallel lessons for side projects here... when I hear about Pieter Levels who famously makes millions with clever and useful services written in PHP and jQuery, I am reminded that some side projects lean into learning the new hotness (in part because hiring teams might prefer less bespoke code and more following of the latest in the industry) but some projects might benefit from fewer industry dependencies and business relationships to maintain - and that an idea that brings value to customers can be substrate independent. 

Monday, November 13, 2023

TLDRing the TLDRs

 Last week I worked on catching up on my backlog of developer newsletter links (Mostly TLDR, TLDR Webdev, Frontend Focus and JavaScript Weekly. The first two I'm on the fence about; getting an update every weekday can be overwhelming if you start to slip behind.) 

Here are some of the most interesting links (a few are from other sources.)

From Akamai to F5 to NTLM... with love. - truly scary tale of some greyhat hacking. Hosting infrastructure has gotten so complex that truly weird vulnerabilities can emerge.

This article on lessons from tiny development teams got me thinking about Pieter Levels, who famously makes thousands and thousands from a few of his projects that found their niche, and yet his stack is PHP+JQuery. Here's more on side hustles.

Habits of Great Software Engineers got me thinking about the constant tension between thwarting long term technical debt and always looking for the flavor of the week - see also this piece on striving to keep well away from the leading edge of technology

Solve the problem, not a different more difficult problem - know when to do a one off.

Nice tutorial with interactive bits on Conflict-free Replicated Data Type - basically the data structure you need to keep shared editable resources online in sync. I think I independently reinvented some of these core ideas for my own, a shared online whiteboard drawing programming that made a lot of use of synchronizing.

Good reminder of six high level concepts any senior engineer should be fluent in

Interesting article It's 2023, here is why your web design sucks. - the design and the engineering got too far separated, possibly for sexist reasons.

More web tool kit specfic

Friday, November 10, 2023

best syncing todo and information management systems

 I've always been interested in personal information management - the tools and tech people use to keep track of what they need to do and things they want to keep track of over the long haul.

Yesterday I spent a few happy hours on some long overdue updates to "jkdb", a bespoke data table editor I keep on my personal site. (It's a little bit like a series of excel sheets, but I never liked how Excel handled wrapping and columns, plus each sheet has more specific form inputs.) I've been using it for YEARS, for password tracking, media journaling, etc, and now its tables finally have "in place" editing so I don't have to bounce to a different screen to add or edit. (Like I said, I made this thing a long time ago)

Since I first made jkdb, a number of apps have emerged covering similar spaces, but with a UX encouraging different uses. Here are the best I've integrated into my life, things that have proven their reliability over the years:

Simplenote: an incredibly minimalist, multiplatform pure text editor with awesome syncing. Has a great web interface as well as desktop and mobile clients. Automattic has been running it as a free service for years, but even if they dropped it, they make it easy to export *en masse*. No folders; it relies on fast search and tags. (It's kind of like the old days when you might have a giant directory of notepad.exe txt files, but faster) I like it better and trust it more than more graphical products such as Evernote or Apple Notes. (Apple notes is my favorite sketch pad, though.)

Tot: an even MORE minimalist text editor, syncing across Mac and iOS. You get 7 color coded memos- no more, no less. It's free on Mac and a one time fee on iOS. I find it awesome as a free form todo list, always at hand on Mac menubar or iPhone homescreen. I use the first memo for general Todo, and other pages for "media to see" or "music to get" etc.

2Do: Solid classic "checkbox" task tracker. Tot's free form text is great for most things but some tasks recur or should show up in a time-sensitive way, and 2Do nails it. It has the best UI I've found for having tasks in different Categories, but displaying the contents of all categories on a single scrollable list, without having to click on it. (Just added it as a homescreen widget)

Google Docs: this is my favorite for anything I have to share and co-edit with others. Its iOS client is a bit wonky, but in general it's a great and accessible choice.

So those are my 4 favorite information management apps, plus my homebrew solution - each has a specialty and it's almost oddly easy to get the muscle memory of what information is living where.

What apps would you advocate for, and what makes them stand out from others?

(Usual plug: I'm a React/Node/Typescript saavy UI to middle layer developer looking for a fulltime gig in Boston, Remote, or Hybrid... if you your team likes the idea of a programmer who really thinks about interactions and enjoys making ideas into code, hit me up!)

Tuesday, November 7, 2023

art by letters

I do think it's obvious (sometimes painfully so) that ChatGPT and other LLMs/neural network based tools will be an increasingly important tool in the coder's toolbox. With the Dall-E 3 art plugin, they can also be used for a bit of fun. Here are three images of my long-term mascot "Alien Bill" - well, two out of three ain't bad... when the scene grew more complex I couldn't persuade the system to stop reverting to a more classic alien form... cute rocket ship though!

I had similar problems getting GPT-4 to construct a single page Leaflet webapp - the core was solid, but each iteration lead to regressions of earlier functionality, and data structures were munged rather than evolved.

This difficulty in prompt wrangling - where you run into the fact that LLMs are not REALLY modeling the world "in their head", but getting extraordinary results based on guesses about what seems likely to say (or paint) next - poses the $50,000 question: is this challenge fundamental to LLMs? Are we near the top leveling off of an "S-shaped" curve? (And/or is there any other form of general AI waiting in the wings?) Or do we just need to be better prompters - either by improving our own human skills, or by utilizing intermediate "prompt-helpers"?

PLUG: I am a Boston-area software developer (with a focus on UI and UX, but proven abilities across the stack) looking for a team. See my portfolio at and let me know if I might be a fit for your company's team!

Monday, November 6, 2023

the possibilities and limits of ChatGPT for MVP prototypes

"If a thing is worth doing, it is worth doing badly."  --G.K. Chesterton (patron saint of MVP prototypes)

TubaChristmas Map 2023: 

Last week I took an afternoon to make an MVP prototype of a map and listings page for TubaChristmas (a collection of local annual events that gather up low brass musicians to play holiday music) As a tuba player and programmer, I would love to help improve the process of site registration and management (which is said to be stuck in a fax-machine era) and thought a quick and dirty prototype would open people's eyes to what could be done.

The first crucial step was to scrape the data from the TubaChrismas listings page:  (click and enjoy the 1998 www vibe!) Since that page was made with a template it wasn't too hard to cook up a DOM-to-JSON converter in the browser but the data was still notably messy -   promotional descriptors being included in the city name, for example.

After cleaning the data by hand, I had ChatGPT write code to update the JSON structure with Lat and Lng in GoogleMap APIs. (I had a API key handy from my work on Porchfests)

For the page itself I decided to give ChatGPT a try in making a Leaflet based solution. The process started off promisingly (first prompt: "using JS and HTML (maybe a vanilla js library) I'd like to: show a map of the US with clickable icons on certain cities. what's the easiest way to do that" and then iterating) but got bogged down - each cycle of refinement tended to create more regressions, and when I finally gave up and went to take over the code, the internal structure was extremely "Jr Developer". I did a minimal amount of restructuring and hacking to get to the filtering/search w/ map highlighting I wanted to prove out and called it a day.

I guess I was heartened by encountering ChatGPT's limits (and this was the paid "GPT4" version) - on the one hand maybe it's just pointing out my own prompt engineering deficiencies, but researches have shown that LLMs can't really create beyond their own training models - and so I think the future is collaboration and refinement of what it means to be a coder.

I do think at this point if you are a programmer who isn't at least using a LLM assistant as a sort of sophisticated "StackOverflow" - providing customized answers to the API details that aren't worth keeping in your own head - you are at risk for falling behind. (My preference is using ChatGPT as a standalone consultant, vs the Co-Pilot hyper-autosuggest model integrated into the editor.) There's absolutely time and effort efficiencies to be had on all levels of work.

Also a reminder: I am on a job hunt, so if your team could use a 10+ year veteran UI/UX engineer who is comfortable coding React/TypeScript/Node but also aware of how automation can help team efficiency - where to trust the machine and when not to - let me know! Available for work near Boston, hybrid, or full-remote. 

Wednesday, November 1, 2023

iterating and reacting

I am working to keep my React skills sharp while I am still on the jobhunt. (Also, some interviewers ask me to bring along some code to discuss, but the vast majority of the React code I've produced I'm not at liberty to share.)

One practical tool I've iterated on is a "pill tracker" - a tool that let me make up printable sheets to keep in the kitchen to keep track of Dean's (RIP, dear kitty) sometimes complicated pill and feeding schedule.

My first approach was the barebones pill charter, and used this exremely geeky textarea-based UI:

The UI, with its ad-hoc syntax, is obviously not very friendly for most folks, but has some advantages. For one thing, it's very easy for the programmer :-D But also it's very easy to share the a schedule with someone else, and you can apply other text tools if you like - copy and pasting lines is actually a very fluid way to work.

Later I made med-sched-grid. I upgraded the look and feel:

More importantly it actually has a UI. Items are constructed as a list on top, with the grid underneath. Maybe the most useful feature is being able to jump ahead a block of time - combined with how it uses localstorage or links to restore what you entered last time, getting the next month's schedule is just a click away (this was useful for the kitty meds with a 2 week pace)

I wrote that as a everything-in-one-html-document app (including the svg graphics) which is a fun, if somewhat gimmicky, way of coding.

I decided to rewrite in React and came up with med-plan-print. I realized the old UI was a little weird because there was only a loose connection between the list at top and the grid itself. My first thought was to allow each column to activate a modal editor, but that was a little ugly, and still separated the editing from its context. With a little cleverness I could allow a more direct connection between the column and its editable metadata:

 (the triangle is made via CSS cleverness, and rests inside the TH for proper locating, but then is shoved up to connect it visually with form.)

The editor is actually semi-modal; the default state of the UI use the same space for the overall controls:

If started fresh, the page opens up the blank editor. Then that uses the same green which is a visual hint for the plus button (I'm still debating if each of the round buttons should have a caption underneath, or if the iconography carries the load.)

This was built in React using parcel and Create App. I threw in some tests (the "going backwards in time" calculation was especially fraught) and put in on github.

Sunday, October 29, 2023

magic 8ball redux

 I made an interactive Halloween costume this year - The Magic 8-Ball!

I had a friend who makes drummer and equipment gear construct a wearable iPad harness, and then using P5.js I wrote a fullscreen webpage to pick one of the classic 20 answers and display it in the classic triangle:

It was fun to iterate on from a usability and "surprise and delight" standpoint:

1. I didn't really want to "shake for new answer", so I did a simple tap that made the new answer fade in and rotate into place. I then added some logic to have the cursor or finger shove the blanked + dimmed triangle around until released (just for a little showmanship as the person was thinking of their question)

2. I made variants to rotate everything 90 degrees so by locking the screen rotation, the straps could obscure the status bar.

3. At the party, I thought of one great feature: a cheat mode. Now, tapping inside the circle gives you a fair answer, but if you click outside the circle, the top half of the screen gives you one of the 10 positive answers, and the lower half gives you one of the 5 negative responses. So based on the question someone asks, you can covertly trigger a yes or no answer based on whatever is funniest.

So, classic example of the importance of testing and iterating!

(I am still on a jobhunt, so if your team might use a UI Engineer who can do the bread and butter React/Node/Typescript as well as throw in a Canvas curveball like this, hit me up!)

Thursday, October 26, 2023

parcel 101

I do like and parcel, but I always forget that it presumes that it's at the root of the server (especially vexing since one of the charms of a lightweight thing like this relative to create-react-app is making a small little thing you can deploy anywhere)

The fix is easy, add a --public-url argument in package.json:

"build-prod": "parcel build src/index.html --public-url ./",

devops from the days of cheap money

15 Years of near 0% interest rates has done weird stuff to our economy, and our sector.

I've been around long enough to hear tales of the first dot com boom, and the following bust - there was a total "land grab" / don't worry about profits mentality then - Amazon was one of the most notable examples of that. (I remember thinking it was weird when they first went to "dilute" their brand from being THE place for books online to other random products... which shows you why I'm a UI/UX Engineer and not a business guy.) So hearing how we had dot com boom (and bust) part two thanks to cheap money makes a sad kind of sense, and many of us our suffering from the industry hangover now.

This video focuses on the implication for DevOps, anything for scale and costs be damned. One former employer of mine really went nuts when they pivoted to AWS Cloud -  just set up services willy nilly for dev or qa or prod or just for the hell of it really, burnt through so much cash. Everyone got some stern "talking to"s and policy was locked down. My following employer was notably more cautious, and ran their own server farm in a datacenter. They were pretty stodgy about it (and were blurring some of the traditional boundaries by having dev engineers overseeing pushes) But maybe some of the benefit you have with that approach had to be weighed against being out of step with the industry, and so generally relying too much on homegrown approaches, and I believe they are doing PoCs for cloud stuff now.

And It's still a challenge for my side project work. For my critical sites, I still depend on old school monolithic deployments - and apply a lot of basic static rendering and pushing stuff to the client for the stuff the traffic that I know will be particularly spiky. In part to avoid cost pitfalls and setting up new business relationships, and also because it seems like you could burn a lot of cycles figuring out what to learn in Jr DevOps land...

(And just to add, I am still on the hunt for a Boston-area or remote UI/UX Engineering Role. I have deep and wide experience on household name websites and small fiesty startups, so if you think you might know a fit let me know!)

Tuesday, October 24, 2023

checking mail deliverability

m@ailgenius seems like a neat way of seeing if your outgoing email is likely to end up in spam folders...

Sunday, October 22, 2023

boston weekend rain

For grins I wanted to see if everyone's intuition that "dang, it's mostly raining on the weekends!" was correct. (I know there are some theories related to auto traffic that try and explain it a bit - it's probably not JUST Murphy's Law...)

I handscraped some data from Wunderground, got ChatGPT to help me massage it into JSON, and used P5.js to come up with the attached chart, where darker bars mark the weekend. 

Conclusions: Indeed, 15 out of 20 weekends going back to June had rain. 

Eyeballing the chart you can see that the blue rain bars do seem a bit likely to line up with the gray.  (rainfall averages and days with any rain listed below)

(Just a plug, I am on a job hunt for a UI Developer role. So if you know a company that might need someone who likes making data approachable and can throw together a static or interactive piece in P5.js to go along with the bread and butter React/TypeScript/Next.js, maybe I'm the one for them!)

Weekends: 15 out of 20
Su 0.258 (10 out of 20) 
Mo 0.169 (10 out of 20) 
Tu 0.209 (11 out of 20) 
We 0.194 (7 out of 20) 
Th 0.043 (4 out of 21) 
Fr 0.062 (6 out of 21) 
Sa 0.245 (9 out of 21) 

Friday, October 20, 2023

the cost of free

Just paying Figma $15/month because nothing else f***ing works - very long piece about open source and trying to keep parity with commercial options. (Along with some bits that bring to mind the old trope "Linux is only free if your time has no value", along with the general improbability of learning the guts of a piece of OSS well enough to make customizations you want.)

Specifically the focus is on Figma, and the punchline is the headline - that sometimes it's better just to give up and use the commercial options. But I was also struck by a tension between precision and responsive design: It's funny because Figma can create friction in web work because it is still is very not HTML native. That flow from its multi-platform nature (like Android or iOS have different layout paradigms, and of course print wants absolute control of everything in a way HTML doesn't) but still, if like 90%+ of what you do in Figma is for web and mobile web,  it's not clear the balance is optimal (Figma updates this summer start to get working a bit more responsive-design-erly, but still.) But interestingly, for the article's author, Figma's competitor Penpot's actually-do-things-in-the-CSS-model is a bug not a feature, because of him wanting more precision. (The article links to this piece by one of Figma's inventors that points out where it innovated.)

But the article is less about that and more about the pain of installing server-based OSS stuff - and reminds me of some other domains where, as reviled as the language is, PHP-based systems have a far lower bar to deployment and modification (specifically I'm thinking of Convention scheduling tool Zambia vs its Ruby on Rails competitor Planorama) I've been retooling to do more of my side project full stack work on more Enterprise-smelling platforms, but sometimes I question if it's a good tradeoff - as always the questions are of scaling for performance and managing complexity over time, but I think the legacy stuff I still sometimes turn to can be better on those fronts than many people assume.

Tuesday, October 17, 2023

one of the ugliest of php's quirks

 I do think that A. "web scripting" is a decent approach to many projects, B. PHP is one of the more seasoned ways of doing web scripting but I ran into one quirk that is horrible:

In short, you can't have a POST variable with a key containing period, space, or open square bracket. This harkens back to the (now safely deprecated) bad old dumb days of automagically exposing POST values as global variables... a horrible practice for sure, but now even though everyone reads values from $_POST and those keys could be anything, keys in that array will those characters converted to underscore...

Saturday, October 14, 2023

i do declare

 I think declarative has been lost to marketing and basically now just means "good"
--Jamison Dance

Friday, October 13, 2023

it was the worst of times, it was the worst of times

Wired reports Finding a Tech Job Is Still a NightmareTech companies have laid off more than 400,000 people in the past two years. Competition for the jobs that remain is getting more and more desperate. 

Welp, that's not great!

I guess I'm lucky because I'm not in financially dire straits, and can look to retool and suspect that "this too shall pass".  My rosiest retirement plans have fallen by the wayside, but still.

Like you can never shake the suspicion that you could be hustling more (but- you'll never be sure how much that would have helped, along with the fear of taking a truly dire desperation role)

So a faint silver lining to this period is that an employment gap in this time probably won't look godawful, and to the extent you can enjoy the time, you should.

And the industry is still bigger than it was in 2019... but because the industry's eyes got bigger than its stomach during the quarantine-field boom, the indigestion right now is truly horrible.

Friday, September 29, 2023


 ChatGPT was able to point me to a half-remembered 2008 Wired feature article Free! Why $0.00 Is the Future of Business.  (ironically enough I think that's a pay-walled link, but I am a long time subscriber to the magazine, grew up with it and the internet)

Now, I have little respect for what Elon Musk did to Twitter: Twitter was invaluable as THE breaking news source, the place to follow individual subject matter experts, a fun place to try and go viral with the perfect bon mot, and a way for ordinary folk to talk back at companies out in the open, where the company's reputation was a bit at stake. X is barely any of those things, and has tacked from medium left to hard right in a lot of ways.

But - I think Elon Musk right, that the way "free always wins" (free as in beer, not as in speech) has really hurt our society. I'm not surprised: no one in their right mind feels comfortable with a meter running. It's nice to dream of an Internet where promoting endless binging wasn't the norm and where the ad folks had less sway, but I don't see anyway we get to that from where we started, and I think Wired laid that out clearly 15 years ago.

(I do like Stewart Brand's line: "Information Wants To Be Free. Information also wants to be expensive. ...That tension will not go away.")

Monday, September 25, 2023

the other september that never ended


favicon and "add to homescreen"

RealFaviconGenerator seems to far and away the easiest way to make up favicons and the "add to homescreen" icon for iOS + Androud etc. Make up one big image (that will scale down well!) and go.

I collaborated with Craiyon to make this favicon / logo for BABAM Book:

The prompt was A minimalist icon showing a music note and a book with the letter B - the background is black and yellow is strong in the other parts. Then I flipped and cropped it. It's a little random but it gets the job done (the main job being making a more recognizable icon when I am hurriedly looking for the link to get to sheetmusic via my iPhone..

Saturday, September 23, 2023

on the limits of the ChatGPT crowd

 Elegant and powerful new result that seriously undermines large language models - LLMS such as ChatGPT make interesting mistakes that point out the limits of what they do, and may prove to be a sidebranch on any path the general "true" Artificial Intelligence. They aren't building their own model of the world "in their head", instead they are phenomenally good at predicting at what our model of the world - communally established in the form of written language - would say when confronted with a new variation.

But that lack of model is weird! The case study they give (which I duplicated via ChatGPT/GPT4) is that it can't tell you who Mary Lee Pfeiffer is the parent of... but then can tell you that one of Tom Cruise's parents is Mary Lee Pfeiffer. And this kind of gap was predicted in discussion of earlier forms of neural networks - which may indicate it's a fundamental problem, a shortcoming that can't readily be bridged. 

It reminds me of Marvin Minsky's late 60s work with Perceptrons. ChatGPT was able to to remind me of the details - 

Minsky and Papert proved that a single-layer perceptron cannot solve problems that aren't linearly separable, like the XOR problem. Specifically, a single-layer perceptron cannot compute the XOR (exclusive or) function.
Of course later multilayer networks (with backpropgation, and later transformers (not the cartoon toys)) overcame these limits, and gave us the LLMs we know are establishing our wary relationships with. So who knows if there could be another breakthrough.

But the results we get with LLMs are astounding - they are a type of "slippery thinking" that is phenomenally powerful... Hofstadter and Sandler called their book "Surfaces and Essences: Analogy as the Fuel and Fire of Thinking" and I would argue that so much of intelligence is an analogy or metaphor - far branches from the human situation of having to make a model with us as a bodily agent in the world. 

And as we find more uses for LLMs, we need to be careful of adversaries thinking one step ahead. Like the once seemingly unstoppable, alien intelligence of AlphaGo derived Go players can be beaten by amatueur players - once other machine learning figured out what AlphaGo can't see on the board. 

Suddenly, Captain Kirk tricking intelligent computers with emotional logic doesn't seem as farfetched as it once did...

Tuesday, September 19, 2023


 A Brief, Incomplete, and Mostly Wrong History of Programming Languages:

1995 - Brendan Eich reads up on every mistake ever made in designing a programming language, invents a few more, and creates LiveScript. Later, in an effort to cash in on the popularity of Java the language is renamed JavaScript. Later still, in an effort to cash in on the popularity of skin diseases the language is renamed ECMAScript.

Tuesday, September 12, 2023

requiem for visual basic

Hacker News had an interesting Why Did Visual Basic Die? thread.

The highest rated response is a link to this great history of Visual Basic - fascinating to hear the history of how it started as a form designer, with the BASIC bit as a johnny-come-lately add-on. (Also interesting to think of the VB relative to the Mac's HyperCard.... it probably says a lot about the Mac vs Windows communities.)

I picked up Visual Basic on my own during college - it was a world easier than the 16-bit "Charles Petzold book" programming I had been doing for the oncampus software studio - and I designed and taught a for-credit course in it, via Tufts University's "Experimental College". 

I had two main paths leading from VB:

One was "professional", making utilities and leaning on its famous form designer. On the one hand, you couldn't do THAT much with the designer, mostly you just laid out controls at fixed sizes and locations, and everything just looked like a big old dialog. But counterpoint: only now are even professional designer tools such as Figma really incorporating thinking about responsive design and lots of resolution variation. And VB really was an egalitarian tool in many respects. Or I think about small companies that could make a living pulling together some VB forms and a database to right custom apps for other small businesses.

Second was "recreational" - ignoring the controls and just using it it to provide a big old window to draw graphics directly on, to make up a game or toy. (You can still see a few things I made in it at my old Alien Bill Productions Toys for Windows page.) This was a continuation of the fun I had on the old 8-bits (Atari 800XL, Commodore 64) and which I later continued with Java Processing and then onto P5.js (And my site has the fruit of those efforts.)

Microsoft famously bungled the pivot to the web-centric world, and VB was one of the most prominent casualties. From that point of view you see a move from the big servers of the 60s and 70s to the desktop of the 80s and 90s, and then back... I'm not sure what happened to the hobbyist vibe - you could do some stuff in Python locally, but I suppose for server stuff you were back into PHP, which meant you had to find appropriate webhosts, so the barrier to entry was a bit higher...

Anyway, Visual Basic was a fun and empowering piece of software.

catching up on links

How to pass the interview for software engineering roles in Big Tech - Handbook

on React Server Components - The key thing to understand is this: Server Components never re-render. They run once on the server to generate the UI. The rendered value is sent to the client and locked in place. As far as React is concerned, this output is immutable, and will never change. -- that explains a lot, actually. Like you can't use useEffect in part because you don't NEED to- side effects are embraced. Still seems weird that it's so tightly bundled with specific server apps.

How WhatsApp did what it did

I do wonder what the current state of PHP is relative to where I should go next in my career.

The 99% of Developers - like the idea that some stuff is aspirational, i.e. few people really fully practice what they preach

hatin' on Tailwind. I admit I haven't used it enough to see if my kneejerk bias against not being on the metal are justified

WebComponents rah rah rah! I do like the platform agnostic aspect, though Web Components have been "the near future" for like a decade...

I also wonder what I should know about Svelte

Monday, September 11, 2023


RIP Molly Holzschlag "the fairy godmother of the web

getting to quality

 Posted on a small private Slack I enjoy (sometimes I think small Slack workspaces are like the future of social media) was this Forbes piece on how important code quality is, and how organizations absolutely Mug Peter to Pay Paul if they rush things early.

My further thoughts were this:

I think there are 2 ends of a spectrum to "what is code quality" and how to improve it:

1. the first is leans reductionist, like doubling down on confirming that each fine grained component lives up to its contract.

2. the second is more holistic; confirming the higher level pattern is appropriate to the problem space it is trying to deal with, with good inspectable flow and and not too many work arounds.

I think as code bases age and show evidence of needing updating, you tend to big pushes of the the former end. And so you get mandates to increase unit test coverage, and lines like "metrics-wise, we expect as line of code coverage increases faults in production will go down" which is.. quite a bit optimistic. (I am strongly in favor of higher level testing - the closer the context your tests are running in to what your production environment is, the more confidence you can have that whatever refactorings you have made preserve the key business values...)

But when trying to achieve improvements in the latter, holistic category... I think of the counter argument that starting a rewrite from scratch, armed with the experience of having done it once, often doesn't pay dividends because it's weirdly difficult to capture all the small improvements you made for edge cases. And so the obvious cost of doing everything over is still at risk for repeating many of the same mistakes and making its own cruftiness. (I imagine this is highly dependent on the circumstances and problem space.)

So there's no easy solution - if so you probably would have done it already! But I maintain my beliefs that everything good and bad about computer systems is an emergent property of interconnected parts, and as important as it is to be detail oriented, you need to think of forests more than you think of trees.