Skip to content

Instantly share code, notes, and snippets.

merging your prs

Tom MacWright tmcw

merging your prs
View GitHub Profile

A faster format in Placemark

Placemark uses GeoJSON, and JSON, everywhere. In the database, features are GeoJSON. When we're sending features into Mapbox GL, it's GeoJSON getting sent with postMessage.

GeoJSON, of course, has its issues:

  • Sending GeoJSON features across to the WebWorker that cuts tiles for Mapbox GL JS is a major bottleneck for the core editing experience.
  • GeoJSON in resident memory is bigger than it needs to be. GeoJSON's coordinate arrays, especially, are an issue - flat arrays would be much more compact.
  • Mapbox GL JS itself has to cut GeoJSON into tiles, which requires some transformation - it creates another flat representation of geometry coordinates.
tmcw / states-income-data.csv
Created Jul 29, 2022
Example files to join
View states-income-data.csv
State HouseholdIncome
Maryland 84805
New Jersey 82545
Hawaii 81275
Massachusetts 81215
Connecticut 78444
Alaska 77640
New Hampshire 76768
California 75235
Virginia 74222

Bonus geometry types in GeoJSON

Basically two types:

  1. Rectangles
  2. Circles

Let's say you have a drawing tool that permits both. Both geometry types benefit from some non-standard drawing abilities: it would be nice to resize circles, and it would be nice to resize rectangles in a way that keeps them rectangular. However, GeoJSON does not have circle or rectangle types. Both are represented as polygons. This gives us two options:

  1. "Detect" these shapes with a heuristic. This is doable for a rectangle by checking the corners, but much, much less doable for circles. Also, circles have an additional property that I will mention in the next bit.
View rich-text.json
"@type": "html",
"value": "<p>Example</p>"

My least favorite part of the web platform

Behold, the worst part of the web platform: user gestures.

In short, let's say you've built a web application that edits maps, or… does something else, hypothetically. You implement file saving in that application using the fancy new native browser APIs. Then you have to do some stuff to process or load the file when you click Save - like if you're converting the file and you want to do it in a WebWorker for optimum smoothness. Soon, you will meet this error message:

SecurityError Failed to execute 'showSaveFilePicker' on 'Window': Must be handling a user gesture to show a file picker.

This is just one example of "user gesture" restrictions: you can also find them when you request permissions or attempt to open windows. Basically, to crack down on bad actors or misuse, the browser restricts certain functions to only being called in connection with a click handler.

tmcw /
Created Dec 21, 2021
Stripe webhooks in development

Micro-devlog for something tiny.

Placemark uses Stripe, and uses their webhooks. You can use Stripe without webhooks, but it's better to build with the webhooks.

I'm developing the account system actively, so I want the webhooks to work in local development. Thankfully, the stripe cli supports proxying webhooks from your development Stripe environment to your local setup. You run

$ stripe listen --forward-to localhost:5000/stripe_webhooks

The Utility Token Puzzle

I've written about IPFS twice: in 2017 and 2019. If I were sticking to a biyearly schedule, this would be my time to try it again. After all, one of the key pieces of Protocol Labs infrastructure, Filecoin, launched late last year. The tokens that run Filecoin are now worth billions of dollars.

I looked at the state of Filecoin from the perspective of using it, and I'm sure there's a funny post to be written about someone actually trying to do that - use it - maybe with a five-hour YouTube walkthrough. But I have a deeper question.

How is any of this supposed to work?

I imagine that Filecoin, and other "utility token" systems, work like this:

View scrape.js
const got = require("got");
const selectAll = require("hast-util-select").selectAll;
const unified = require("unified");
const parse = require("rehype-parse");
(async function () {
const { body } = await got("");
const ast = await unified()
.use(parse, { emitParseErrors: true, duplicateAttribute: false })
View load.js
const [map, geojson] = await Promise.all([
new Promise((resolve) => map.on("load", () => resolve(map))),
fetch("foo.json").then((r) => r.json()),

Here's a sort of unsolved logic/programming problem.

I made this site, Old Fashioned, for cocktail recipes. It has structured data for ingredients and recipes for cocktails. And one of its neat features is that, given certain ingredients, it shows you what's possible or what's nearly possible.

I've been thinking about the opposite angle: from which set of 5 ingredients can you make the highest number of different cocktails? How does one correctly solve this problem.

If formal descriptions are your jam: let's say:

  • You have 100 different ingredients
  • You have 20 cocktails, each of which use 2-6 ingredients