{"$schema":"https://ejfox.com/schema/page-twin@1.json","kind":"project","url":"https://ejfox.com/projects/scrapbook-core","json_url":"https://ejfox.com/projects/scrapbook-core.json","generator":"ejfox.com/json-twin@1","data":{"cacheVersion":"2026-10-05-source-hash","sourceHash":"f336c06120b5352c090425174bbed4f06fd65f488f55e65cec9f33587f6decb1","html":"<p class=\"\">I have gotten pretty good at saving interesting things that I see on the internet. Anytime I read something I think I might want to reference, show someone, or think about later, I chuck it into my bookmarks. I've done this for over 10 years now, making little notes of my favorite things while wading through the ever-expanding deluge of \"content.\" Now I want to take a step back and map the constellations that emerge.</p>\n<figure role=\"figure\" aria-label=\"A phyllotaxis of the scrap corpus — every screenshot placed by the golden angle, the way a sunflower packs its seeds\" class=\"\">\n<img src=\"https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_1280/projects/scrapbook-core/phyllotaxis.png\" alt=\"A phyllotaxis of the scrap corpus — every screenshot placed by the golden angle, the way a sunflower packs its seeds\" loading=\"lazy\" decoding=\"async\" srcset=\"https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_640/projects/scrapbook-core/phyllotaxis.png 640w, https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_960/projects/scrapbook-core/phyllotaxis.png 960w, https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_1280/projects/scrapbook-core/phyllotaxis.png 1280w, https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_1920/projects/scrapbook-core/phyllotaxis.png 1920w\" sizes=\"(max-width: 430px) 92vw, (max-width: 768px) 85vw, 900px\" title=\"A phyllotaxis of the scrap corpus — every screenshot placed by the golden angle, the way a sunflower packs its seeds\" width=\"1600\" height=\"1600\" style=\"--splay-rot:1.18deg;\" data-dimensions=\"1600×1600\" class=\"img-full my-8 rounded-sm img-splay aspect-square w-full mx-auto py-4\" crossorigin=\"anonymous\">\n<figcaption class=\"\">A phyllotaxis of the scrap corpus — every screenshot placed by the golden angle, the way a sunflower packs its seeds</figcaption>\n</figure>\n<p class=\"\">All of these things — mastodon posts, pinboard bookmarks, github activity, are.na blocks — can be thought of as various scraps of paper sitting on my desk, clipped out from some source material. But first I needed to reclaim my data from the various services in which they live. Scrapbook-core is that engine: a scraper for my Pinboard bookmarks, are.na blocks, public GitHub actions, and Mastodon posts, all coordinated in an index file that handles rate limiting, sequencing, and the CLI options, landing everything in one database — the substrate everything else (<a href=\"#scrapbook-cli\" class=\"text-blue-600 dark:text-blue-400\">the CLI</a>, <a href=\"#scrapscroller\" class=\"text-blue-600 dark:text-blue-400\">the scrollers</a>) reads from. Every bookmark gets fetched and summarized into a standalone list of facts, tagged, embedded for similarity searching and clustering, and mined for relationships — <code class=\"md-inline-code\">[Person:Stewart Brand] -[:CreatedBy]-> [Publication:Whole Earth Catalog]</code> — that I can easily turn into nodes and edges with some lightweight regex parsing.</p>\n<figure role=\"figure\" aria-label=\"Running the scraper\" class=\"\"><video class=\"img-full my-8 rounded-sm\" autoplay loop muted playsinline preload=\"metadata\" poster=\"https://res.cloudinary.com/ejf/video/upload/so_0,f_jpg,q_auto,w_1280/v1722610899/Screen_Recording_2024-08-02_at_10.59.48_AM.jpg\" src=\"https://res.cloudinary.com/ejf/video/upload/q_auto/v1722610899/Screen_Recording_2024-08-02_at_10.59.48_AM.gif\" aria-label=\"Running the scraper\"></video><figcaption class=\"\">Running the scraper</figcaption></figure>\n<p class=\"\">Even though no one will likely be using this specific tool except for me, I will be practicing a bit of self care in the form of a well-crafted tool that I can find, and most importantly, figure out how to use months or years down the line. The ability to own, possess, remix, and re-explore my own data is crucial for me to make sense of the world. It's how I access my own thoughts, understand <em class=\"\">how</em> I think about things, and to find creative paths forward and decide what is worth focusing on. If I gave up those responsibilities to a faceless algorithm whose goals are quite different from my own, I would be giving up quite a lot. You are what you eat.</p>\n<p class=\"\">The full story: <a href=\"/blog/2024/my-modern-scrapbook\" class=\"text-blue-600 dark:text-blue-400\">My Modern Scrapbook</a>.</p>\n<h2 class=\"\" id=\"the-scrapbook-family\">The scrapbook family</h2>\n<h3 class=\"\" id=\"scrapbook-cli\">Scrapbook CLI</h3>\n<figure role=\"figure\" aria-label=\"The scrapbook CLI in action — browse bookmarks, full-text search, and an AI summary of each scrap\" class=\"\">\n<img src=\"https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_1280/projects/scrapbook-cli/app.png\" alt=\"The scrapbook CLI in action — browse bookmarks, full-text search, and an AI summary of each scrap\" loading=\"lazy\" decoding=\"async\" srcset=\"https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_640/projects/scrapbook-cli/app.png 640w, https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_960/projects/scrapbook-cli/app.png 960w, https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_1280/projects/scrapbook-cli/app.png 1280w, https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_1920/projects/scrapbook-cli/app.png 1920w\" sizes=\"(max-width: 430px) 92vw, (max-width: 768px) 85vw, 900px\" title=\"The scrapbook CLI in action — browse bookmarks, full-text search, and an AI summary of each scrap\" width=\"3804\" height=\"2474\" style=\"--splay-rot:0.83deg;\" data-dimensions=\"3804×2474\" class=\"img-full my-8 rounded-sm img-splay w-full mx-auto py-4\" crossorigin=\"anonymous\">\n<figcaption class=\"\">The scrapbook CLI in action — browse bookmarks, full-text search, and an AI summary of each scrap</figcaption>\n</figure>\n<p class=\"\"><a href=\"https://github.com/ejfox/scrapbook-cli\" 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/scrapbook-cli\">Scrapbook CLI<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 command-line interface for exploring the scrapbook — browse bookmarks, run full-text search, and read an AI summary of each scrap without leaving the terminal. The interface is built from blessed widgets, including a mini-map.</p>\n<h3 class=\"\" id=\"scrapscroller\">Scrapscroller</h3>\n<figure role=\"figure\" aria-label=\"scrapscroller running: the SCRAP_ZONE feed with source/tag sidebar and stats\" class=\"\">\n<img src=\"https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_1280/v1789191930/projects/scrapscroller/landing.png\" alt=\"scrapscroller running: the SCRAP_ZONE feed with source/tag sidebar and stats\" loading=\"lazy\" decoding=\"async\" srcset=\"https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_640/v1789191930/projects/scrapscroller/landing.png 640w, https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_960/v1789191930/projects/scrapscroller/landing.png 960w, https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_1280/v1789191930/projects/scrapscroller/landing.png 1280w, https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_1920/v1789191930/projects/scrapscroller/landing.png 1920w\" sizes=\"(max-width: 430px) 92vw, (max-width: 768px) 85vw, 900px\" title=\"scrapscroller running: the SCRAP_ZONE feed with source/tag sidebar and stats\" width=\"1440\" height=\"813\" style=\"--splay-rot:2.59deg;\" data-dimensions=\"1440×813\" class=\"img-full my-8 rounded-sm img-splay aspect-video w-full mx-auto py-4\" crossorigin=\"anonymous\">\n<figcaption class=\"\">scrapscroller running: the SCRAP_ZONE feed with source/tag sidebar and stats</figcaption>\n</figure>\n<p class=\"\">Scrapscroller turns the scraps into a scrolling feed, with a source and tag sidebar and running stats.</p>\n<h3 class=\"\" id=\"arena-cards\">Arena Cards</h3>\n<figure role=\"figure\" aria-label=\"Arena Cards — a real are.na channel laid out as draggable spatial cards\" class=\"\">\n<img src=\"https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_1280/projects/arena-cards/cards.png\" alt=\"Arena Cards — a real are.na channel laid out as draggable spatial cards\" loading=\"lazy\" decoding=\"async\" srcset=\"https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_640/projects/arena-cards/cards.png 640w, https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_960/projects/arena-cards/cards.png 960w, https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_1280/projects/arena-cards/cards.png 1280w, https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_1920/projects/arena-cards/cards.png 1920w\" sizes=\"(max-width: 430px) 92vw, (max-width: 768px) 85vw, 900px\" title=\"Arena Cards — a real are.na channel laid out as draggable spatial cards\" width=\"2389\" height=\"1593\" style=\"--splay-rot:2.04deg;\" data-dimensions=\"2389×1593\" class=\"img-full my-8 rounded-sm img-splay w-full mx-auto py-4\" crossorigin=\"anonymous\">\n<figcaption class=\"\">Arena Cards — a real are.na channel laid out as draggable spatial cards</figcaption>\n</figure>\n<p class=\"\"><a href=\"https://github.com/ejfox/arena-cards\" 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/arena-cards\">Arena Cards<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> turns an are.na channel into draggable cards for spatial thinking. It was originally made for a YouTube video exploring the are.na API and taking control of your own data.</p>\n<h3 class=\"\" id=\"retroscope\">Retroscope</h3>\n<figure role=\"figure\" aria-label=\"Retroscope running — config verification: Cloudinary initialized, all checks green, health server up\" class=\"\">\n<img src=\"https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_1280/v1789191374/projects/retroscope/landing.png\" alt=\"Retroscope running — config verification: Cloudinary initialized, all checks green, health server up\" loading=\"lazy\" decoding=\"async\" srcset=\"https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_640/v1789191374/projects/retroscope/landing.png 640w, https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_960/v1789191374/projects/retroscope/landing.png 960w, https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_1280/v1789191374/projects/retroscope/landing.png 1280w, https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_1920/v1789191374/projects/retroscope/landing.png 1920w\" sizes=\"(max-width: 430px) 92vw, (max-width: 768px) 85vw, 900px\" title=\"Retroscope running — config verification: Cloudinary initialized, all checks green, health server up\" width=\"1200\" height=\"800\" style=\"--splay-rot:0.6deg;\" data-dimensions=\"1200×800\" class=\"img-full my-8 rounded-sm img-splay w-full mx-auto py-4\" crossorigin=\"anonymous\">\n<figcaption class=\"\">Retroscope running — config verification: Cloudinary initialized, all checks green, health server up</figcaption>\n</figure>\n<p class=\"\"><a href=\"https://github.com/ejfox/retroscope\" 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/retroscope\">Retroscope<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> uses an AI to generate text descriptions of every screenshot stored in Cloudinary.</p>\n<h3 class=\"\" id=\"photos\">Photos</h3>\n<figure role=\"figure\" aria-label=\"A frame from the photo blog\" class=\"\">\n<img src=\"https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_1280/projects/photos/hero.png\" alt=\"A frame from the photo blog\" loading=\"lazy\" decoding=\"async\" srcset=\"https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_640/projects/photos/hero.png 640w, https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_960/projects/photos/hero.png 960w, https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_1280/projects/photos/hero.png 1280w, https://res.cloudinary.com/ejf/image/upload/c_scale,f_auto,q_auto:good,w_1920/projects/photos/hero.png 1920w\" sizes=\"(max-width: 430px) 92vw, (max-width: 768px) 85vw, 900px\" title=\"A frame from the photo blog\" width=\"1440\" height=\"900\" style=\"--splay-rot:-1.7deg;\" data-dimensions=\"1440×900\" class=\"img-full my-8 rounded-sm img-splay w-full mx-auto py-4\" crossorigin=\"anonymous\">\n<figcaption class=\"\">A frame from the photo blog</figcaption>\n</figure>\n<p class=\"\"><a href=\"https://github.com/ejfox/photos\" 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/photos\">Photos<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 the photo blog and the custom media-organizing and publishing app behind it. It's Nuxt and Cloudinary, fed by bash scripts and Apple Automator, for getting photographs from camera to web.</p>","title":"Scrapbook Core","metadata":{"title":"Scrapbook Core","date":"2026-05-05T05:00:00.000Z","category":"Tools","featured":false,"url":"https://github.com/ejfox/scrapbook-core","tech":["JavaScript"],"state":"deployed","ai-involvement":"ai-assisted","tags":["tools"],"words":581,"images":7,"imageDetails":{"total":7,"cloudinary":7,"withDimensions":0},"links":7,"codeBlocks":0,"headers":{"h2":1},"toc":[{"text":"The scrapbook family","slug":"the-scrapbook-family","level":"h2","children":[{"text":"Scrapbook CLI","slug":"scrapbook-cli","level":"h3","children":[]},{"text":"Scrapscroller","slug":"scrapscroller","level":"h3","children":[]},{"text":"Arena Cards","slug":"arena-cards","level":"h3","children":[]},{"text":"Retroscope","slug":"retroscope","level":"h3","children":[]},{"text":"Photos","slug":"photos","level":"h3","children":[]}]}],"type":"post"}},"_links":{"self":"https://ejfox.com/projects/scrapbook-core.json","html":"https://ejfox.com/projects/scrapbook-core","index":"/projects.json"}}