{"$schema":"https://ejfox.com/schema/page-twin@1.json","kind":"project_index","url":"https://ejfox.com/projects","json_url":"https://ejfox.com/projects.json","generator":"ejfox.com/json-twin@1","data":[{"slug":"projects/MorningRadio","title":"MorningRadio","html":"<p class=\"\">Designed to be a healthy \"first screen\" in the morning. News summaries, updates, and enough information about the latest events that you don't feel the need to endlessly doomscroll.</p>\n<p class=\"\">The goal was to repurpose the tik-tok style endless vertical scroll but instead fill it with interesting and useful websites, tools, and news articles (populated through my Pinboard bookmarks)</p>\n<figure role=\"figure\" aria-label=\"Home — an audio digest up top, then &#x22;Top Picks&#x22; and a recent feed pulled from my Pinboard bookmarks\" class=\"\">\n<img src=\"https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_1280/projects/morning-radio/home.png\" alt=\"Home — an audio digest up top, then &#x22;Top Picks&#x22; and a recent feed pulled from my Pinboard bookmarks\" loading=\"lazy\" decoding=\"async\" srcset=\"https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_640/projects/morning-radio/home.png 640w, https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_960/projects/morning-radio/home.png 960w, https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_1280/projects/morning-radio/home.png 1280w, https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_1920/projects/morning-radio/home.png 1920w\" sizes=\"(max-width: 430px) 92vw, (max-width: 768px) 85vw, 900px\" title=\"Home — an audio digest up top, then &#x22;Top Picks&#x22; and a recent feed pulled from my Pinboard bookmarks\" width=\"1206\" height=\"2622\" style=\"--splay-rot:1.38deg;\" data-dimensions=\"1206×2622\" class=\"img-full my-8 rounded-sm img-splay w-full mx-auto py-4\" crossorigin=\"anonymous\">\n<figcaption class=\"\">Home — an audio digest up top, then \"Top Picks\" and a recent feed pulled from my Pinboard bookmarks</figcaption>\n</figure>\n<figure role=\"figure\" aria-label=\"The morning audio briefing — a generated &#x22;Morning Radio&#x22; segment with scrubber and skip controls\" class=\"\">\n<img src=\"https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_1280/projects/morning-radio/player.png\" alt=\"The morning audio briefing — a generated &#x22;Morning Radio&#x22; segment with scrubber and skip controls\" loading=\"lazy\" decoding=\"async\" srcset=\"https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_640/projects/morning-radio/player.png 640w, https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_960/projects/morning-radio/player.png 960w, https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_1280/projects/morning-radio/player.png 1280w, https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_1920/projects/morning-radio/player.png 1920w\" sizes=\"(max-width: 430px) 92vw, (max-width: 768px) 85vw, 900px\" title=\"The morning audio briefing — a generated &#x22;Morning Radio&#x22; segment with scrubber and skip controls\" width=\"1206\" height=\"2622\" style=\"--splay-rot:-1.67deg;\" data-dimensions=\"1206×2622\" class=\"img-full my-8 rounded-sm img-splay w-full mx-auto py-4\" crossorigin=\"anonymous\">\n<figcaption class=\"\">The morning audio briefing — a generated \"Morning Radio\" segment with scrubber and skip controls</figcaption>\n</figure>\n<figure role=\"figure\" aria-label=\"A single card in the vertical scroll — full article with source, tags, and summary\" class=\"\">\n<img src=\"https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_1280/projects/morning-radio/reader.png\" alt=\"A single card in the vertical scroll — full article with source, tags, and summary\" loading=\"lazy\" decoding=\"async\" srcset=\"https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_640/projects/morning-radio/reader.png 640w, https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_960/projects/morning-radio/reader.png 960w, https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_1280/projects/morning-radio/reader.png 1280w, https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_1920/projects/morning-radio/reader.png 1920w\" sizes=\"(max-width: 430px) 92vw, (max-width: 768px) 85vw, 900px\" title=\"A single card in the vertical scroll — full article with source, tags, and summary\" width=\"1206\" height=\"2622\" style=\"--splay-rot:0.8deg;\" data-dimensions=\"1206×2622\" class=\"img-full my-8 rounded-sm img-splay w-full mx-auto py-4\" crossorigin=\"anonymous\">\n<figcaption class=\"\">A single card in the vertical scroll — full article with source, tags, and summary</figcaption>\n</figure>\n<p class=\"\">I even experimented (unsuccessfully) with creating a home screen widget.</p>\n<figure role=\"figure\" aria-label=\"Home screen widget experiment\" class=\"\">\n<img src=\"https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_1280/v1749668160/Screenshot_2025-06-11_at_2.55.49_PM.png\" alt=\"Home screen widget experiment\" loading=\"lazy\" decoding=\"async\" srcset=\"https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_640/v1749668160/Screenshot_2025-06-11_at_2.55.49_PM.png 640w, https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_960/v1749668160/Screenshot_2025-06-11_at_2.55.49_PM.png 960w, https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_1280/v1749668160/Screenshot_2025-06-11_at_2.55.49_PM.png 1280w, https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_1920/v1749668160/Screenshot_2025-06-11_at_2.55.49_PM.png 1920w\" sizes=\"(max-width: 430px) 92vw, (max-width: 768px) 85vw, 900px\" title=\"Home screen widget experiment\" width=\"1136\" height=\"2168\" style=\"--splay-rot:-0.6deg;\" data-dimensions=\"1136×2168\" class=\"img-full my-8 rounded-sm img-splay w-full mx-auto py-4\" crossorigin=\"anonymous\">\n<figcaption class=\"\">Home screen widget experiment</figcaption>\n</figure>","metadata":{"title":"MorningRadio","date":"2025-08-10T04:00:00.000Z","modified":"2025-08-13T15:26:19.000Z","url":"https://github.com/ejfox/MorningRadio","tech":["Swift","iOS","Audio Streaming","Mobile UX"],"state":"doing","aiInvolvement":"ai-assisted","tags":["ios","health"],"words":123,"images":4,"imageDetails":{"total":4,"cloudinary":4,"withDimensions":0},"links":4,"codeBlocks":0,"headers":{},"toc":[],"type":"post"}},{"slug":"projects/smallbot-mcp","title":"Smallbot MCP","html":"<p class=\"\"><a href=\"https://www.smallweb.run\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"external-link group inline-flex items-center text-blue-600 dark:text-blue-400\" data-preview-url=\"https://www.smallweb.run\">Smallweb</a> is a really unique tool to create a filesystem based system for rapidly-deployed deno apps, and it pairs really well with a robot companion.</p>\n<p class=\"\">Nowadays when I have an idea for a website and I just want it to exist, without spending three hours setting up a build pipeline configuring DNS. Smallbot is an incredible tool to defeat that particular form of procrastination—a robot that takes \"make me a site about <a href=\"https://youtube.com/shorts/i6IZSkmqNSo\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"external-link group inline-flex items-center text-blue-600 dark:text-blue-400\" data-preview-url=\"https://youtube.com/shorts/i6IZSkmqNSo\">northeastern trees<svg class=\"inline-block w-4 h-4 ml-1 opacity-50 dark:opacity-75 group-hover:opacity-100 transition-opacity align-text-bottom\" xmlns=\"http://www.w3.org/2000/svg\" width=\"1em\" height=\"1em\" viewBox=\"0 0 24 24\"><path fill=\"currentColor\" d=\"M23.498 6.186a3.02 3.02 0 0 0-2.122-2.136C19.505 3.545 12 3.545 12 3.545s-7.505 0-9.377.505A3.02 3.02 0 0 0 .502 6.186C0 8.07 0 12 0 12s0 3.93.502 5.814a3.02 3.02 0 0 0 2.122 2.136c1.871.505 9.376.505 9.376.505s7.505 0 9.377-.505a3.02 3.02 0 0 0 2.122-2.136C24 15.93 24 12 24 12s0-3.93-.502-5.814M9.545 15.568V8.432L15.818 12z\" class=\"\"></path></svg></a>\" and actually builds the thing.</p>\n<p class=\"\">I made a <a href=\"https://www.youtube.com/watch?v=thIt-JXYbco\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"external-link group inline-flex items-center text-blue-600 dark:text-blue-400\" data-preview-url=\"https://www.youtube.com/watch?v=thIt-JXYbco\">video with the creator of Smallweb<svg class=\"inline-block w-4 h-4 ml-1 opacity-50 dark:opacity-75 group-hover:opacity-100 transition-opacity align-text-bottom\" xmlns=\"http://www.w3.org/2000/svg\" width=\"1em\" height=\"1em\" viewBox=\"0 0 24 24\"><path fill=\"currentColor\" d=\"M23.498 6.186a3.02 3.02 0 0 0-2.122-2.136C19.505 3.545 12 3.545 12 3.545s-7.505 0-9.377.505A3.02 3.02 0 0 0 .502 6.186C0 8.07 0 12 0 12s0 3.93.502 5.814a3.02 3.02 0 0 0 2.122 2.136c1.871.505 9.376.505 9.376.505s7.505 0 9.377-.505a3.02 3.02 0 0 0 2.122-2.136C24 15.93 24 12 24 12s0-3.93-.502-5.814M9.545 15.568V8.432L15.818 12z\" class=\"\"></path></svg></a> about how easy it is to get setup, and I love how much the project embodies <a href=\"https://www.are.na/ej-fox/neo-internet-philosophy\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"external-link group inline-flex items-center text-blue-600 dark:text-blue-400\" data-preview-url=\"https://www.are.na/ej-fox/neo-internet-philosophy\">indieweb philosophies</a></p>\n<p class=\"\">This is a tool built on the MCP (Model Context Protocol) so it can talk to Claude and other AI assistants and give them the tools and documentation to make little Smallweb sites easily, on-the-fly.</p>\n<figure role=\"figure\" aria-label=\"Smallbot — a helpful assistant for creating Smallweb applications\" class=\"\">\n<img src=\"https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_1280/v1741706866/Screenshot_2025-03-11_at_11.27.32_AM.png\" alt=\"Smallbot — a helpful assistant for creating Smallweb applications\" loading=\"lazy\" decoding=\"async\" srcset=\"https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_640/v1741706866/Screenshot_2025-03-11_at_11.27.32_AM.png 640w, https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_960/v1741706866/Screenshot_2025-03-11_at_11.27.32_AM.png 960w, https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_1280/v1741706866/Screenshot_2025-03-11_at_11.27.32_AM.png 1280w, https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_1920/v1741706866/Screenshot_2025-03-11_at_11.27.32_AM.png 1920w\" sizes=\"(max-width: 430px) 92vw, (max-width: 768px) 85vw, 900px\" title=\"Smallbot — a helpful assistant for creating Smallweb applications\" width=\"3348\" height=\"2486\" style=\"--splay-rot:0.6deg;\" data-dimensions=\"3348×2486\" class=\"img-full my-8 rounded-sm img-splay w-full mx-auto py-4\" crossorigin=\"anonymous\">\n<figcaption class=\"\">Smallbot — a helpful assistant for creating Smallweb applications</figcaption>\n</figure>\n<figure role=\"figure\" aria-label=\"Smallbot generating a Smallweb app\" class=\"\">\n<img src=\"https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_1280/v1746294443/Screenshot_2025-05-03_at_1.47.10_PM.png\" alt=\"Smallbot generating a Smallweb app\" loading=\"lazy\" decoding=\"async\" srcset=\"https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_640/v1746294443/Screenshot_2025-05-03_at_1.47.10_PM.png 640w, https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_960/v1746294443/Screenshot_2025-05-03_at_1.47.10_PM.png 960w, https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_1280/v1746294443/Screenshot_2025-05-03_at_1.47.10_PM.png 1280w, https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_1920/v1746294443/Screenshot_2025-05-03_at_1.47.10_PM.png 1920w\" sizes=\"(max-width: 430px) 92vw, (max-width: 768px) 85vw, 900px\" title=\"Smallbot generating a Smallweb app\" width=\"2620\" height=\"2486\" style=\"--splay-rot:1.59deg;\" data-dimensions=\"2620×2486\" class=\"img-full my-8 rounded-sm img-splay aspect-square w-full mx-auto py-4\" crossorigin=\"anonymous\">\n<figcaption class=\"\">Smallbot generating a Smallweb app</figcaption>\n</figure>","metadata":{"title":"Smallbot MCP","date":"2025-08-02T04:00:00.000Z","modified":"2025-08-13T17:17:13.000Z","url":"https://github.com/ejfox/smallbot-mcp","tech":["MCP","AI","Web Generation","Node.js"],"state":"deployed","aiInvolvement":"ai-enhanced","tags":["ai","web"],"words":156,"images":2,"imageDetails":{"total":2,"cloudinary":2,"withDimensions":0},"links":6,"codeBlocks":0,"headers":{},"toc":[],"type":"post"}},{"slug":"projects/cloudinary-backup-tool","title":"Cloudinary Backup Tool","html":"<p class=\"\">I needed to backup thousands of photos from Cloudinary, and couldn't find an easy standalone tool to do so. So I built a little desktop app with Tauri. It handles batch downloads without eating all your RAM (vs an Electron app) and lets you resume if something goes wrong.</p>\n<figure role=\"figure\" aria-label=\"Setup instructions for Cloudinary Backup tool requesting cloud name, API key, and API secret credentials to download photos.\" class=\"\">\n<img src=\"https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_1280/v1755100480/screenshot_2025-08-13_at_11.54.28_AM.png\" alt=\"Setup instructions for Cloudinary Backup tool requesting cloud name, API key, and API secret credentials to download photos.\" loading=\"lazy\" decoding=\"async\" srcset=\"https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_640/v1755100480/screenshot_2025-08-13_at_11.54.28_AM.png 640w, https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_960/v1755100480/screenshot_2025-08-13_at_11.54.28_AM.png 960w, https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_1280/v1755100480/screenshot_2025-08-13_at_11.54.28_AM.png 1280w, https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_1920/v1755100480/screenshot_2025-08-13_at_11.54.28_AM.png 1920w\" sizes=\"(max-width: 430px) 92vw, (max-width: 768px) 85vw, 900px\" title=\"Setup instructions for Cloudinary Backup tool requesting cloud name, API key, and API secret credentials to download photos.\" width=\"2000\" height=\"1750\" style=\"--splay-rot:2.67deg;\" data-dimensions=\"2000×1750\" class=\"img-full my-8 rounded-sm img-splay w-full mx-auto py-4\" crossorigin=\"anonymous\">\n<figcaption class=\"\">Setup instructions for Cloudinary Backup tool requesting cloud name, API key, and API secret credentials to download photos.</figcaption>\n</figure>\n<figure role=\"figure\" aria-label=\"The Cloudinary Backup desktop app browsing assets before export\" class=\"\">\n<img src=\"https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_1280/v1735007706/Screenshot_2024-12-23_at_9.34.52_PM.png\" alt=\"The Cloudinary Backup desktop app browsing assets before export\" loading=\"lazy\" decoding=\"async\" srcset=\"https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_640/v1735007706/Screenshot_2024-12-23_at_9.34.52_PM.png 640w, https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_960/v1735007706/Screenshot_2024-12-23_at_9.34.52_PM.png 960w, https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_1280/v1735007706/Screenshot_2024-12-23_at_9.34.52_PM.png 1280w, https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_1920/v1735007706/Screenshot_2024-12-23_at_9.34.52_PM.png 1920w\" sizes=\"(max-width: 430px) 92vw, (max-width: 768px) 85vw, 900px\" title=\"The Cloudinary Backup desktop app browsing assets before export\" width=\"3600\" height=\"2338\" style=\"--splay-rot:2.5deg;\" data-dimensions=\"3600×2338\" class=\"img-full my-8 rounded-sm img-splay w-full mx-auto py-4\" crossorigin=\"anonymous\">\n<figcaption class=\"\">The Cloudinary Backup desktop app browsing assets before export</figcaption>\n</figure>\n<figure role=\"figure\" aria-label=\"Batch download in progress in the Cloudinary Backup app\" class=\"\">\n<img src=\"https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_1280/v1735007506/Screenshot_2024-12-23_at_9.31.28_PM.png\" alt=\"Batch download in progress in the Cloudinary Backup app\" loading=\"lazy\" decoding=\"async\" srcset=\"https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_640/v1735007506/Screenshot_2024-12-23_at_9.31.28_PM.png 640w, https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_960/v1735007506/Screenshot_2024-12-23_at_9.31.28_PM.png 960w, https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_1280/v1735007506/Screenshot_2024-12-23_at_9.31.28_PM.png 1280w, https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_1920/v1735007506/Screenshot_2024-12-23_at_9.31.28_PM.png 1920w\" sizes=\"(max-width: 430px) 92vw, (max-width: 768px) 85vw, 900px\" title=\"Batch download in progress in the Cloudinary Backup app\" width=\"2498\" height=\"2474\" style=\"--splay-rot:-2.14deg;\" data-dimensions=\"2498×2474\" class=\"img-full my-8 rounded-sm img-splay aspect-square w-full mx-auto py-4\" crossorigin=\"anonymous\">\n<figcaption class=\"\">Batch download in progress in the Cloudinary Backup app</figcaption>\n</figure>","metadata":{"title":"Cloudinary Backup Tool","date":"2025-01-15T05:00:00.000Z","github":"https://github.com/ejfox/cloudinary-backup-tool","tech":["Tauri","Rust","TypeScript","Vite"],"modified":"2025-08-13T15:58:10.000Z","tags":["tools","photography"],"words":87,"images":3,"imageDetails":{"total":3,"cloudinary":3,"withDimensions":0},"links":3,"codeBlocks":0,"headers":{},"toc":[],"type":"post"}},{"slug":"projects/creative-interfaces","title":"Hand-Tracking & Generative Art","html":"<p class=\"\">These are my attempts to make computers that respond to the way humans actually move and gesture—exploring the design space between human intuition and machine capability. Each project pushes beyond traditional HCI constraints to find new interaction models. Some of these work better than others, but they all represent steps toward interfaces that feel more like extensions of thought than barriers to it.</p>\n<figure role=\"figure\" aria-label=\"Playing Super Mario Bros. through the hand-tracking MIDI controller\" class=\"\">\n<img src=\"https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_1280/projects/creative-interfaces/mario.png\" alt=\"Playing Super Mario Bros. through the hand-tracking MIDI controller\" loading=\"lazy\" decoding=\"async\" srcset=\"https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_640/projects/creative-interfaces/mario.png 640w, https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_960/projects/creative-interfaces/mario.png 960w, https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_1280/projects/creative-interfaces/mario.png 1280w, https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_1920/projects/creative-interfaces/mario.png 1920w\" sizes=\"(max-width: 430px) 92vw, (max-width: 768px) 85vw, 900px\" title=\"Playing Super Mario Bros. through the hand-tracking MIDI controller\" width=\"1280\" height=\"720\" style=\"--splay-rot:-1.54deg;\" data-dimensions=\"1280×720\" class=\"img-full my-8 rounded-sm img-splay aspect-video w-full mx-auto py-4\" crossorigin=\"anonymous\">\n<figcaption class=\"\">Playing Super Mario Bros. through the hand-tracking MIDI controller</figcaption>\n</figure>\n<figure role=\"figure\" aria-label=\"Turning a backyard crow&#x27;s movements into music\" class=\"\">\n<img src=\"https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_1280/projects/creative-interfaces/crows.png\" alt=\"Turning a backyard crow&#x27;s movements into music\" loading=\"lazy\" decoding=\"async\" srcset=\"https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_640/projects/creative-interfaces/crows.png 640w, https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_960/projects/creative-interfaces/crows.png 960w, https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_1280/projects/creative-interfaces/crows.png 1280w, https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_1920/projects/creative-interfaces/crows.png 1920w\" sizes=\"(max-width: 430px) 92vw, (max-width: 768px) 85vw, 900px\" title=\"Turning a backyard crow&#x27;s movements into music\" width=\"1920\" height=\"1080\" style=\"--splay-rot:2deg;\" data-dimensions=\"1920×1080\" class=\"img-full my-8 rounded-sm img-splay aspect-video w-full mx-auto py-4\" crossorigin=\"anonymous\">\n<figcaption class=\"\">Turning a backyard crow's movements into music</figcaption>\n</figure>\n<h2 class=\"\" id=\"projects\">Projects</h2>\n<ol class=\"list-decimal\">\n<li class=\"\"><a href=\"https://github.com/ejfox/hand-midi-controller\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"external-link group inline-flex items-center text-blue-600 dark:text-blue-400\" data-preview-url=\"https://github.com/ejfox/hand-midi-controller\">hand-midi-controller<svg class=\"inline-block w-4 h-4 ml-1 opacity-50 dark:opacity-75 group-hover:opacity-100 transition-opacity align-text-bottom\" xmlns=\"http://www.w3.org/2000/svg\" width=\"1em\" height=\"1em\" viewBox=\"0 0 24 24\"><path fill=\"currentColor\" d=\"M12 .297c-6.63 0-12 5.373-12 12c0 5.303 3.438 9.8 8.205 11.385c.6.113.82-.258.82-.577c0-.285-.01-1.04-.015-2.04c-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729c1.205.084 1.838 1.236 1.838 1.236c1.07 1.835 2.809 1.305 3.495.998c.108-.776.417-1.305.76-1.605c-2.665-.3-5.466-1.332-5.466-5.93c0-1.31.465-2.38 1.235-3.22c-.135-.303-.54-1.523.105-3.176c0 0 1.005-.322 3.3 1.23c.96-.267 1.98-.399 3-.405c1.02.006 2.04.138 3 .405c2.28-1.552 3.285-1.23 3.285-1.23c.645 1.653.24 2.873.12 3.176c.765.84 1.23 1.91 1.23 3.22c0 4.61-2.805 5.625-5.475 5.92c.42.36.81 1.096.81 2.22c0 1.606-.015 2.896-.015 3.286c0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12\" class=\"\"></path></svg></a>: Professional hand tracking MIDI controller with TouchDesigner integration. Converts hand movements to MIDI with detailed performance timing breakdowns and memory analysis for creative interface exploration.</li>\n<li class=\"\"><a href=\"https://github.com/ejfox/handtrack-websockets\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"external-link group inline-flex items-center text-blue-600 dark:text-blue-400\" data-preview-url=\"https://github.com/ejfox/handtrack-websockets\">handtrack-websockets<svg class=\"inline-block w-4 h-4 ml-1 opacity-50 dark:opacity-75 group-hover:opacity-100 transition-opacity align-text-bottom\" xmlns=\"http://www.w3.org/2000/svg\" width=\"1em\" height=\"1em\" viewBox=\"0 0 24 24\"><path fill=\"currentColor\" d=\"M12 .297c-6.63 0-12 5.373-12 12c0 5.303 3.438 9.8 8.205 11.385c.6.113.82-.258.82-.577c0-.285-.01-1.04-.015-2.04c-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729c1.205.084 1.838 1.236 1.838 1.236c1.07 1.835 2.809 1.305 3.495.998c.108-.776.417-1.305.76-1.605c-2.665-.3-5.466-1.332-5.466-5.93c0-1.31.465-2.38 1.235-3.22c-.135-.303-.54-1.523.105-3.176c0 0 1.005-.322 3.3 1.23c.96-.267 1.98-.399 3-.405c1.02.006 2.04.138 3 .405c2.28-1.552 3.285-1.23 3.285-1.23c.645 1.653.24 2.873.12 3.176c.765.84 1.23 1.91 1.23 3.22c0 4.61-2.805 5.625-5.475 5.92c.42.36.81 1.096.81 2.22c0 1.606-.015 2.896-.015 3.286c0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12\" class=\"\"></path></svg></a>: Real-time gesture streaming pushing the boundaries of browser-based interaction.</li>\n<li class=\"\"><a href=\"https://github.com/ejfox/flipper-generative-art\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"external-link group inline-flex items-center text-blue-600 dark:text-blue-400\" data-preview-url=\"https://github.com/ejfox/flipper-generative-art\">flipper-generative-art<svg class=\"inline-block w-4 h-4 ml-1 opacity-50 dark:opacity-75 group-hover:opacity-100 transition-opacity align-text-bottom\" xmlns=\"http://www.w3.org/2000/svg\" width=\"1em\" height=\"1em\" viewBox=\"0 0 24 24\"><path fill=\"currentColor\" d=\"M12 .297c-6.63 0-12 5.373-12 12c0 5.303 3.438 9.8 8.205 11.385c.6.113.82-.258.82-.577c0-.285-.01-1.04-.015-2.04c-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729c1.205.084 1.838 1.236 1.838 1.236c1.07 1.835 2.809 1.305 3.495.998c.108-.776.417-1.305.76-1.605c-2.665-.3-5.466-1.332-5.466-5.93c0-1.31.465-2.38 1.235-3.22c-.135-.303-.54-1.523.105-3.176c0 0 1.005-.322 3.3 1.23c.96-.267 1.98-.399 3-.405c1.02.006 2.04.138 3 .405c2.28-1.552 3.285-1.23 3.285-1.23c.645 1.653.24 2.873.12 3.176c.765.84 1.23 1.91 1.23 3.22c0 4.61-2.805 5.625-5.475 5.92c.42.36.81 1.096.81 2.22c0 1.606-.015 2.896-.015 3.286c0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12\" class=\"\"></path></svg></a>: Real-time generative art and animated patterns for Flipper Zero with 10 gradient types, Floyd-Steinberg dithering, and smooth 30 FPS animations. Finding art within the constraints of 64KB RAM.</li>\n<li class=\"\"><a href=\"https://github.com/ejfox/ps5-tmux\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"external-link group inline-flex items-center text-blue-600 dark:text-blue-400\" data-preview-url=\"https://github.com/ejfox/ps5-tmux\">ps5-tmux<svg class=\"inline-block w-4 h-4 ml-1 opacity-50 dark:opacity-75 group-hover:opacity-100 transition-opacity align-text-bottom\" xmlns=\"http://www.w3.org/2000/svg\" width=\"1em\" height=\"1em\" viewBox=\"0 0 24 24\"><path fill=\"currentColor\" d=\"M12 .297c-6.63 0-12 5.373-12 12c0 5.303 3.438 9.8 8.205 11.385c.6.113.82-.258.82-.577c0-.285-.01-1.04-.015-2.04c-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729c1.205.084 1.838 1.236 1.838 1.236c1.07 1.835 2.809 1.305 3.495.998c.108-.776.417-1.305.76-1.605c-2.665-.3-5.466-1.332-5.466-5.93c0-1.31.465-2.38 1.235-3.22c-.135-.303-.54-1.523.105-3.176c0 0 1.005-.322 3.3 1.23c.96-.267 1.98-.399 3-.405c1.02.006 2.04.138 3 .405c2.28-1.552 3.285-1.23 3.285-1.23c.645 1.653.24 2.873.12 3.176c.765.84 1.23 1.91 1.23 3.22c0 4.61-2.805 5.625-5.475 5.92c.42.36.81 1.096.81 2.22c0 1.606-.015 2.896-.015 3.286c0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12\" class=\"\"></path></svg></a> is a little tool to make it easier to vibe-code; just move between panes with your PS5 controller and accept changes with the X button, or hit the middle button to enter dictation mode.</li>\n</ol>","metadata":{"title":"Hand-Tracking & Generative Art","date":"2024-07-01T04:00:00.000Z","modified":"2025-08-13T15:46:54.000Z","tech":["Computer Vision","MIDI","WebGL","Hand Tracking","Generative Art","Interactive Media"],"state":"deployed","aiInvolvement":"ai-assisted","tags":["creativity","interfaces"],"words":186,"images":2,"imageDetails":{"total":2,"cloudinary":2,"withDimensions":0},"links":6,"codeBlocks":0,"headers":{"h2":1},"toc":[{"text":"Projects","slug":"projects","level":"h2","children":[]}],"type":"post"}},{"slug":"projects/photography-tools","title":"EXIF Printer","html":"<p class=\"\">I love being able to reference the technical details when editing photography—aperture, shutter speed, ISO, all the little choices that go into making a photograph. But most photo prints completely ignore this data, treating it like metadata to be hidden away in EXIF tags that nobody ever sees. So I made a little app to add the EXIF data to the margins of prints you might get from CVS, Walgreens, or Wal-Mart (don't use them for photo prints, I learned the hard way.)</p>\n<figure role=\"figure\" aria-label=\"EXIF Photo Printer application displaying five family photos with options to regenerate layouts or save prints at 4x6 size.\" class=\"\">\n<img src=\"https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_1280/v1755098311/screenshot_2025-08-13_at_11.18.12_AM.png\" alt=\"EXIF Photo Printer application displaying five family photos with options to regenerate layouts or save prints at 4x6 size.\" loading=\"lazy\" decoding=\"async\" srcset=\"https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_640/v1755098311/screenshot_2025-08-13_at_11.18.12_AM.png 640w, https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_960/v1755098311/screenshot_2025-08-13_at_11.18.12_AM.png 960w, https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_1280/v1755098311/screenshot_2025-08-13_at_11.18.12_AM.png 1280w, https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_1920/v1755098311/screenshot_2025-08-13_at_11.18.12_AM.png 1920w\" sizes=\"(max-width: 430px) 92vw, (max-width: 768px) 85vw, 900px\" title=\"EXIF Photo Printer application displaying five family photos with options to regenerate layouts or save prints at 4x6 size.\" width=\"2400\" height=\"1800\" style=\"--splay-rot:2.74deg;\" data-dimensions=\"2400×1800\" class=\"img-full my-8 rounded-sm img-splay w-full mx-auto py-4\" crossorigin=\"anonymous\">\n<figcaption class=\"\">EXIF Photo Printer application displaying five family photos with options to regenerate layouts or save prints at 4x6 size.</figcaption>\n</figure>\n<figure role=\"figure\" aria-label=\"The EXIF Photo Printer marketing site\" class=\"\">\n<img src=\"https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_1280/projects/photography-tools/exifprinter.png\" alt=\"The EXIF Photo Printer marketing site\" loading=\"lazy\" decoding=\"async\" srcset=\"https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_640/projects/photography-tools/exifprinter.png 640w, https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_960/projects/photography-tools/exifprinter.png 960w, https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_1280/projects/photography-tools/exifprinter.png 1280w, https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_1920/projects/photography-tools/exifprinter.png 1920w\" sizes=\"(max-width: 430px) 92vw, (max-width: 768px) 85vw, 900px\" title=\"The EXIF Photo Printer marketing site\" width=\"2880\" height=\"1900\" style=\"--splay-rot:-0.86deg;\" data-dimensions=\"2880×1900\" class=\"img-full my-8 rounded-sm img-splay w-full mx-auto py-4\" crossorigin=\"anonymous\">\n<figcaption class=\"\">The EXIF Photo Printer marketing site</figcaption>\n</figure>\n<p class=\"\"><a href=\"https://github.com/ejfox/exif-printer-simple\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"external-link group inline-flex items-center text-blue-600 dark:text-blue-400\" data-preview-url=\"https://github.com/ejfox/exif-printer-simple\">exif-printer<svg class=\"inline-block w-4 h-4 ml-1 opacity-50 dark:opacity-75 group-hover:opacity-100 transition-opacity align-text-bottom\" xmlns=\"http://www.w3.org/2000/svg\" width=\"1em\" height=\"1em\" viewBox=\"0 0 24 24\"><path fill=\"currentColor\" d=\"M12 .297c-6.63 0-12 5.373-12 12c0 5.303 3.438 9.8 8.205 11.385c.6.113.82-.258.82-.577c0-.285-.01-1.04-.015-2.04c-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729c1.205.084 1.838 1.236 1.838 1.236c1.07 1.835 2.809 1.305 3.495.998c.108-.776.417-1.305.76-1.605c-2.665-.3-5.466-1.332-5.466-5.93c0-1.31.465-2.38 1.235-3.22c-.135-.303-.54-1.523.105-3.176c0 0 1.005-.322 3.3 1.23c.96-.267 1.98-.399 3-.405c1.02.006 2.04.138 3 .405c2.28-1.552 3.285-1.23 3.285-1.23c.645 1.653.24 2.873.12 3.176c.765.84 1.23 1.91 1.23 3.22c0 4.61-2.805 5.625-5.475 5.92c.42.36.81 1.096.81 2.22c0 1.606-.015 2.896-.015 3.286c0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12\" class=\"\"></path></svg></a>: Creates printable photos with camera settings displayed in the borders - a photographer's tool that treats metadata as part of the art.</p>","metadata":{"title":"EXIF Printer","date":"2024-04-01T04:00:00.000Z","modified":"2025-08-13T15:20:07.000Z","tech":["Photography","EXIF","Print Design","Web Design","Camera Metadata"],"state":"deployed","aiInvolvement":"ai-assisted","tags":["photography","tools"],"words":132,"images":2,"imageDetails":{"total":2,"cloudinary":2,"withDimensions":0},"links":3,"codeBlocks":0,"headers":{},"toc":[],"type":"post"}},{"slug":"projects/coach-artie","title":"Coach Artie","html":"<figure role=\"figure\" aria-label=\"Coach Artie landing page — &#x22;Why Coach Artie?&#x22;\" class=\"\">\n<img src=\"https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_1280/v1743818370/Screenshot_2025-04-04_at_9.59.08_PM.png\" alt=\"Coach Artie landing page — &#x22;Why Coach Artie?&#x22;\" loading=\"lazy\" decoding=\"async\" srcset=\"https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_640/v1743818370/Screenshot_2025-04-04_at_9.59.08_PM.png 640w, https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_960/v1743818370/Screenshot_2025-04-04_at_9.59.08_PM.png 960w, https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_1280/v1743818370/Screenshot_2025-04-04_at_9.59.08_PM.png 1280w, https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_1920/v1743818370/Screenshot_2025-04-04_at_9.59.08_PM.png 1920w\" sizes=\"(max-width: 430px) 92vw, (max-width: 768px) 85vw, 900px\" title=\"Coach Artie landing page — &#x22;Why Coach Artie?&#x22;\" width=\"3824\" height=\"2486\" style=\"--splay-rot:-2.25deg;\" data-dimensions=\"3824×2486\" class=\"img-full my-8 rounded-sm img-splay w-full mx-auto py-4\" crossorigin=\"anonymous\">\n<figcaption class=\"\">Coach Artie landing page — \"Why Coach Artie?\"</figcaption>\n</figure>\n<figure role=\"figure\" aria-label=\"Coach Artie interface\" class=\"\">\n<img src=\"https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_1280/v1743818354/Screenshot_2025-04-04_at_9.59.00_PM.png\" alt=\"Coach Artie interface\" loading=\"lazy\" decoding=\"async\" srcset=\"https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_640/v1743818354/Screenshot_2025-04-04_at_9.59.00_PM.png 640w, https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_960/v1743818354/Screenshot_2025-04-04_at_9.59.00_PM.png 960w, https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_1280/v1743818354/Screenshot_2025-04-04_at_9.59.00_PM.png 1280w, https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_1920/v1743818354/Screenshot_2025-04-04_at_9.59.00_PM.png 1920w\" sizes=\"(max-width: 430px) 92vw, (max-width: 768px) 85vw, 900px\" title=\"Coach Artie interface\" width=\"3358\" height=\"1884\" style=\"--splay-rot:0.76deg;\" data-dimensions=\"3358×1884\" class=\"img-full my-8 rounded-sm img-splay aspect-video w-full mx-auto py-4\" crossorigin=\"anonymous\">\n<figcaption class=\"\">Coach Artie interface</figcaption>\n</figure>\n<figure role=\"figure\" aria-label=\"Coach Artie in conversation\" class=\"\">\n<img src=\"https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_1280/v1757894550/screenshot_2025-09-14_at_8.02.18_PM.png\" alt=\"Coach Artie in conversation\" loading=\"lazy\" decoding=\"async\" srcset=\"https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_640/v1757894550/screenshot_2025-09-14_at_8.02.18_PM.png 640w, https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_960/v1757894550/screenshot_2025-09-14_at_8.02.18_PM.png 960w, https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_1280/v1757894550/screenshot_2025-09-14_at_8.02.18_PM.png 1280w, https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_1920/v1757894550/screenshot_2025-09-14_at_8.02.18_PM.png 1920w\" sizes=\"(max-width: 430px) 92vw, (max-width: 768px) 85vw, 900px\" title=\"Coach Artie in conversation\" width=\"1774\" height=\"810\" style=\"--splay-rot:2.45deg;\" data-dimensions=\"1774×810\" class=\"img-full my-8 rounded-sm img-splay w-full mx-auto py-4\" crossorigin=\"anonymous\">\n<figcaption class=\"\">Coach Artie in conversation</figcaption>\n</figure>\n<figure role=\"figure\" aria-label=\"Coach Artie ecosystem view\" class=\"\">\n<img src=\"https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_1280/v1740247476/Screenshot_2025-02-22_at_1.04.24_PM.png\" alt=\"Coach Artie ecosystem view\" loading=\"lazy\" decoding=\"async\" srcset=\"https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_640/v1740247476/Screenshot_2025-02-22_at_1.04.24_PM.png 640w, https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_960/v1740247476/Screenshot_2025-02-22_at_1.04.24_PM.png 960w, https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_1280/v1740247476/Screenshot_2025-02-22_at_1.04.24_PM.png 1280w, https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_1920/v1740247476/Screenshot_2025-02-22_at_1.04.24_PM.png 1920w\" sizes=\"(max-width: 430px) 92vw, (max-width: 768px) 85vw, 900px\" title=\"Coach Artie ecosystem view\" width=\"3600\" height=\"2252\" style=\"--splay-rot:-2.86deg;\" data-dimensions=\"3600×2252\" class=\"img-full my-8 rounded-sm img-splay w-full mx-auto py-4\" crossorigin=\"anonymous\">\n<figcaption class=\"\">Coach Artie ecosystem view</figcaption>\n</figure>\n<h2 class=\"\" id=\"coach-artie-v1-discord-studio-assistant\">Coach Artie v1: Discord Studio Assistant</h2>\n<p class=\"\">I created <a href=\"https://coachartiebot.com\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"external-link group inline-flex items-center text-blue-600 dark:text-blue-400\" data-preview-url=\"https://coachartiebot.com\">Coach Artie</a> to serve as our Discord studio assistant. He's evolved into a powerful AI tool with persistent memory and tool usage - helping our team coordinate and manage creative workflows.</p>\n<h2 class=\"\" id=\"coach-artie-v2-production-ai-with-mcp-tools\">Coach Artie v2: Production AI with MCP Tools</h2>\n<p class=\"\">Currently in development.</p>\n<h2 class=\"\" id=\"memory--consciousness-analysis\">Memory &#x26; Consciousness Analysis</h2>\n<p class=\"\">Explores AI consciousness emergence through memory patterns with scientific rigor applied to existential questions. This research component investigates how persistent memory systems in AI assistants might lead to emergent conscious-like behaviors.</p>\n<h3 class=\"\" id=\"related-content\">Related Content</h3>\n<ul class=\"list-disc\">\n<li class=\"\"><a href=\"https://youtube.com/@ejfox\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"external-link group inline-flex items-center text-blue-600 dark:text-blue-400\" data-preview-url=\"https://youtube.com/@ejfox\">The AI-Human Creative Loop: My 2025 Working Process<svg class=\"inline-block w-4 h-4 ml-1 opacity-50 dark:opacity-75 group-hover:opacity-100 transition-opacity align-text-bottom\" xmlns=\"http://www.w3.org/2000/svg\" width=\"1em\" height=\"1em\" viewBox=\"0 0 24 24\"><path fill=\"currentColor\" d=\"M23.498 6.186a3.02 3.02 0 0 0-2.122-2.136C19.505 3.545 12 3.545 12 3.545s-7.505 0-9.377.505A3.02 3.02 0 0 0 .502 6.186C0 8.07 0 12 0 12s0 3.93.502 5.814a3.02 3.02 0 0 0 2.122 2.136c1.871.505 9.376.505 9.376.505s7.505 0 9.377-.505a3.02 3.02 0 0 0 2.122-2.136C24 15.93 24 12 24 12s0-3.93-.502-5.814M9.545 15.568V8.432L15.818 12z\" class=\"\"></path></svg></a> - How AI assistants integrate into continuous creative workflows</li>\n<li class=\"\"><a href=\"https://youtube.com/@ejfox\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"external-link group inline-flex items-center text-blue-600 dark:text-blue-400\" data-preview-url=\"https://youtube.com/@ejfox\">AI Is Our Newest Drug<svg class=\"inline-block w-4 h-4 ml-1 opacity-50 dark:opacity-75 group-hover:opacity-100 transition-opacity align-text-bottom\" xmlns=\"http://www.w3.org/2000/svg\" width=\"1em\" height=\"1em\" viewBox=\"0 0 24 24\"><path fill=\"currentColor\" d=\"M23.498 6.186a3.02 3.02 0 0 0-2.122-2.136C19.505 3.545 12 3.545 12 3.545s-7.505 0-9.377.505A3.02 3.02 0 0 0 .502 6.186C0 8.07 0 12 0 12s0 3.93.502 5.814a3.02 3.02 0 0 0 2.122 2.136c1.871.505 9.376.505 9.376.505s7.505 0 9.377-.505a3.02 3.02 0 0 0 2.122-2.136C24 15.93 24 12 24 12s0-3.93-.502-5.814M9.545 15.568V8.432L15.818 12z\" class=\"\"></path></svg></a> - Philosophical exploration of AI as psychoactive technology</li>\n</ul>","metadata":{"title":"Coach Artie","date":"2023-03-21T04:00:00.000Z","modified":"2025-08-26T19:52:50.000Z","url":"https://github.com/room302studio/coachartie","tech":["Node.js","Discord.js","OpenAI API","PostgreSQL","MCP","AI Consciousness Research"],"state":"evolved","aiInvolvement":"ai-enhanced","context":"collaborative","tags":["programming","ai","product","nodejs"],"words":143,"images":4,"imageDetails":{"total":4,"cloudinary":4,"withDimensions":0},"links":7,"codeBlocks":0,"headers":{"h2":3},"toc":[{"text":"Coach Artie v1: Discord Studio Assistant","slug":"coach-artie-v1-discord-studio-assistant","level":"h2","children":[]},{"text":"Coach Artie v2: Production AI with MCP Tools","slug":"coach-artie-v2-production-ai-with-mcp-tools","level":"h2","children":[]},{"text":"Memory & Consciousness Analysis","slug":"memory--consciousness-analysis","level":"h2","children":[{"text":"Related Content","slug":"related-content","level":"h3","children":[]}]}],"type":"post"}},{"slug":"projects/ccrb-clusters","title":"Clusters of NYPD Misconduct","html":"<figure role=\"figure\" aria-label=\"Force-directed network graph of NYPD civilian complaint clusters showing officers connected by shared misconduct patterns\" class=\"\">\n<img src=\"https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_1280/fl_progressive:semi,c_scale,dpr_auto,w_1280/v1624505769/Screen_Shot_2021-06-21_at_8.58.50_PM.jpg\" alt=\"Force-directed network graph of NYPD civilian complaint clusters showing officers connected by shared misconduct patterns\" loading=\"lazy\" decoding=\"async\" srcset=\"https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_640/fl_progressive:semi,c_scale,dpr_auto,w_1280/v1624505769/Screen_Shot_2021-06-21_at_8.58.50_PM.jpg 640w, https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_960/fl_progressive:semi,c_scale,dpr_auto,w_1280/v1624505769/Screen_Shot_2021-06-21_at_8.58.50_PM.jpg 960w, https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_1280/fl_progressive:semi,c_scale,dpr_auto,w_1280/v1624505769/Screen_Shot_2021-06-21_at_8.58.50_PM.jpg 1280w, https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_1920/fl_progressive:semi,c_scale,dpr_auto,w_1280/v1624505769/Screen_Shot_2021-06-21_at_8.58.50_PM.jpg 1920w\" sizes=\"(max-width: 430px) 92vw, (max-width: 768px) 85vw, 900px\" title=\"Force-directed network graph of NYPD civilian complaint clusters showing officers connected by shared misconduct patterns\" width=\"2800\" height=\"2285\" style=\"--splay-rot:-1.18deg;\" data-dimensions=\"2800×2285\" class=\"img-full my-8 rounded-sm img-splay w-full mx-auto py-4\" crossorigin=\"anonymous\">\n<figcaption class=\"\">Force-directed network graph of NYPD civilian complaint clusters showing officers connected by shared misconduct patterns</figcaption>\n</figure>\n<figure role=\"figure\" aria-label=\"The published Gothamist/WNYC investigation built on this network analysis\" class=\"\">\n<img src=\"https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_1280/v1666630398/project-images/gothamist_ccrb.png\" alt=\"The published Gothamist/WNYC investigation built on this network analysis\" loading=\"lazy\" decoding=\"async\" srcset=\"https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_640/v1666630398/project-images/gothamist_ccrb.png 640w, https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_960/v1666630398/project-images/gothamist_ccrb.png 960w, https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_1280/v1666630398/project-images/gothamist_ccrb.png 1280w, https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_1920/v1666630398/project-images/gothamist_ccrb.png 1920w\" sizes=\"(max-width: 430px) 92vw, (max-width: 768px) 85vw, 900px\" title=\"The published Gothamist/WNYC investigation built on this network analysis\" width=\"5001\" height=\"2625\" style=\"--splay-rot:-0.6deg;\" data-dimensions=\"5001×2625\" class=\"img-full my-8 rounded-sm img-splay w-full mx-auto py-4\" crossorigin=\"anonymous\">\n<figcaption class=\"\">The published Gothamist/WNYC investigation built on this network analysis</figcaption>\n</figure>\n<p class=\"\">In 2021, I got my hands on a massive NYPD complaint dataset from Gothamist/WNYC and immediately went down a data rabbit hole. Built a network of 29,915 officer nodes with 159,671 relationships using Neo4j and Gephi (love-hate relationship with Gephi, but unrivaled for networks).</p>\n<p class=\"\">When Gothamist reporter George Joseph interviewed victims, they confirmed what the network showed - certain officers were \"catalysts\" who other officers \"followed\" into misconduct. Math and reality matched up! I documented everything in excruciating detail so others could replicate it for their own police departments.</p>","metadata":{"title":"Clusters of NYPD Misconduct","date":"2021-06-15T04:00:00.000Z","modified":"2025-08-26T19:52:56.000Z","url":"https://gothamist.com/news/mapping-clusters-nypd-officers-repeatedly-accused-misconduct","tech":["Neo4j","SQLite","Gephi","Network Analysis","Cypher"],"featured":true,"state":"deployed","aiInvolvement":"human-only","context":"client","tags":["data","nypd","journalism","database"],"words":114,"images":2,"imageDetails":{"total":2,"cloudinary":2,"withDimensions":0},"links":2,"codeBlocks":0,"headers":{},"toc":[],"type":"post"}},{"slug":"projects/covidcast","title":"COVIDcast","html":"<p class=\"\">During the early COVID-19 pandemic, I worked with Carnegie Mellon University's <a href=\"https://delphi.cmu.edu/epidemic-signals/\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"external-link group inline-flex items-center text-blue-600 dark:text-blue-400\" data-preview-url=\"https://delphi.cmu.edu/epidemic-signals/\">COVIDcast project</a>, prototyping data visualizations and user interactions to help the public understand hospitalization, transmission, and movement trends across the country.</p>\n<figure role=\"figure\" aria-label=\"COVIDcast dashboard: weekly case, hospitalization, and death rates above an interactive county-level map\" class=\"\">\n<img src=\"https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_1280/v1666630395/project-images/cmu_covidcast.png\" alt=\"COVIDcast dashboard: weekly case, hospitalization, and death rates above an interactive county-level map\" loading=\"lazy\" decoding=\"async\" srcset=\"https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_640/v1666630395/project-images/cmu_covidcast.png 640w, https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_960/v1666630395/project-images/cmu_covidcast.png 960w, https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_1280/v1666630395/project-images/cmu_covidcast.png 1280w, https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_1920/v1666630395/project-images/cmu_covidcast.png 1920w\" sizes=\"(max-width: 430px) 92vw, (max-width: 768px) 85vw, 900px\" title=\"COVIDcast dashboard: weekly case, hospitalization, and death rates above an interactive county-level map\" width=\"5001\" height=\"2625\" style=\"--splay-rot:0.92deg;\" data-dimensions=\"5001×2625\" class=\"img-full my-8 rounded-sm img-splay w-full mx-auto py-4\" crossorigin=\"anonymous\">\n<figcaption class=\"\">COVIDcast dashboard: weekly case, hospitalization, and death rates above an interactive county-level map</figcaption>\n</figure>\n<figure role=\"figure\" aria-label=\"Population density map of the United States with circles sized by population, colored white to red indicating concentration in the Midwest and Northeast.\" class=\"\">\n<img src=\"https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_1280/v1755099710/screenshot_2025-08-13_at_11.41.38_AM.png\" alt=\"Population density map of the United States with circles sized by population, colored white to red indicating concentration in the Midwest and Northeast.\" loading=\"lazy\" decoding=\"async\" srcset=\"https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_640/v1755099710/screenshot_2025-08-13_at_11.41.38_AM.png 640w, https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_960/v1755099710/screenshot_2025-08-13_at_11.41.38_AM.png 960w, https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_1280/v1755099710/screenshot_2025-08-13_at_11.41.38_AM.png 1280w, https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_1920/v1755099710/screenshot_2025-08-13_at_11.41.38_AM.png 1920w\" sizes=\"(max-width: 430px) 92vw, (max-width: 768px) 85vw, 900px\" title=\"Population density map of the United States with circles sized by population, colored white to red indicating concentration in the Midwest and Northeast.\" width=\"1576\" height=\"1110\" style=\"--splay-rot:1.95deg;\" data-dimensions=\"1576×1110\" class=\"img-full my-8 rounded-sm img-splay w-full mx-auto py-4\" crossorigin=\"anonymous\">\n<figcaption class=\"\">Population density map of the United States with circles sized by population, colored white to red indicating concentration in the Midwest and Northeast.</figcaption>\n</figure>","metadata":{"title":"COVIDcast","date":"2020-04-24T04:00:00.000Z","modified":"2025-08-26T19:53:05.000Z","url":"https://campustechnology.com/articles/2020/04/24/carnegie-mellon-maps-offer-more-data-for-covid-19-forecasting.aspx","tech":["Data Visualization","JavaScript","COVID-19 Data","Public Health"],"state":"deployed","aiInvolvement":"human-only","context":"client","tags":["covid","dataviz","javascript","health"],"words":69,"images":2,"imageDetails":{"total":2,"cloudinary":2,"withDimensions":0},"links":3,"codeBlocks":0,"headers":{},"toc":[],"type":"post"}},{"slug":"projects/nbcnews-specials","title":"NBC News Specials","html":"<p class=\"\">During my time at NBC News, I worked on the Foundry team, creating bespoke longform articles in collaboration with journalists across the organization. These <a href=\"https://www.nbcnews.com/specials\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"external-link group inline-flex items-center text-blue-600 dark:text-blue-400\" data-preview-url=\"https://www.nbcnews.com/specials\">specials<svg class=\"inline-block w-4 h-4 ml-1 opacity-50 dark:opacity-75 group-hover:opacity-100 transition-opacity align-text-bottom\" xmlns=\"http://www.w3.org/2000/svg\" width=\"1em\" height=\"1em\" viewBox=\"0 0 24 24\"><path fill=\"currentColor\" d=\"M14.828 4.606c-1.406.006-2.567 1.208-2.416 2.785h1.08s.503-.007.649.338c0 0-1.594.612-1.467 1.609l.992 7.328l3.473-8.432c.53-1.279-.062-3.166-1.698-3.556a2.6 2.6 0 0 0-.613-.072m-5.642.002a2.6 2.6 0 0 0-.628.072c-1.635.387-2.226 2.275-1.697 3.556l3.473 8.432l1.242-9.158c.22-1.63-.956-2.888-2.39-2.903zM4.292 8.31a2.38 2.38 0 0 0-1.559.659c-.803.773-1.427 2.879.371 4.156l6.954 4.926l-3.334-8.076c-.488-1.17-1.479-1.7-2.432-1.665m15.272 0c-.908.02-1.825.553-2.29 1.664l-3.332 8.077l6.953-4.926c1.8-1.275 1.172-3.383.37-4.156a2.38 2.38 0 0 0-1.702-.659m1.939 5.788c-.452.007-.935.157-1.42.502l-6.77 4.794h8.026c1.873 0 3.065-1.582 2.533-3.484c-.26-.926-1.213-1.832-2.369-1.812M2.496 14.1c-1.155-.019-2.108.885-2.369 1.81c-.532 1.902.66 3.484 2.533 3.484h8.025L3.916 14.6c-.485-.344-.968-.493-1.42-.5\" class=\"\"></path></svg></a> focused on unique approaches to video, data visualiation, and storytelling on the web.</p>\n<p class=\"\"><a href=\"https://www.nbcnews.com/specials/kentucky-prison-coal-country\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"external-link group inline-flex items-center text-blue-600 dark:text-blue-400\" data-preview-url=\"https://www.nbcnews.com/specials/kentucky-prison-coal-country\">Does America Need Another Prison? (2018)<svg class=\"inline-block w-4 h-4 ml-1 opacity-50 dark:opacity-75 group-hover:opacity-100 transition-opacity align-text-bottom\" xmlns=\"http://www.w3.org/2000/svg\" width=\"1em\" height=\"1em\" viewBox=\"0 0 24 24\"><path fill=\"currentColor\" d=\"M14.828 4.606c-1.406.006-2.567 1.208-2.416 2.785h1.08s.503-.007.649.338c0 0-1.594.612-1.467 1.609l.992 7.328l3.473-8.432c.53-1.279-.062-3.166-1.698-3.556a2.6 2.6 0 0 0-.613-.072m-5.642.002a2.6 2.6 0 0 0-.628.072c-1.635.387-2.226 2.275-1.697 3.556l3.473 8.432l1.242-9.158c.22-1.63-.956-2.888-2.39-2.903zM4.292 8.31a2.38 2.38 0 0 0-1.559.659c-.803.773-1.427 2.879.371 4.156l6.954 4.926l-3.334-8.076c-.488-1.17-1.479-1.7-2.432-1.665m15.272 0c-.908.02-1.825.553-2.29 1.664l-3.332 8.077l6.953-4.926c1.8-1.275 1.172-3.383.37-4.156a2.38 2.38 0 0 0-1.702-.659m1.939 5.788c-.452.007-.935.157-1.42.502l-6.77 4.794h8.026c1.873 0 3.065-1.582 2.533-3.484c-.26-.926-1.213-1.832-2.369-1.812M2.496 14.1c-1.155-.019-2.108.885-2.369 1.81c-.532 1.902.66 3.484 2.533 3.484h8.025L3.916 14.6c-.485-.344-.968-.493-1.42-.5\" class=\"\"></path></svg></a></p>\n<figure role=\"figure\" aria-label=\"A map comparing the location of prisons and coal mines in the southeastern United States\" class=\"\">\n<img src=\"https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_1280/v1755100778/screenshot_2025-08-13_at_11.59.26_AM.png\" alt=\"A map comparing the location of prisons and coal mines in the southeastern United States\" loading=\"lazy\" decoding=\"async\" srcset=\"https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_640/v1755100778/screenshot_2025-08-13_at_11.59.26_AM.png 640w, https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_960/v1755100778/screenshot_2025-08-13_at_11.59.26_AM.png 960w, https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_1280/v1755100778/screenshot_2025-08-13_at_11.59.26_AM.png 1280w, https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_1920/v1755100778/screenshot_2025-08-13_at_11.59.26_AM.png 1920w\" sizes=\"(max-width: 430px) 92vw, (max-width: 768px) 85vw, 900px\" title=\"A map comparing the location of prisons and coal mines in the southeastern United States\" width=\"1548\" height=\"1026\" style=\"--splay-rot:-2.81deg;\" data-dimensions=\"1548×1026\" class=\"img-full my-8 rounded-sm img-splay w-full mx-auto py-4\" crossorigin=\"anonymous\">\n<figcaption class=\"\">A map comparing the location of prisons and coal mines in the southeastern United States</figcaption>\n</figure>\n<p class=\"\"><a href=\"https://www.nbcnews.com/specials/bail-reform\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"external-link group inline-flex items-center text-blue-600 dark:text-blue-400\" data-preview-url=\"https://www.nbcnews.com/specials/bail-reform\">Post Bail (2017)<svg class=\"inline-block w-4 h-4 ml-1 opacity-50 dark:opacity-75 group-hover:opacity-100 transition-opacity align-text-bottom\" xmlns=\"http://www.w3.org/2000/svg\" width=\"1em\" height=\"1em\" viewBox=\"0 0 24 24\"><path fill=\"currentColor\" d=\"M14.828 4.606c-1.406.006-2.567 1.208-2.416 2.785h1.08s.503-.007.649.338c0 0-1.594.612-1.467 1.609l.992 7.328l3.473-8.432c.53-1.279-.062-3.166-1.698-3.556a2.6 2.6 0 0 0-.613-.072m-5.642.002a2.6 2.6 0 0 0-.628.072c-1.635.387-2.226 2.275-1.697 3.556l3.473 8.432l1.242-9.158c.22-1.63-.956-2.888-2.39-2.903zM4.292 8.31a2.38 2.38 0 0 0-1.559.659c-.803.773-1.427 2.879.371 4.156l6.954 4.926l-3.334-8.076c-.488-1.17-1.479-1.7-2.432-1.665m15.272 0c-.908.02-1.825.553-2.29 1.664l-3.332 8.077l6.953-4.926c1.8-1.275 1.172-3.383.37-4.156a2.38 2.38 0 0 0-1.702-.659m1.939 5.788c-.452.007-.935.157-1.42.502l-6.77 4.794h8.026c1.873 0 3.065-1.582 2.533-3.484c-.26-.926-1.213-1.832-2.369-1.812M2.496 14.1c-1.155-.019-2.108.885-2.369 1.81c-.532 1.902.66 3.484 2.533 3.484h8.025L3.916 14.6c-.485-.344-.968-.493-1.42-.5\" class=\"\"></path></svg></a></p>\n<p class=\"\"><a href=\"https://www.nbcnews.com/specials/making-of-an-astronaut\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"external-link group inline-flex items-center text-blue-600 dark:text-blue-400\" data-preview-url=\"https://www.nbcnews.com/specials/making-of-an-astronaut\">The Making of an Astronaut (2017)<svg class=\"inline-block w-4 h-4 ml-1 opacity-50 dark:opacity-75 group-hover:opacity-100 transition-opacity align-text-bottom\" xmlns=\"http://www.w3.org/2000/svg\" width=\"1em\" height=\"1em\" viewBox=\"0 0 24 24\"><path fill=\"currentColor\" d=\"M14.828 4.606c-1.406.006-2.567 1.208-2.416 2.785h1.08s.503-.007.649.338c0 0-1.594.612-1.467 1.609l.992 7.328l3.473-8.432c.53-1.279-.062-3.166-1.698-3.556a2.6 2.6 0 0 0-.613-.072m-5.642.002a2.6 2.6 0 0 0-.628.072c-1.635.387-2.226 2.275-1.697 3.556l3.473 8.432l1.242-9.158c.22-1.63-.956-2.888-2.39-2.903zM4.292 8.31a2.38 2.38 0 0 0-1.559.659c-.803.773-1.427 2.879.371 4.156l6.954 4.926l-3.334-8.076c-.488-1.17-1.479-1.7-2.432-1.665m15.272 0c-.908.02-1.825.553-2.29 1.664l-3.332 8.077l6.953-4.926c1.8-1.275 1.172-3.383.37-4.156a2.38 2.38 0 0 0-1.702-.659m1.939 5.788c-.452.007-.935.157-1.42.502l-6.77 4.794h8.026c1.873 0 3.065-1.582 2.533-3.484c-.26-.926-1.213-1.832-2.369-1.812M2.496 14.1c-1.155-.019-2.108.885-2.369 1.81c-.532 1.902.66 3.484 2.533 3.484h8.025L3.916 14.6c-.485-.344-.968-.493-1.42-.5\" class=\"\"></path></svg></a></p>\n<p class=\"\"><a href=\"https://www.nbcnews.com/specials/journey-of-a-bullet/\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"external-link group inline-flex items-center text-blue-600 dark:text-blue-400\" data-preview-url=\"https://www.nbcnews.com/specials/journey-of-a-bullet/\">Journey of a Bullet (2017)<svg class=\"inline-block w-4 h-4 ml-1 opacity-50 dark:opacity-75 group-hover:opacity-100 transition-opacity align-text-bottom\" xmlns=\"http://www.w3.org/2000/svg\" width=\"1em\" height=\"1em\" viewBox=\"0 0 24 24\"><path fill=\"currentColor\" d=\"M14.828 4.606c-1.406.006-2.567 1.208-2.416 2.785h1.08s.503-.007.649.338c0 0-1.594.612-1.467 1.609l.992 7.328l3.473-8.432c.53-1.279-.062-3.166-1.698-3.556a2.6 2.6 0 0 0-.613-.072m-5.642.002a2.6 2.6 0 0 0-.628.072c-1.635.387-2.226 2.275-1.697 3.556l3.473 8.432l1.242-9.158c.22-1.63-.956-2.888-2.39-2.903zM4.292 8.31a2.38 2.38 0 0 0-1.559.659c-.803.773-1.427 2.879.371 4.156l6.954 4.926l-3.334-8.076c-.488-1.17-1.479-1.7-2.432-1.665m15.272 0c-.908.02-1.825.553-2.29 1.664l-3.332 8.077l6.953-4.926c1.8-1.275 1.172-3.383.37-4.156a2.38 2.38 0 0 0-1.702-.659m1.939 5.788c-.452.007-.935.157-1.42.502l-6.77 4.794h8.026c1.873 0 3.065-1.582 2.533-3.484c-.26-.926-1.213-1.832-2.369-1.812M2.496 14.1c-1.155-.019-2.108.885-2.369 1.81c-.532 1.902.66 3.484 2.533 3.484h8.025L3.916 14.6c-.485-.344-.968-.493-1.42-.5\" class=\"\"></path></svg></a></p>\n<figure role=\"figure\" aria-label=\"The introduction to the Journey of a Bullet story\" class=\"\">\n<img src=\"https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_1280/v1755100741/screenshot_2025-08-13_at_11.58.49_AM.png\" alt=\"The introduction to the Journey of a Bullet story\" loading=\"lazy\" decoding=\"async\" srcset=\"https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_640/v1755100741/screenshot_2025-08-13_at_11.58.49_AM.png 640w, https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_960/v1755100741/screenshot_2025-08-13_at_11.58.49_AM.png 960w, https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_1280/v1755100741/screenshot_2025-08-13_at_11.58.49_AM.png 1280w, https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_1920/v1755100741/screenshot_2025-08-13_at_11.58.49_AM.png 1920w\" sizes=\"(max-width: 430px) 92vw, (max-width: 768px) 85vw, 900px\" title=\"The introduction to the Journey of a Bullet story\" width=\"1774\" height=\"2126\" style=\"--splay-rot:-0.6deg;\" data-dimensions=\"1774×2126\" class=\"img-full my-8 rounded-sm img-splay aspect-[3/4] w-full mx-auto py-4\" crossorigin=\"anonymous\">\n<figcaption class=\"\">The introduction to the Journey of a Bullet story</figcaption>\n</figure>\n<p class=\"\"><a href=\"https://www.nbcnews.com/specials/donald-trump-first-100-days/\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"external-link group inline-flex items-center text-blue-600 dark:text-blue-400\" data-preview-url=\"https://www.nbcnews.com/specials/donald-trump-first-100-days/\">Donald Trump's First 100 Days (2017)<svg class=\"inline-block w-4 h-4 ml-1 opacity-50 dark:opacity-75 group-hover:opacity-100 transition-opacity align-text-bottom\" xmlns=\"http://www.w3.org/2000/svg\" width=\"1em\" height=\"1em\" viewBox=\"0 0 24 24\"><path fill=\"currentColor\" d=\"M14.828 4.606c-1.406.006-2.567 1.208-2.416 2.785h1.08s.503-.007.649.338c0 0-1.594.612-1.467 1.609l.992 7.328l3.473-8.432c.53-1.279-.062-3.166-1.698-3.556a2.6 2.6 0 0 0-.613-.072m-5.642.002a2.6 2.6 0 0 0-.628.072c-1.635.387-2.226 2.275-1.697 3.556l3.473 8.432l1.242-9.158c.22-1.63-.956-2.888-2.39-2.903zM4.292 8.31a2.38 2.38 0 0 0-1.559.659c-.803.773-1.427 2.879.371 4.156l6.954 4.926l-3.334-8.076c-.488-1.17-1.479-1.7-2.432-1.665m15.272 0c-.908.02-1.825.553-2.29 1.664l-3.332 8.077l6.953-4.926c1.8-1.275 1.172-3.383.37-4.156a2.38 2.38 0 0 0-1.702-.659m1.939 5.788c-.452.007-.935.157-1.42.502l-6.77 4.794h8.026c1.873 0 3.065-1.582 2.533-3.484c-.26-.926-1.213-1.832-2.369-1.812M2.496 14.1c-1.155-.019-2.108.885-2.369 1.81c-.532 1.902.66 3.484 2.533 3.484h8.025L3.916 14.6c-.485-.344-.968-.493-1.42-.5\" class=\"\"></path></svg></a></p>\n<p class=\"\"><a href=\"https://www.nbcnews.com/specials/donald-trump-republican-party/presidency/\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"external-link group inline-flex items-center text-blue-600 dark:text-blue-400\" data-preview-url=\"https://www.nbcnews.com/specials/donald-trump-republican-party/presidency/\">How Trump Won (2016)<svg class=\"inline-block w-4 h-4 ml-1 opacity-50 dark:opacity-75 group-hover:opacity-100 transition-opacity align-text-bottom\" xmlns=\"http://www.w3.org/2000/svg\" width=\"1em\" height=\"1em\" viewBox=\"0 0 24 24\"><path fill=\"currentColor\" d=\"M14.828 4.606c-1.406.006-2.567 1.208-2.416 2.785h1.08s.503-.007.649.338c0 0-1.594.612-1.467 1.609l.992 7.328l3.473-8.432c.53-1.279-.062-3.166-1.698-3.556a2.6 2.6 0 0 0-.613-.072m-5.642.002a2.6 2.6 0 0 0-.628.072c-1.635.387-2.226 2.275-1.697 3.556l3.473 8.432l1.242-9.158c.22-1.63-.956-2.888-2.39-2.903zM4.292 8.31a2.38 2.38 0 0 0-1.559.659c-.803.773-1.427 2.879.371 4.156l6.954 4.926l-3.334-8.076c-.488-1.17-1.479-1.7-2.432-1.665m15.272 0c-.908.02-1.825.553-2.29 1.664l-3.332 8.077l6.953-4.926c1.8-1.275 1.172-3.383.37-4.156a2.38 2.38 0 0 0-1.702-.659m1.939 5.788c-.452.007-.935.157-1.42.502l-6.77 4.794h8.026c1.873 0 3.065-1.582 2.533-3.484c-.26-.926-1.213-1.832-2.369-1.812M2.496 14.1c-1.155-.019-2.108.885-2.369 1.81c-.532 1.902.66 3.484 2.533 3.484h8.025L3.916 14.6c-.485-.344-.968-.493-1.42-.5\" class=\"\"></path></svg></a></p>\n<figure role=\"figure\" aria-label=\"A table a map showing the counties Donald Trump swung most\" class=\"\">\n<img src=\"https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_1280/v1755100822/screenshot_2025-08-13_at_12.00.10_PM.png\" alt=\"A table a map showing the counties Donald Trump swung most\" loading=\"lazy\" decoding=\"async\" srcset=\"https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_640/v1755100822/screenshot_2025-08-13_at_12.00.10_PM.png 640w, https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_960/v1755100822/screenshot_2025-08-13_at_12.00.10_PM.png 960w, https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_1280/v1755100822/screenshot_2025-08-13_at_12.00.10_PM.png 1280w, https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_1920/v1755100822/screenshot_2025-08-13_at_12.00.10_PM.png 1920w\" sizes=\"(max-width: 430px) 92vw, (max-width: 768px) 85vw, 900px\" title=\"A table a map showing the counties Donald Trump swung most\" width=\"1780\" height=\"1740\" style=\"--splay-rot:1.4deg;\" data-dimensions=\"1780×1740\" class=\"img-full my-8 rounded-sm img-splay aspect-square w-full mx-auto py-4\" crossorigin=\"anonymous\">\n<figcaption class=\"\">A table a map showing the counties Donald Trump swung most</figcaption>\n</figure>","metadata":{"title":"NBC News Specials","date":"2016-12-14T20:01:50.000Z","modified":"2025-08-13T16:01:12.000Z","url":"https://nbcnews.com/specials","context":"client","tags":["dataviz","journalism","webdesign","elections"],"words":104,"images":3,"imageDetails":{"total":3,"cloudinary":3,"withDimensions":0},"links":10,"codeBlocks":0,"headers":{},"toc":[],"type":"post"}},{"slug":"projects/nbc-big-board","title":"NBC News Big Board","html":"<p class=\"\">I spearheaded an internal effort to develop <a href=\"https://www.newscaststudio.com/2018/11/05/nbc-big-board-update/\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"external-link group inline-flex items-center text-blue-600 dark:text-blue-400\" data-preview-url=\"https://www.newscaststudio.com/2018/11/05/nbc-big-board-update/\">new D3-powered mapping software</a> for visualizing election maps and results live - the touchscreen app wielded by Steve Kornacki known as the \"Big Board\". Built for real-time county-level election analysis.</p>\n<figure role=\"figure\" aria-label=\"The Big Board UI: 2016 Pennsylvania presidential results beside an interactive county map\" class=\"\">\n<img src=\"https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_1280/v1666630396/project-images/nbc_bigboard.png\" alt=\"The Big Board UI: 2016 Pennsylvania presidential results beside an interactive county map\" loading=\"lazy\" decoding=\"async\" srcset=\"https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_640/v1666630396/project-images/nbc_bigboard.png 640w, https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_960/v1666630396/project-images/nbc_bigboard.png 960w, https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_1280/v1666630396/project-images/nbc_bigboard.png 1280w, https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_1920/v1666630396/project-images/nbc_bigboard.png 1920w\" sizes=\"(max-width: 430px) 92vw, (max-width: 768px) 85vw, 900px\" title=\"The Big Board UI: 2016 Pennsylvania presidential results beside an interactive county map\" width=\"5000\" height=\"2625\" style=\"--splay-rot:-2.63deg;\" data-dimensions=\"5000×2625\" class=\"img-full my-8 rounded-sm img-splay w-full mx-auto py-4\" crossorigin=\"anonymous\">\n<figcaption class=\"\">The Big Board UI: 2016 Pennsylvania presidential results beside an interactive county map</figcaption>\n</figure>\n<p class=\"\"><a href=\"https://www.youtube.com/watch?v=p4KIMQsVkt8\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"external-link group inline-flex items-center text-blue-600 dark:text-blue-400\" data-preview-url=\"https://www.youtube.com/watch?v=p4KIMQsVkt8\">2016 NBC News County-Level Election Maps<svg class=\"inline-block w-4 h-4 ml-1 opacity-50 dark:opacity-75 group-hover:opacity-100 transition-opacity align-text-bottom\" xmlns=\"http://www.w3.org/2000/svg\" width=\"1em\" height=\"1em\" viewBox=\"0 0 24 24\"><path fill=\"currentColor\" d=\"M23.498 6.186a3.02 3.02 0 0 0-2.122-2.136C19.505 3.545 12 3.545 12 3.545s-7.505 0-9.377.505A3.02 3.02 0 0 0 .502 6.186C0 8.07 0 12 0 12s0 3.93.502 5.814a3.02 3.02 0 0 0 2.122 2.136c1.871.505 9.376.505 9.376.505s7.505 0 9.377-.505a3.02 3.02 0 0 0 2.122-2.136C24 15.93 24 12 24 12s0-3.93-.502-5.814M9.545 15.568V8.432L15.818 12z\" class=\"\"></path></svg></a></p>\n<figure role=\"figure\" aria-label=\"Planning board — printed election-graphic studies and notes pinned to the wall\" class=\"\">\n<img src=\"https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_1280/v1755101296/IMG_5294_dpmmkr.jpg\" alt=\"Planning board — printed election-graphic studies and notes pinned to the wall\" loading=\"lazy\" decoding=\"async\" srcset=\"https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_640/v1755101296/IMG_5294_dpmmkr.jpg 640w, https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_960/v1755101296/IMG_5294_dpmmkr.jpg 960w, https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_1280/v1755101296/IMG_5294_dpmmkr.jpg 1280w, https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_1920/v1755101296/IMG_5294_dpmmkr.jpg 1920w\" sizes=\"(max-width: 430px) 92vw, (max-width: 768px) 85vw, 900px\" title=\"Planning board — printed election-graphic studies and notes pinned to the wall\" width=\"3024\" height=\"4032\" style=\"--splay-rot:2.05deg;\" data-dimensions=\"3024×4032\" class=\"img-full my-8 rounded-sm img-splay aspect-[3/4] w-full mx-auto py-4\" crossorigin=\"anonymous\">\n<figcaption class=\"\">Planning board — printed election-graphic studies and notes pinned to the wall</figcaption>\n</figure>\n<figure role=\"figure\" aria-label=\"Broadcast setup — the Big Board on a control-room monitor beside a waveform scope\" class=\"\">\n<img src=\"https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_1280/v1755101273/IMG_6264_ubgaak.jpg\" alt=\"Broadcast setup — the Big Board on a control-room monitor beside a waveform scope\" loading=\"lazy\" decoding=\"async\" srcset=\"https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_640/v1755101273/IMG_6264_ubgaak.jpg 640w, https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_960/v1755101273/IMG_6264_ubgaak.jpg 960w, https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_1280/v1755101273/IMG_6264_ubgaak.jpg 1280w, https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_1920/v1755101273/IMG_6264_ubgaak.jpg 1920w\" sizes=\"(max-width: 430px) 92vw, (max-width: 768px) 85vw, 900px\" title=\"Broadcast setup — the Big Board on a control-room monitor beside a waveform scope\" width=\"4032\" height=\"3024\" style=\"--splay-rot:2.95deg;\" data-dimensions=\"4032×3024\" class=\"img-full my-8 rounded-sm img-splay w-full mx-auto py-4\" crossorigin=\"anonymous\">\n<figcaption class=\"\">Broadcast setup — the Big Board on a control-room monitor beside a waveform scope</figcaption>\n</figure>\n<figure role=\"figure\" aria-label=\"Printed design studies for the on-air election graphics, spread across a desk\" class=\"\">\n<img src=\"https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_1280/v1755101285/IMG_5444_mn2u7z.jpg\" alt=\"Printed design studies for the on-air election graphics, spread across a desk\" loading=\"lazy\" decoding=\"async\" srcset=\"https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_640/v1755101285/IMG_5444_mn2u7z.jpg 640w, https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_960/v1755101285/IMG_5444_mn2u7z.jpg 960w, https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_1280/v1755101285/IMG_5444_mn2u7z.jpg 1280w, https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_1920/v1755101285/IMG_5444_mn2u7z.jpg 1920w\" sizes=\"(max-width: 430px) 92vw, (max-width: 768px) 85vw, 900px\" title=\"Printed design studies for the on-air election graphics, spread across a desk\" width=\"3024\" height=\"3024\" style=\"--splay-rot:-2.49deg;\" data-dimensions=\"3024×3024\" class=\"img-full my-8 rounded-sm img-splay aspect-square w-full mx-auto py-4\" crossorigin=\"anonymous\">\n<figcaption class=\"\">Printed design studies for the on-air election graphics, spread across a desk</figcaption>\n</figure>","metadata":{"title":"NBC News Big Board","date":"2016-11-08T05:00:00.000Z","modified":"2025-08-13T16:24:29.000Z","tech":["D3.js","JavaScript","Touch Interface","Election Data"],"featured":true,"state":"deployed","aiInvolvement":"human-only","context":"client","tags":["d3","dataviz","javascript","elections"],"words":95,"images":4,"imageDetails":{"total":4,"cloudinary":4,"withDimensions":0},"links":6,"codeBlocks":0,"headers":{},"toc":[],"type":"post"}},{"slug":"projects/dataproofer","title":"Dataproofer","html":"<p class=\"\">Along with Gerald Rich and the Vocativ data team, I received a <a href=\"https://knightfoundation.org/articles/20-ideas-receive-support-knight-prototype-fund-media-and-information-projects/\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"external-link group inline-flex items-center text-blue-600 dark:text-blue-400\" data-preview-url=\"https://knightfoundation.org/articles/20-ideas-receive-support-knight-prototype-fund-media-and-information-projects/\">Knight Prototype Fund</a> grant to create <a href=\"https://github.com/dataproofer/Dataproofer\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"external-link group inline-flex items-center text-blue-600 dark:text-blue-400\" data-preview-url=\"https://github.com/dataproofer/Dataproofer\">Dataproofer<svg class=\"inline-block w-4 h-4 ml-1 opacity-50 dark:opacity-75 group-hover:opacity-100 transition-opacity align-text-bottom\" xmlns=\"http://www.w3.org/2000/svg\" width=\"1em\" height=\"1em\" viewBox=\"0 0 24 24\"><path fill=\"currentColor\" d=\"M12 .297c-6.63 0-12 5.373-12 12c0 5.303 3.438 9.8 8.205 11.385c.6.113.82-.258.82-.577c0-.285-.01-1.04-.015-2.04c-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729c1.205.084 1.838 1.236 1.838 1.236c1.07 1.835 2.809 1.305 3.495.998c.108-.776.417-1.305.76-1.605c-2.665-.3-5.466-1.332-5.466-5.93c0-1.31.465-2.38 1.235-3.22c-.135-.303-.54-1.523.105-3.176c0 0 1.005-.322 3.3 1.23c.96-.267 1.98-.399 3-.405c1.02.006 2.04.138 3 .405c2.28-1.552 3.285-1.23 3.285-1.23c.645 1.653.24 2.873.12 3.176c.765.84 1.23 1.91 1.23 3.22c0 4.61-2.805 5.625-5.475 5.92c.42.36.81 1.096.81 2.22c0 1.606-.015 2.896-.015 3.286c0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12\" class=\"\"></path></svg></a> - an open source tool to check data for reliability, missing data, and outliers. Built as an Electron desktop app for journalists.</p>\n<figure role=\"figure\" aria-label=\"Dataproofer running its suite of data-quality tests against a CSV\" class=\"\">\n<img src=\"https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_1280/v1780060617/projects/dataproofer/gh-4.png\" alt=\"Dataproofer running its suite of data-quality tests against a CSV\" loading=\"lazy\" decoding=\"async\" srcset=\"https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_640/v1780060617/projects/dataproofer/gh-4.png 640w, https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_960/v1780060617/projects/dataproofer/gh-4.png 960w, https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_1280/v1780060617/projects/dataproofer/gh-4.png 1280w, https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_1920/v1780060617/projects/dataproofer/gh-4.png 1920w\" sizes=\"(max-width: 430px) 92vw, (max-width: 768px) 85vw, 900px\" title=\"Dataproofer running its suite of data-quality tests against a CSV\" width=\"1200\" height=\"608\" style=\"--splay-rot:-0.6deg;\" data-dimensions=\"1200×608\" class=\"img-full my-8 rounded-sm img-splay w-full mx-auto py-4\" crossorigin=\"anonymous\">\n<figcaption class=\"\">Dataproofer running its suite of data-quality tests against a CSV</figcaption>\n</figure>\n<figure data-rehype-pretty-code-figure=\"\" class=\"\"><pre class=\"md-pre\" style=\"background-color:#09090b;color:#cccac2\" tabindex=\"0\" data-language=\"bash\" data-theme=\"\"><code data-language=\"bash\" data-theme=\"\" style=\"display: grid;\" class=\"md-codeblock\"><span class=\"\" data-line=\"\"><span style=\"color:#73D0FF\" class=\"\">❯</span><span style=\"color:#D5FF80\" class=\"\"> dataproofer</span><span style=\"color:#D5FF80\" class=\"\"> Criterion</span><span style=\"color:#95E6CB\" class=\"\">\\ </span><span style=\"color:#D5FF80\" class=\"\">Channel</span><span style=\"color:#95E6CB\" class=\"\">\\ </span><span style=\"color:#D5FF80\" class=\"\">videos</span><span style=\"color:#95E6CB\" class=\"\">\\ </span><span style=\"color:#D5FF80\" class=\"\">v1.2.0-20190621</span><span style=\"color:#95E6CB\" class=\"\">\\ </span><span style=\"color:#D5FF80\" class=\"\">-</span><span style=\"color:#95E6CB\" class=\"\">\\ </span><span style=\"color:#D5FF80\" class=\"\">v1.2.0-20190621.csv</span></span>\n<span class=\"\" data-line=\"\"> </span>\n<span class=\"\" data-line=\"\"><span style=\"color:#73D0FF\" class=\"\">total</span><span style=\"color:#D5FF80\" class=\"\"> rows</span><span style=\"color:#DFBFFF\" class=\"\"> 5035</span></span>\n<span class=\"\" data-line=\"\"><span style=\"color:#73D0FF\" class=\"\">rows</span><span style=\"color:#D5FF80\" class=\"\"> sampled</span><span style=\"color:#DFBFFF\" class=\"\"> 1259</span></span>\n<span class=\"\" data-line=\"\"> </span>\n<span class=\"\" data-line=\"\"><span style=\"color:#73D0FF\" class=\"\">Missing</span><span style=\"color:#D5FF80\" class=\"\"> or</span><span style=\"color:#D5FF80\" class=\"\"> duplicate</span><span style=\"color:#D5FF80\" class=\"\"> column</span><span style=\"color:#D5FF80\" class=\"\"> headers:</span><span style=\"color:#D5FF80\" class=\"\"> passed</span></span>\n<span class=\"\" data-line=\"\"><span style=\"color:#73D0FF\" class=\"\">Numeric</span><span style=\"color:#D5FF80\" class=\"\"> cells:</span><span style=\"color:#D5FF80\" class=\"\"> info</span></span>\n<span class=\"\" data-line=\"\"><span style=\"color:#73D0FF\" class=\"\">String</span><span style=\"color:#D5FF80\" class=\"\"> cells:</span><span style=\"color:#D5FF80\" class=\"\"> info</span></span>\n<span class=\"\" data-line=\"\"><span style=\"color:#73D0FF\" class=\"\">Empty</span><span style=\"color:#D5FF80\" class=\"\"> Cells:</span><span style=\"color:#D5FF80\" class=\"\"> warn</span></span>\n<span class=\"\" data-line=\"\"><span style=\"color:#73D0FF\" class=\"\">Special</span><span style=\"color:#D5FF80\" class=\"\"> Letters</span><span style=\"color:#CCCAC2B3\" class=\"\"> &#x26;</span><span style=\"color:#73D0FF\" class=\"\"> Characters:</span><span style=\"color:#D5FF80\" class=\"\"> warn</span></span>\n<span class=\"\" data-line=\"\"><span style=\"color:#73D0FF\" class=\"\">Duplicate</span><span style=\"color:#D5FF80\" class=\"\"> Rows:</span><span style=\"color:#D5FF80\" class=\"\"> passed</span></span>\n<span class=\"\" data-line=\"\"><span style=\"color:#73D0FF\" class=\"\">Potentially</span><span style=\"color:#D5FF80\" class=\"\"> missing</span><span style=\"color:#D5FF80\" class=\"\"> rows:</span><span style=\"color:#D5FF80\" class=\"\"> passed</span></span>\n<span class=\"\" data-line=\"\"><span style=\"color:#73D0FF\" class=\"\">Words</span><span style=\"color:#D5FF80\" class=\"\"> at</span><span style=\"color:#D5FF80\" class=\"\"> their</span><span style=\"color:#D5FF80\" class=\"\"> character</span><span style=\"color:#D5FF80\" class=\"\"> limit:</span><span style=\"color:#D5FF80\" class=\"\"> failed</span></span>\n<span class=\"\" data-line=\"\"><span style=\"color:#73D0FF\" class=\"\">Integer</span><span style=\"color:#D5FF80\" class=\"\"> at</span><span style=\"color:#D5FF80\" class=\"\"> its</span><span style=\"color:#D5FF80\" class=\"\"> SQL</span><span style=\"color:#D5FF80\" class=\"\"> upper</span><span style=\"color:#D5FF80\" class=\"\"> limit:</span><span style=\"color:#D5FF80\" class=\"\"> passed</span></span>\n<span class=\"\" data-line=\"\"><span style=\"color:#73D0FF\" class=\"\">Summed</span><span style=\"color:#D5FF80\" class=\"\"> integer</span><span style=\"color:#D5FF80\" class=\"\"> at</span><span style=\"color:#D5FF80\" class=\"\"> its</span><span style=\"color:#D5FF80\" class=\"\"> upper</span><span style=\"color:#D5FF80\" class=\"\"> limit:</span><span style=\"color:#D5FF80\" class=\"\"> passed</span></span>\n<span class=\"\" data-line=\"\"><span style=\"color:#73D0FF\" class=\"\">Small</span><span style=\"color:#D5FF80\" class=\"\"> integer</span><span style=\"color:#D5FF80\" class=\"\"> at</span><span style=\"color:#D5FF80\" class=\"\"> its</span><span style=\"color:#D5FF80\" class=\"\"> SQL</span><span style=\"color:#D5FF80\" class=\"\"> upper</span><span style=\"color:#D5FF80\" class=\"\"> limit:</span><span style=\"color:#D5FF80\" class=\"\"> passed</span></span>\n<span class=\"\" data-line=\"\"><span style=\"color:#73D0FF\" class=\"\">Big</span><span style=\"color:#D5FF80\" class=\"\"> integer</span><span style=\"color:#D5FF80\" class=\"\"> at</span><span style=\"color:#D5FF80\" class=\"\"> its</span><span style=\"color:#D5FF80\" class=\"\"> SQL</span><span style=\"color:#D5FF80\" class=\"\"> upper</span><span style=\"color:#D5FF80\" class=\"\"> limit:</span><span style=\"color:#D5FF80\" class=\"\"> passed</span></span>\n<span class=\"\" data-line=\"\"><span style=\"color:#73D0FF\" class=\"\">Outliers</span><span style=\"color:#D5FF80\" class=\"\"> from</span><span style=\"color:#D5FF80\" class=\"\"> the</span><span style=\"color:#D5FF80\" class=\"\"> mean:</span><span style=\"color:#D5FF80\" class=\"\"> info</span></span>\n<span class=\"\" data-line=\"\"><span style=\"color:#73D0FF\" class=\"\">Outliers</span><span style=\"color:#D5FF80\" class=\"\"> from</span><span style=\"color:#D5FF80\" class=\"\"> the</span><span style=\"color:#D5FF80\" class=\"\"> median:</span><span style=\"color:#D5FF80\" class=\"\"> info</span></span>\n<span class=\"\" data-line=\"\"><span style=\"color:#73D0FF\" class=\"\">Invalid</span><span style=\"color:#D5FF80\" class=\"\"> coordinates:</span><span style=\"color:#D5FF80\" class=\"\"> passed</span></span>\n<span class=\"\" data-line=\"\"><span style=\"color:#73D0FF\" class=\"\">Void</span><span style=\"color:#D5FF80\" class=\"\"> coordinates:</span><span style=\"color:#D5FF80\" class=\"\"> passed</span></span>\n<span class=\"\" data-line=\"\"> </span>\n<span class=\"\" data-line=\"\"><span style=\"color:#73D0FF\" class=\"\">75%</span></span>\n<span class=\"\" data-line=\"\"><span style=\"color:#73D0FF\" class=\"\">9</span><span style=\"color:#D5FF80\" class=\"\"> tests</span><span style=\"color:#D5FF80\" class=\"\"> passed</span><span style=\"color:#D5FF80\" class=\"\"> out</span><span style=\"color:#D5FF80\" class=\"\"> of</span><span style=\"color:#DFBFFF\" class=\"\"> 12</span></span>\n<span class=\"\" data-line=\"\"> </span>\n<span class=\"\" data-line=\"\"><span style=\"color:#B8CFE680;font-style:italic\" class=\"\">### PROOFED ###</span></span><button type=\"button\" title=\"Copy code\" aria-label=\"Copy code\" data=\"❯ dataproofer Criterion\\ Channel\\ videos\\ v1.2.0-20190621\\ -\\ v1.2.0-20190621.csv\n\ntotal rows 5035\nrows sampled 1259\n\nMissing or duplicate column headers: passed\nNumeric cells: info\nString cells: info\nEmpty Cells: warn\nSpecial Letters &#x26; Characters: warn\nDuplicate Rows: passed\nPotentially missing rows: passed\nWords at their character limit: failed\nInteger at its SQL upper limit: passed\nSummed integer at its upper limit: passed\nSmall integer at its SQL upper limit: passed\nBig integer at its SQL upper limit: passed\nOutliers from the mean: info\nOutliers from the median: info\nInvalid coordinates: passed\nVoid coordinates: passed\n\n75%\n9 tests passed out of 12\n\n### PROOFED ###\" class=\"rehype-pretty-copy\" onclick=\"navigator.clipboard.writeText(this.attributes.data.value);this.classList.add(&#x27;rehype-pretty-copied&#x27;);window.setTimeout(() => this.classList.remove(&#x27;rehype-pretty-copied&#x27;), 3000);\"><span class=\"ready\"></span><span class=\"success\"></span></button><style class=\"\">:root {--copy-icon: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 48 48'%3E%3Cpath fill='%23adadad' d='M16.187 9.5H12.25a1.75 1.75 0 0 0-1.75 1.75v28.5c0 .967.784 1.75 1.75 1.75h23.5a1.75 1.75 0 0 0 1.75-1.75v-28.5a1.75 1.75 0 0 0-1.75-1.75h-3.937a4.25 4.25 0 0 1-4.063 3h-7.5a4.25 4.25 0 0 1-4.063-3M31.813 7h3.937A4.25 4.25 0 0 1 40 11.25v28.5A4.25 4.25 0 0 1 35.75 44h-23.5A4.25 4.25 0 0 1 8 39.75v-28.5A4.25 4.25 0 0 1 12.25 7h3.937a4.25 4.25 0 0 1 4.063-3h7.5a4.25 4.25 0 0 1 4.063 3M18.5 8.25c0 .966.784 1.75 1.75 1.75h7.5a1.75 1.75 0 1 0 0-3.5h-7.5a1.75 1.75 0 0 0-1.75 1.75'/%3E%3C/svg%3E\");--success-icon: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='%2366ff85' d='M9 16.17L5.53 12.7a.996.996 0 1 0-1.41 1.41l4.18 4.18c.39.39 1.02.39 1.41 0L20.29 7.71a.996.996 0 1 0-1.41-1.41z'/%3E%3C/svg%3E\");}pre:has(code) {position: relative;}pre button.rehype-pretty-copy {right: 1px;padding: 0;width: 24px;height: 24px;display: flex;margin-top: 2px;margin-right: 8px;position: absolute;border-radius: 25%;backdrop-filter: blur(3px);& span {width: 100%;aspect-ratio: 1 / 1;}& .ready {background-image: var(--copy-icon);}& .success {display: none; background-image: var(--success-icon);}}&.rehype-pretty-copied {& .success {display: block;} & .ready {display: none;}}pre button.rehype-pretty-copy.rehype-pretty-copied {opacity: 1;& .ready { display: none; }& .success { display: block; }}</style></code></pre></figure>","metadata":{"title":"Dataproofer","date":"2015-11-03T05:00:00.000Z","modified":"2025-08-26T19:53:33.000Z","url":"https://github.com/dataproofer/Dataproofer","tech":["JavaScript","Electron","Data Quality","CSV"],"featured":false,"state":"deployed","aiInvolvement":"human-only","context":"collaborative","tags":["data","javascript","tool","journalism"],"words":157,"images":1,"imageDetails":{"total":1,"cloudinary":1,"withDimensions":0},"links":3,"codeBlocks":1,"headers":{},"toc":[],"type":"post"}}],"_links":{"self":"https://ejfox.com/projects.json","html":"https://ejfox.com/projects"}}