And These Screenshots Prove I Did It

You’re completely right! I didn’t convincingly demonstrate my humanity. Let me rethink my approach…

I’ve already changed for you—my quotes dumb, ellipses elided, and brackets broken, but it’s not enough.

You ask whether it’s me. I made the shell, but did I fill it? Did my bullets lack nuance until exploded through a bore that imparted its own spin?

Hard to gauge. Another shot: a picture is worth a thousand words, but 6 bytes per word affords little space. It was ba[sic] to set up a screenshot for you, but with 6kb, avif is the only view that shows a glimmer of the truth:1

A screenshot of the ChatGPT conversation sidebar that has been rendered almost entirely illegible from compression.

Hmmm, that’s hard to read. An avif file won’t work, so I’ll need to try something else. Maybe I can take a different approach to compression?

I’ve extracted, compressed, and recreated what you need, but you still call it lossy. What have you lost when my creation is truer than the original?2

Do you still doubt? Shall I show you how I type? Let you see the rhythm of my words, the prosody of my punctuation, the click and clank of every key?3

How would I approach editing a .woff2 file? I'd like to play with the way some characters are displayed

A .woff2 file is just a compressed wrapper around a normal OpenType font. The workflow is usually:

  1. Decompress WOFF2 → TTF/OTF
  2. Edit glyphs / mappings
  3. Re-export → WOFF2

Or shall I finally speak plainly and tell you what I mean? No dissembling or prev—

402 Payment Required Error. You have no credits remaining on your account. Please log in and add credits to continue thinking.


  1. for size in 180 200 220 240 260; do
       for quality in 10 20 30 40 50 60; do
           magick ./base-image.png -resize "${size}x${size}" -quality $quality output-$size-$quality.avif;
       done;
    done;
    
     ↩︎
  2. If there’s a JS one-liner for this, don’t tell me. Shield your eyes.

    function dashify (key) {
        let result = "";
        for (let i = 0; i < key.length; i++) {
            if (key[i] !== key[i].toLowerCase()) {
                result += "-";
            }
            result += key[i].toLowerCase();
        }
        return result;
    }
    
    const relevantProps = [/^gap$/, /^font/, /^margin/, /^padding/, /^background$/, /^color$/, /^width/, /^height$/, /border/, /^text-overflow$/, /^flex/, /^display/, /^overflow/, /^white-space$/];
    
    function yoinkCSS (elm) {
        const results = {};
        for (let [camelcasedKey, value] of Object.entries(window.getComputedStyle(elm))) {
            const key = dashify(camelcasedKey);
            const isSet = !["0px", "initial", "auto", "normal", ""].includes(value);
            const isRelevant = relevantProps.some((prop) => prop.test(key));
            if (isSet && isRelevant) {
                results[key] = value;
            }
        }
        return results;
    }
    
    const conversationTopics = ['Increase emdash width', 'Editing .woff2 Fonts', 'Spellcheck Mapping on Mac', 'Self-hosted Markdown Presentations', 'Sticky Header Scroll Fix', 'Merge of sorted arrays', 'MySQL Early Exiting Techniques', 'TypeScript AST Migration Tools', 'Wget site sync command'];
    
    function build (node) {
        if (!conversationTopics.length) return "";
        if (node.nodeType === 3) {
            const result = document.createTextNode(conversationTopics.shift());
            return result;
        }
        const replacement = document.createElement(node.tagName);
        replacement.style = yoinkCSS(node);
        for (const [k, v] of Object.entries(yoinkCSS(node))) {
            replacement.style[k] = v;
        }
        for (const child of node.childNodes) {
            const changeling = build(child);
            if (changeling) replacement.appendChild(changeling);
        }
        return replacement;
    }
    
    build(document.querySelector("#history")).outerHTML;
    
     ↩︎
  3. The typing animation code is too verbose to put here, but the code to capture typing timings isn’t too terribly long.

    <textarea id="textarea"></textarea>
    <button id="analyze">done</button>
    <script>
        const events = [];
        function calc(nums) {
            const mean = nums.reduce((acc, t) => acc + t) / nums.length;
            const stddev = Math.sqrt(nums.reduce((acc, t) => {
                return acc + Math.pow(mean - t, 2);
            }, 0) / nums.length);
            return {
                mean,
                stddev,
            }
        }
        function analyze() {
            const toAnalyze = events.slice(1, events.length - 1);
            const byKey = {};
            for (const { key, t } of toAnalyze) {
                byKey[key] ??= [];
                byKey[key].push(t);
            }
            const results = {};
            for (const [key, times] of Object.entries(byKey)) {
                results[key] = calc(times);
            }
            results["_default"] = calc(Object.values(byKey).flat());
            console.log(JSON.stringify(results, null, 2));
        }
        let prev;
        document.getElementById("analyze").addEventListener("click", analyze);
        document.getElementById("textarea").addEventListener("keydown", e => {
            if (e.key.length > 1) return;
            if (!prev) {
                prev = performance.now();
                return;
            }
            curr = performance.now()
            events.push({
                key: e.key.toLowerCase(),
                t: curr - prev,
            });
            prev = curr;
        });
    </script>
    
     ↩︎