Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cfabb20537 | ||
|
|
f6611257b3 | ||
|
|
e0e08ba67d | ||
|
|
92a8311bb2 | ||
|
|
8684735f6c | ||
|
|
735406c0da | ||
|
|
d4102dea45 | ||
|
|
dcf8488ec2 | ||
|
|
606293585c | ||
|
|
3118905624 | ||
|
|
b46b016305 | ||
|
|
fe54e2eecb |
@@ -155,12 +155,15 @@
|
||||
|
||||
// Image and figure styling
|
||||
figure {
|
||||
margin: 2rem 0;
|
||||
margin: 2rem auto;
|
||||
text-align: center;
|
||||
|
||||
img {
|
||||
display: block;
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
@@ -174,6 +177,29 @@ figure {
|
||||
}
|
||||
}
|
||||
|
||||
// Two (or more) figures side by side, wrapping to a stack on narrow screens.
|
||||
// Use by wrapping images in a <div class="figure-row"> via the rawhtml shortcode.
|
||||
.figure-row {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
align-items: flex-start;
|
||||
gap: 1.5rem;
|
||||
margin: 2rem 0;
|
||||
|
||||
figure {
|
||||
flex: 1 1 0;
|
||||
min-width: 220px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
img {
|
||||
max-height: 70vh;
|
||||
width: auto;
|
||||
max-width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
// Also style standalone images in posts
|
||||
.post-content img:not(figure img) {
|
||||
display: block;
|
||||
|
||||
@@ -102,6 +102,11 @@ theme = "m10c"
|
||||
name = "Weeknotes"
|
||||
url = "/weeknotes/"
|
||||
weight = 4
|
||||
[[menu.tertiary]]
|
||||
identifier = "mygames"
|
||||
name = "My Boardgames"
|
||||
url = "/mygames/"
|
||||
weight = 1
|
||||
[[menu.tertiary]]
|
||||
identifier = "mytweets"
|
||||
name = "Local Tweet Archive"
|
||||
@@ -111,4 +116,9 @@ theme = "m10c"
|
||||
identifier = "randomrecipeproject"
|
||||
name = "Random Recipe Project (cooking videos)"
|
||||
url = "https://www.youtube.com/@RandomRecipeProject"
|
||||
weight = 1
|
||||
weight = 1
|
||||
[[menu.featured]]
|
||||
identifier = "bggpipe"
|
||||
name = "bggpipe: My automated boardgame organizer"
|
||||
url = "https://git.kestrelsnest.social/eric/bggpipe"
|
||||
weight = 2
|
||||
@@ -0,0 +1,12 @@
|
||||
---
|
||||
title:
|
||||
description: My board game library, cataloged from shelf photos — search it, browse it, dig into any title.
|
||||
---
|
||||
|
||||
Welcome to my board game library. It's [cataloged straight from photos of my shelves](https://git.kestrelsnest.social/eric/bggpipe), pulling details and cover art from BoardGameGeek. Search the collection below, or click any game to see the full rundown.
|
||||
|
||||
{{< rawhtml >}}
|
||||
<link rel="stylesheet" href="/games/embed.css">
|
||||
<div id="games-embed"><p>Loading the shelves…</p></div>
|
||||
<script src="/games/embed.js" defer></script>
|
||||
{{< /rawhtml >}}
|
||||
@@ -0,0 +1,28 @@
|
||||
---
|
||||
title: "Weeknotes: May 9–15, 2026"
|
||||
date: 2026-05-16T09:00:00-04:00
|
||||
draft: false
|
||||
description: An ordinary week felt almost luxurious — cutting laser faceplates for the long-overdue CONpossible trophies, chipping away at LocallyGrown quality-of-life features, and eyeing a solo trip back to Austin.
|
||||
tags:
|
||||
- weeknotes
|
||||
- locallygrown
|
||||
- conpossible
|
||||
---
|
||||
|
||||
*Taking a breather and catching up on all the thises and thats that have piled up over the last couple of months.*
|
||||
|
||||
It was a boring week, and I mean that in the best way. Routine has been a rarity around here lately, so a week full of ordinary tasks felt almost luxurious. Juniper has a few weeks of school left and it's coming down to the wire, so we're helping her push through to the end. I also got current on some at-home medical treatments I'd been deferring, the kind that eat two or three hours of an evening a few times a week. Knocking those out, along with the housework and the rest of the to-do list, was plenty.
|
||||
|
||||
## Shipped
|
||||
|
||||
Most of the week went to odd jobs around the house. I switched the 3D printer over to laser burner mode and finally cut the faceplates for the trophies from the CONpossible costume contest back in February. I printed and assembled the blue-ribbon certificates for the winners too, and got everything into the mail. That one had been looming over me for months, so clearing it off the list felt good.
|
||||
|
||||
I also kept chipping away at LocallyGrown.net. The bug fixes are getting rarer these days, so lately the work is mostly quality-of-life features and better documentation.
|
||||
|
||||
## Read
|
||||
|
||||
Not much reading this week. But I've been enjoying the new seasons of Taskmaster (such a good show for the soul), For All Mankind, and Star Trek: Academy.
|
||||
|
||||
## What's Next
|
||||
|
||||
The Athens Stomp Fest is next weekend, an annual free day-long surf music festival. Tiki Taco Tuesday started back up this week and runs weekly through August; I missed this first one thanks to a last-minute work conflict, but we plan to make every one we can after that. And the next production rollout at work got pushed back a few weeks, so I'm thinking about heading back to Austin on my own to take advantage of some face-to-face time with people there while the window's open.
|
||||
|
After Width: | Height: | Size: 223 KiB |
|
After Width: | Height: | Size: 377 KiB |
@@ -0,0 +1,62 @@
|
||||
---
|
||||
title: "Weeknotes: May 16–22, 2026"
|
||||
date: 2026-05-23T09:00:00-04:00
|
||||
draft: false
|
||||
description: A 20x speedup on LocallyGrown's product page, ten hours of surf music at the Athens Stomp Fest, the return of Tiki Taco Tuesday, and a CSA share signed over by friends.
|
||||
tags:
|
||||
- weeknotes
|
||||
- work
|
||||
- locallygrown
|
||||
- music
|
||||
- cooking
|
||||
- family
|
||||
lastmod: 2026-06-07T16:47:50.264Z
|
||||
---
|
||||
|
||||
*We're shifting from a jam-packed spring into the lazy days of summer, though there's nothing actually lazy about them.*
|
||||
|
||||
## Shipped
|
||||
|
||||
I voted in the statewide primaries this week. There were plenty of state and local races on the ballot, and I did my part to push the best candidates through to the next round.
|
||||
|
||||
It was a good week for LocallyGrown.net. I shipped a range of improvements, from small conveniences for the market managers to performance work on the webhooks that handle email provider callbacks. The one I'm proudest of is a roughly 20x speedup on the page that lists all the products, which is the heart of the whole system. It was already fast, at least as quick as any typical e-commerce platform, but now it loads instantly. It got so fast that it exposed some infrastructure that's normally invisible at slower speeds, so I got to clean that up too.
|
||||
|
||||
At work I've been laying the groundwork to take all the great work my team did before I arrived and apply it to other new systems on the roadmap. Most of it is making things internally consistent at the architectural level so the good patterns can be safely extracted and reused. Done right, it's completely invisible to users, but it makes the existing application easier to maintain and the next applications much easier to build. It's solid, satisfying work.
|
||||
|
||||
I've also been trying to leave my mark wherever I can, contributing where I see gaps and stepping in where I think I can make a difference. The most recent example is an internal documentation portal I built, a single Welcome Center that pulls together the architectural and design docs I've written, the educational presentations I've put together, and end-user documentation other folks contributed. Our launch went well, which means a lot of new eyes are on our corner of the company right now, and I wanted to give them somewhere soft to land and learn about who we are and what we're doing.
|
||||
|
||||
## Read
|
||||
|
||||
Reading time has fallen off a cliff lately, so my progress through [*Starter Villain*](https://books.kestrelsnest.social/book/27758/s/starter-villain) has been slooooow. I'm still enjoying it, just a few pages at a time.
|
||||
|
||||
## Played
|
||||
|
||||
The Athens Stomp Fest was a great time, ten hours of local-ish surf music in every flavor you can imagine. There was straight classic surf, surf that borrowed from spaghetti western soundtracks, southern gothic surf, post-apocalyptic surf, even science fiction surf. I love the variety and the creativity on display, and it's a day I look forward to every year. Ever since I was a teenager I've had this idea of starting a surf band themed around the classic Universal monsters. I'd play the Creature from the Black Lagoon on bass, naturally.
|
||||
|
||||

|
||||
|
||||
Tiki Taco Tuesday is back. My partner and I didn't miss a single week last year and pretty firmly established ourselves as regulars. We won't manage to be quite that consistent this year, but I'm thrilled to be back at it. We dress for the occasion, bring our own tiki mugs, and settle in for an evening of grilled tacos and great drinks. And there's Rum School, which I can't leave out. This week's lesson was "The Raw Material," all about how the sugar cane itself shapes the final product.
|
||||
|
||||

|
||||
|
||||
## Cooked
|
||||
|
||||
Some friends were out of town and couldn't pick up their CSA share, so they signed it over to me. I headed out to the farmers market early Saturday to grab it and ended up chatting with old friends for a while. A few of the vendors there are farmers I sold alongside some twenty-five years ago at this market's precursor, and who went on to sell at my own market for decades. It was good to catch up with them. I came home with a bounty of fresh vegetables, which I rounded out with an assortment of mushrooms, some pork, a few baked goods, and a bouquet of flowers. It was a great morning all around.
|
||||
|
||||

|
||||
|
||||
The neighborhood grocery had racks of ribs on buy-one-get-one, so of course I had to make a mess of them. My grill and smoker are out of commission at the moment, so I cooked them in the oven instead, low and slow, and they came out almost as good. They were at least as good as any pre-cooked ribs I can buy in town.
|
||||
|
||||
## Noticed
|
||||
|
||||
Summer is here. This is Juniper's last week of school, and I am so looking forward to not stressing over assignments and homework, though probably not even half as much as she is. It's been a rough year, and I think we all need a reset.
|
||||
|
||||

|
||||
|
||||
## Thinking About
|
||||
|
||||
At work, my immediate team is still focused on rolling out the histology application to the other lab that needs it, but more and more my attention is drifting toward what comes after. How do we keep the momentum going, and how do we safely extract the patterns that worked so we can apply them to the next product and hand them to other teams building in the same ecosystem? There's also the regulatory side. We operate in a heavily regulated environment, and a lot of my thinking lately is about telling the real rules apart from the ones that are really just internal interpretations of rules written for a different context and kept around out of inertia.
|
||||
|
||||
## What's Next
|
||||
|
||||
I've booked another trip to Austin in a couple of weeks, a low-key two-day visit to take advantage of some in-person time with a project manager who's also a director, in town that same week. I'm looking forward to it.
|
||||
|
After Width: | Height: | Size: 364 KiB |
|
After Width: | Height: | Size: 297 KiB |
@@ -40,17 +40,17 @@ A few weeks ago I was in San Carlos for a production launch, and I had a slow da
|
||||
|
||||
I went to see Apple Park, and specifically The Ring, the enormous circular building that was Steve Jobs's last project. I never actually saw it. As I mentioned in my weeknotes, the campus is buried so deeply in trees and clever landscaping that you can stand a hundred feet away and see nothing but oak and grass and a low fence half-swallowed by shrubs.
|
||||
|
||||

|
||||
{{< img src="IMG_3796.jpeg" alt="A sunlit woodland clearing. A large oak with deeply furrowed bark stands at right, its limbs arching overhead. Tall trees and dense shrubs ring the clearing, with a patch of blue sky and clouds showing through the canopy. The ground runs from leaf litter at left to lush grass dotted with small purple wildflowers in front. A low dark metal fence sits in the middle distance, mostly swallowed by greenery." caption="I'm standing about a hundred feet from the Ring. This is all you can see of it." >}}
|
||||
|
||||
But the reason I made the trip in the first place wasn't architectural. It was that the company hidden behind those trees made the machine that made me. I wanted to put my feet on the ground there, the week I was helping ship software into the industry that machine helped bring into being.
|
||||
|
||||
The visitor center had a scale model of the whole campus, above ground and below, and an augmented-reality app running on borrowed iPads that let you peel the model apart layer by layer and look at the engineering underneath.
|
||||
|
||||

|
||||
{{< img src="IMG_4040.jpeg" alt="A hand holds a tablet at arm's length. Its screen shows a detailed 3D aerial render of a vast circular glass building ringed by trees, labeled “Ring Building,” with onscreen controls and a prompt to swipe up. Behind the tablet, blurred and out of focus, is a large white physical scale model of the same campus." caption="The augmented-reality app at the visitor center, doing what the trees wouldn't let me." >}}
|
||||
|
||||
I got a coffee and sat at a table with my laptop while a man at the next table taught an enthusiastic class on how to make art on an iPad.
|
||||
|
||||

|
||||
{{< img src="IMG_3780.jpeg" alt="Looking straight down at a paper cup of latte on a wooden table. The foam and crema form two rounded lobes with a notch between them and a small stem at the top." caption="An apple, at Apple. I'll take it." >}}
|
||||
|
||||
I bought a t-shirt with the old six-color logo, the one I remember from the side of that ][+, sold only at the visitor center and nowhere else.
|
||||
|
||||
@@ -58,9 +58,9 @@ I bought a t-shirt with the old six-color logo, the one I remember from the side
|
||||
|
||||
Walking the grounds, I came across a barn. An actual barn, dark gray board-and-batten, tucked behind a fence and some tall grass near the edge of the campus. A plaque told the story. The Glendenning Barn was built around 1916 by the descendants of a Scottish family who'd been farming this land since 1851, back when the valley grew apricots and plums instead of software. The farm was sold off in the 1960s, the barn passed through Hewlett-Packard's campus and then Apple's, and when the Ring went up the barn was cataloged, taken apart, and rebuilt a little ways away. The most forward-looking company on earth kept a hundred-year-old hay barn and moved it out of the way rather than tear it down.
|
||||
|
||||

|
||||
{{< img src="IMG_3800.jpeg" alt="A dark gray board-and-batten barn with a steeply pitched roof, seen from below across a slope. A fence of tall, irregularly spaced gray slats stands in front of it, with shrubs and ornamental grasses around the base and a bright blue, partly cloudy sky above." caption="The Glendenning Barn, near the edge of the campus, a century older than anything else in sight." >}}
|
||||
|
||||

|
||||
{{< img src="IMG_3798.jpeg" alt="A black metal pedestal sign with a gold border and gold lettering, set in tall grass before a wall of green shrubs. The upper half is a dense block of readable text headed “Glendenning Barn”; below it is the City of Cupertino seal. The lower half of the pedestal is blank." caption="Built around 1916 on a farm that predates the valley's chips and code, dismantled and rebuilt when the Ring went up." >}}
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -0,0 +1,110 @@
|
||||
---
|
||||
title: Anatomy of a Support Inbox
|
||||
date: 2026-05-30
|
||||
draft: false
|
||||
description: How one email address quietly turned into a pipeline that reads, sorts, drafts, and watches my back — without ever taking a decision out of my hands.
|
||||
tags:
|
||||
- n8n
|
||||
- automation
|
||||
- ai
|
||||
- email
|
||||
- dmarc
|
||||
- locallygrown
|
||||
lastmod: 2026-05-30T05:58:31.507Z
|
||||
---
|
||||
|
||||
{{< callout type="info" title="TL;DR" >}}
|
||||
One email address, `support@locallygrown.net`, quietly turned into a pipeline that reads, sorts, and drafts replies before I ever see a message: it skips the noise, runs a spam gate that *fails open*, untangles forwarded threads to find the real sender, figures out which market they belong to (with a plain-English database as backup), and hands the whole thing to **Sage**, the same expert agent my market managers already trust. A recent branch even watches DMARC reports for anyone sending mail *as me* — and caught a "forger" that turned out to be a customer's security scanner. Every stage ends in a human decision, on purpose. The machine reads and drafts; I decide.
|
||||
{{< /callout >}}
|
||||
|
||||
For years, `support@locallygrown.net` was just an inbox I dreaded. Mail came in — a grower who couldn't log in, a customer whose eggs never showed up at pickup, a market manager with a policy question, and in between all of it, an unrelenting drizzle of SEO pitches and "I noticed your website could rank higher." Every message was a context switch, and most of them weren't even for me.
|
||||
|
||||
These days it's less an inbox than a pipeline. When an email lands, a small machine reads it before I ever do: it throws out the noise, decides whether it's spam, untangles forwarded threads to find the actual human, figures out which of our markets the person belongs to, drafts a reply, and hands me a tidy little ticket with all of that already done. And lately it does one more thing: it keeps an eye on who's out there sending mail *as me*.
|
||||
|
||||
None of this replaced me. That distinction matters to me more than the automation does, and I'll come back to it. But first, the anatomy.
|
||||
|
||||
## One address, a dozen jobs
|
||||
|
||||
The whole thing hangs off a single Mailgun inbound route that posts every message to an n8n workflow. n8n is the connective tissue, a self-hosted automation tool where each step is a node and you can watch data flow through them like water through pipes. That visibility turned out to matter a lot, because the work was never in any one clever step but in the whole chain.
|
||||
|
||||
{{< img src="support-processor-left.png" alt="The leftmost section of the support-processor canvas in n8n, a node-graph automation editor. A Mailgun Webhook trigger feeds a Switch node (in Rules mode) with two outputs. Output 0 runs up and to the right into an 'Evaluate DMARC Report' code node, then a 'Send DMARC Report' HTTP node. The 'Fallback' output runs along the bottom into 'Filter Skip List,' then a 'Detect Spam' node backed by Anthropic, then 'Filter Spam,' then 'Lookup Market from Email,' continuing off the right edge." caption="The front half of the pipeline. Everything enters through one Mailgun webhook, then the Switch forks the flow. DMARC reports peel off along the top to get parsed and watched, while everything else takes the “Fallback” path into the support chain: skip the noise, detect spam, filter it, and find the sender's market." >}}
|
||||
|
||||
Here's what the chain does, in order.
|
||||
|
||||
### 1. Skip the obvious noise
|
||||
|
||||
The first node is almost insultingly simple: a short list of senders that never need a human, like mail from myself, PayPal receipts, or anything from a `no-reply@` address. If the sender matches, the workflow just stops. No point spending a single CPU cycle thinking hard about a transaction receipt.
|
||||
|
||||
### 2. A spam gate that fails open
|
||||
|
||||
What's left goes through a small, fast language model whose only job is to answer one question: is this unsolicited junk? It returns a confidence score, and only mail it's *quite* sure about (80% or higher) gets dropped.
|
||||
|
||||
The interesting decision here isn't the model itself but what happens when the model is *unsure*, or when its answer comes back malformed: the workflow lets the message through. I made that call on purpose. A spam email that reaches me costs me three seconds and a delete. A real customer's email that gets silently eaten because a classifier had a bad day costs me a customer. Those are not symmetric mistakes, so the system is biased toward the cheaper one. "When in doubt, bother the human" is a good default for anything that sits between people and me.
|
||||
|
||||
### 3. Untangling the forward
|
||||
|
||||
A surprising amount of support mail arrives forwarded, and the single biggest source of those forwards is *me*. I've been online long enough to have accumulated email addresses like barnacles: old personal ones, role accounts, addresses I'd genuinely forgotten I had. People use every one of them. Someone digs an address of mine out of a decade-old order, or I'm still sitting in their contacts from who-knows-when, and they write to me directly instead of to support. So I forward it into the funnel, at which point the message looks like it's *from me*, about a problem that has nothing to do with me. Market managers do the same thing, bouncing a customer's complaint my way. Either way, the sender on the envelope is no longer the person with the problem.
|
||||
|
||||
So there's a node that does a bit of archaeology, hunting for the tell-tale signs (the `Begin forwarded message:` lines, the `Fwd:` in the subject, the quoted `From:` headers buried in the body) and digging out the *original* sender. It's unglamorous regex work, the kind that's never quite finished, but it means the rest of the pipeline reasons about the actual customer instead of the messenger, which matters most when the messenger is me. I'd very much rather the system not go looking *me* up as the aggrieved party.
|
||||
|
||||
### 4. Whose market is this, anyway?
|
||||
|
||||
LocallyGrown is a platform that hosts many local food markets, each with its own manager. To route a message well, I need to know which market the sender belongs to. There's a lookup table of market managers keyed by email, and most of the time that's an instant hit.
|
||||
|
||||
But when it misses, the fallback is my favorite part of the whole build: the workflow asks the production database, in plain English, through a separate "chat with a database" sub-workflow. It can take "which market does this person belong to?" and turn it into the actual SQL, run it, and hand back the answer. Take a grower who sells at several markets: a plain lookup just tells me who they are, but the database comes back with every market they belong to and how they use the system, which is the difference between a name and a full picture. The lookup table handles the fast path, and the database conversation is there as a safety net for everything it misses. Belt and suspenders, except the suspenders speak English.
|
||||
|
||||
{{< img src="talk-to-the-database.png" alt="A separate n8n canvas, the 'talk to the database' sub-workflow. A chat-message trigger and a 'When Executed by Another Workflow' trigger both feed into a 'Normalize Input' code node, which connects to a central 'Data Assistant Agent.' Three things hang beneath the agent: an Anthropic chat model, a simple memory store, and an 'Execute a SQL query' tool." caption="The entire “plain-English database” is this small: an agent, a model, a little memory, and one SQL tool. The support processor reaches it through the “When Executed by Another Workflow” trigger at the lower left." >}}
|
||||
|
||||
### 5. Sage
|
||||
|
||||
With the sender and their market now resolved, the pipeline reaches its heart: the hand-off to Sage.
|
||||
|
||||
{{< img src="support-processor-middle.png" alt="The middle section of the same support-processor canvas. From 'Lookup Market from Email,' the flow runs through 'Get Manager Market' (a data-table lookup), then 'Merge Market Data,' then an 'Unknown Sender?' decision node. Its 'true' branch passes through 'Edit Fields,' a call to the 'Chat with a database using AI' sub-workflow, and 'Merge in DB Response'; its 'false' branch skips straight ahead. Both paths converge on 'Prepare Sage Call.'" caption="Resolving the sender's market, then handing off to Sage. If the manager table already knows who they are, the flow runs straight to “Prepare Sage Call” along the “false” path. If it doesn't, the “true” path detours through the plain-English database to work out the market first. Either way, everything funnels into the call to Sage." >}}
|
||||
|
||||
By now the message has everything it needs around it: who actually sent it, whether it arrived through a forward, which market they belong to. The pipeline could hand all of that to a general-purpose model with a clever prompt and get a passable guess back, but it doesn't. It hands it to Sage instead.
|
||||
|
||||
Sage is honestly the most useful thing in this entire post, and I nearly forgot to mention them, which tells you how thoroughly I've come to take them for granted. They're an agent I built specifically for LocallyGrown, not an off-the-shelf model wearing a support hat. They have our actual documentation at their fingertips and a real working knowledge of how the platform behaves: markets, growers, orders, pickups, payments, the whole tangle. The things a general model would have to guess at, Sage simply knows.
|
||||
|
||||
And here's what makes them more than a triage gadget: Sage isn't only a behind-the-scenes step in this pipeline. They're the *same* agent market managers talk to directly inside the application, and those managers have been leaning on them hard, to genuinely good effect. So when a support email lands, it isn't getting handed to some bespoke email-only classifier I bolted on but routed to the exact same expert the managers already trust, just arriving through a different door. The inbox and the in-app assistant share one brain.
|
||||
|
||||
For triage, Sage hands back a small, strict packet of JSON: a category (grower issue, login problem, technical bug, payment, missing items, or a market-specific question), an urgency from 1 to 5, whether it's something a market manager should handle rather than me, and the part I lean on most, a **drafted reply**, written in a warm, plain voice.
|
||||
|
||||
Here's the honest version of how I use that draft, though, because it isn't what you'd guess: I have never once copied it into a reply and hit send. I *could*; the drafts are almost always spot-on. But that was never the point. Reading the suggested answer turns out to be nearly as valuable as reading the original question. Seeing the problem and a plausible response side by side keeps me from skimming past a detail or jumping to the wrong conclusion; the two together hand my brain a far better starting point than the bare email does. Then my own fingers write the actual reply from there. The draft works less as the output than as a second pair of eyes on the situation before I commit to an interpretation.
|
||||
|
||||
Sage is told never to ask for information they already have. If they know the market, they use the name. If there's an attachment, they acknowledge it instead of asking for one. These are little things, but they're what make the draft worth *reading*. A generic template would tell me nothing, while a draft that wrestles with *this* person's actual problem is what sharpens my own read of it.
|
||||
|
||||
### 6. Assembling the ticket
|
||||
|
||||
{{< img src="support-processor-right.png" alt="The rightmost section of the same support-processor canvas. 'Prepare Sage Call' connects to a 'Talk to Sage' HTTP node, then a 'Code in JavaScript' node that parses Sage's reply, then a 'Send to Eric' HTTP node that posts the finished ticket out through Mailgun." caption="The hand-off. “Prepare Sage Call” packages up the message and everything we've learned about it, “Talk to Sage” asks the agent, the code node unpacks their reply, and “Send to Eric” drops the finished ticket (analysis, draft, and original thread) into my inbox." >}}
|
||||
|
||||
Finally, the workflow assembles all of that into a single enriched email and drops it in my actual inbox. The subject line alone does a lot of work (something like `[Athens][MISSING ITEMS][P4] customer didn't receive eggs at pickup`), so I can triage from the subject before I even open it. Inside, I find Sage's read on the issue, the customer's address with a one-click copy button, the market (linked), the suggested reply in a quotable block, and the original thread underneath.
|
||||
|
||||
What used to be a raw, contextless email is now a ticket that has already been read, classified, routed, and answered in draft. I open it, glance, usually tweak a sentence, and send.
|
||||
|
||||
### 7. Watching who sends mail as me
|
||||
|
||||
The most recent addition has nothing to do with support, but it's the reason I started writing this post, because it turned into a small detective story.
|
||||
|
||||
Every day, the big mailbox providers send DMARC aggregate reports, gzipped XML files that tell you who's been sending email claiming to be from your domain, and whether it passed authentication. Almost nobody reads them, because they're unreadable. They were piling up in `support@` like everyone else's, getting triaged as if they were customer tickets.
|
||||
|
||||
So I gave them their own branch. A switch peels off anything that looks like a DMARC report before it touches the support machinery. From there it gets decompressed and parsed *inside the workflow*, which sounds trivial and absolutely was not. The off-the-shelf decompression node kept quietly eating the file, so I ended up writing a tiny gzip inflater by hand, in plain JavaScript, with no dependencies. There's a particular kind of joy, for someone who started on an Apple ][+, in implementing DEFLATE from first principles at midnight because a black-box node wouldn't cooperate. (It works. I validated it byte-for-byte against real reports before I trusted it.)
|
||||
|
||||
Once parsed, the branch applies a few rules about what's actually worth waking me for: anything getting *rejected*, anything failing authentication while claiming to be my domain, but *not* the enormous volume of mail that simply passes, and not the legitimate forwarding that fails for boring reasons. Only genuine surprises send an email.
|
||||
|
||||
The first surprise it caught was an AWS server I didn't recognize, sending as my domain, getting five messages rejected. For about an hour I was convinced someone was impersonating me. I host everything on DigitalOcean and have nothing in AWS. It wasn't my mail provider either. The thing that cracked it was a single reverse-DNS lookup: the IP belonged to `cloud-sec-av.com`, which is **Check Point's Harmony email security** (formerly Avanan).
|
||||
|
||||
The "attacker" was a *bodyguard*. One of my recipients runs their inbox behind a security scanner that intercepts incoming mail, inspects it, and re-injects it. When it passes my message through untouched, my signature survives and everything's fine. When it rewrites the message (adds a banner, mangles a link), my cryptographic signature breaks, and from the outside that looks exactly like forgery. My "reject" policy was correctly refusing to vouch for a tampered copy of my own email. Nothing was wrong. A security product was doing its job, and so was mine, and they happened to disagree.
|
||||
|
||||
That was the real lesson: in DMARC data alone, a recipient's spam filter and an actual impersonator look nearly identical. The only thing that told them apart was the reverse-DNS name, which the reports don't include. So the branch now does that lookup itself, recognizes the big email-security vendors by name, and files their forwarding under "informational" instead of paging me. Real strangers still ring the bell. Bodyguards don't.
|
||||
|
||||
## What runs through all of it
|
||||
|
||||
One thing keeps pulling me back. Every single stage of this pipeline ends in a human decision, on purpose.
|
||||
|
||||
The spam filter fails open. Sage writes a draft rather than a sent reply, and I never send that draft anyway; I read it to think more clearly, then write my own. The market routing only suggests, and I'm the one who confirms it. The DMARC monitor surfaces a judgment call and hands it to me with enough context to make it, without quarantining anyone or touching a DNS record. The machine handles the reading and the sorting and the first-draft thinking, all the repetitive, attention-shredding work. I do the deciding.
|
||||
|
||||
I'm not apologizing for that limitation. It's the only shape of AI automation I actually trust myself to run on a system real people depend on for their groceries and their livelihoods. Automation that acts on its own has to be *right*. Automation that drafts and defers only has to be *helpful*, and helpful is a much more forgiving target. A bad suggestion costs nothing more than the second it takes to ignore it. A bad autonomous action means a customer never got their reply, or a legitimate sender got blocked.
|
||||
|
||||
There's a pleasant recursion in how it got built, too. I built this *with* an AI assistant, pairing with it the way I'd pair with a sharp colleague. The rote stuff got handed off: write me a dependency-free gzip inflater, validate this parser against four real reports, run the whole node the way the platform will run it and show me it works. The judgment stayed mine: what counts as an anomaly, what to suppress, when to fail open, whether that AWS IP was a threat or a misunderstanding. It's the same philosophy, one level up. The tool amplified what I was doing without deciding anything that mattered.
|
||||
|
||||
`support@locallygrown.net` is still only an email address. It just has better instincts now, and the good sense to ask me before it does anything it can't take back.
|
||||
|
After Width: | Height: | Size: 108 KiB |
|
After Width: | Height: | Size: 96 KiB |
|
After Width: | Height: | Size: 147 KiB |
|
After Width: | Height: | Size: 84 KiB |
|
After Width: | Height: | Size: 395 KiB |
@@ -0,0 +1,55 @@
|
||||
---
|
||||
title: "Weeknotes: May 23–29, 2026"
|
||||
date: 2026-05-30T09:00:00-04:00
|
||||
draft: false
|
||||
description: Sending off a Gloomhaven plagueherald, letting AI write the tests I'd been avoiding, leek and potato soup from last week's borrowed CSA share, and a focused few days ahead in Austin.
|
||||
tags:
|
||||
- weeknotes
|
||||
- work
|
||||
- locallygrown
|
||||
- games
|
||||
- cooking
|
||||
lastmod: 2026-06-16T23:35:25.044Z
|
||||
---
|
||||
|
||||
*A restorative week, with the play recharging me about as much as the work paid off.*
|
||||
|
||||
## Shipped
|
||||
|
||||
At work I keep leaning into the part of the job that suits me best, which is spotting where things could connect better and then doing something about it. There are a lot of small teams here moving fast on tight timelines, and each one solves its problems in whatever way works for that team on that schedule. Stack enough of those together and you end up with the same job being done several different ways, with real gaps in between. I'm new, but I'm also one of the older hands in the room, and I've been handed a lot of latitude to make it easier for everyone to pull in the same direction. Last week it was the Welcome Center; this week it's been beating the drum for shared patterns and good documentation wherever I can find an audience. I'm thriving on it.
|
||||
|
||||
I also found some quiet time between features to point a few of the newer AI models at my test coverage for the LocallyGrown.net API endpoints. I'm the only consumer of that API right now, but that might not always be true, so a real test suite matters. The business logic lives in a separate service layer that's had thorough tests since the start. The API layer, though, the part that checks the shape of the data going in and out and decides what happens when something unexpected shows up, was mostly bare. Now it isn't. I still don't trust these tools to write code for me on their own, but they're surprisingly good, and getting better, at writing tests around code that already exists.
|
||||
|
||||
## Played
|
||||
|
||||
After months of playing a plagueherald in our Gloomhaven group, I finally hit my personal retirement goal and got to send the character off. Plagueheralds are a swarm of insects with a single hive mind, able to pull themselves into a humanoid shape, and they're built for spreading pestilence around the map to soften up enemies so the rest of the party can finish them. I named mine Cricket, after the character on It's Always Sunny in Philadelphia, and by the end I had the whole approach dialed in: the right mix of abilities and gear to race out ahead of the group and hit hard enough to leave enemies reeling before anyone else arrived. The retirement goal itself was a strange one. I had to become exhausted, the game's version of going down in a scenario, an absurd number of times, so my whole strategy was to charge in, deal tremendous damage, and not particularly care whether I survived. I'll miss that bag of bugs, but honestly it had gone formulaic by the end, and I'm ready for something new.
|
||||
|
||||
For my next character I went through the mercenaries we've unlocked and landed on Hail. She'd been an NPC until now, someone we dealt with to set up scenarios and move the story along, but a recent expansion turned her into a fully playable character. I love her personality. She wants to learn everything about everything and quietly resents that doing so means putting up with the real world and all its inconveniences. She's cranky and curious and odd, and I find a fair amount of her relatable. We're getting close to the endgame, so she'll probably be my last mercenary before we wrap this campaign and move on to the sequel, Frosthaven.
|
||||
|
||||

|
||||
|
||||
There's a chain of game stores called Level Up, with five or so locations around Georgia now. The big one is in Duluth, the Atlanta suburb I've mentioned before for its incredible run of international markets and restaurants. I have far too many games at home already and nobody to play them with, but I still love going just to soak up the room and browse. It's where I found the mercenary pack with Hail in it, long sold out online. If I lived closer to that one I'd be there constantly. They recently opened a location here in Athens, on the same side of town where my group meets for Gloomhaven, so I stopped in after our last session. It's smaller, and I don't feel the same pull to haunt it the way I would the Duluth store, but the selection is impressive and there are rows of tables set up for players. It's a great resource to have in town.
|
||||
|
||||

|
||||
|
||||
I also picked up the complete Mass Effect series on the Xbox for five dollars, most of it covered by rewards points I'd been sitting on, and started poking around. BioWare's Dragon Age is my favorite series for story, with Skyrim and Fallout close behind, but I'd somehow never touched Mass Effect. From what I hear it's a lot like Dragon Age with the fantasy swapped out for science fiction, which sounds exactly like my kind of thing.
|
||||
|
||||
## Cooked
|
||||
|
||||
I made a big pot of leek and potato soup out of that CSA haul I wrote about last week. This is a soup I can make with my eyes closed, and like the stone soup in the old story it's a fine excuse to use up whatever odds and ends of vegetables and mushrooms are loitering in the fridge. Every batch comes out a little different from the last, and I've yet to make one I didn't love.
|
||||
|
||||
## Noticed
|
||||
|
||||
Tiki Taco Tuesday is in full swing again, and I'm glad to have my summer Tuesday evenings spoken for through the season. The tacos are good and the tropical drinks are better, with a fresh Rum School lesson most weeks for anyone who wants to actually learn something.
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
## Thinking About
|
||||
|
||||
I'm still working on getting my work and life back into a better balance. It isn't out of control, and it's honestly probably healthier than it was at the old job, but I want to settle into a rhythm that leaves room for all the things I like to do outside of work. It's been far too long since I kept a steady release schedule on my Random Recipe Project videos, for one. LocallyGrown is more responsible for that than the day job is, though it all runs together in the end. There are only so many hours in a day.
|
||||
|
||||
## What's Next
|
||||
|
||||
A few days in Austin, in the office to meet people face to face, tour some labs, and absorb as much as I can while I'm there. I booked a hotel within walking distance of the office and deliberately picked one with no nightlife anywhere near it, so there's nothing to do but focus on why I came.
|
||||
|
After Width: | Height: | Size: 292 KiB |
|
After Width: | Height: | Size: 272 KiB |
|
After Width: | Height: | Size: 354 KiB |
|
After Width: | Height: | Size: 142 KiB |
|
After Width: | Height: | Size: 218 KiB |
@@ -0,0 +1,63 @@
|
||||
---
|
||||
title: "Weeknotes: May 30–June 5, 2026"
|
||||
date: 2026-06-06T09:00:00-04:00
|
||||
draft: false
|
||||
description: "A few focused days in Austin meeting the team in person, a day commiserating with bioscience friends over good beer, painting Hail at a new game store, and getting quietly pulled into Mass Effect."
|
||||
tags:
|
||||
- weeknotes
|
||||
- work
|
||||
- travel
|
||||
- games
|
||||
- cooking
|
||||
- family
|
||||
---
|
||||
|
||||
*Traveling for work on my own terms, balanced against a rich home life, is what I want every week to look like.*
|
||||
|
||||
## Shipped
|
||||
|
||||
Last week I signed off saying I'd booked a focused few days in Austin, and this week I went. With a relative lull in deliverables, and my team starting to map out our next major initiative, it was a good time to be in the room with everyone face to face. Austin is an easy flight from Atlanta. Door to office runs about six or seven hours, which is a hell of a commute but not a stressful one.
|
||||
|
||||
I usually drive to the airport and leave my car in one of the nearby lots, but with a single bag this time I tried MARTA instead. I got up early, drove a little over an hour to the closest station, which has long-term parking and caters to airport travelers, and took the train in from there. It didn't really save me any time, but it was cheaper, and there's something nice about not having to drive through the heart of the city to reach the airport. I expect I'll be doing this more often.
|
||||
|
||||

|
||||
|
||||
Since this was a solo trip rather than a team gathering, I booked a hotel essentially across the street from the office. It was really half a mile down the road, but there are sidewalks the whole way, so it came to a fifteen-minute walk, which for Texas counts as close. I'd booked it deliberately for having nothing around it, the way I mentioned last week, so I'd have no reason to do anything but focus on why I came. That worked exactly as intended this trip. It won't be my regular place, though. Normally I want somewhere I can walk around in the mornings and evenings, even if getting to the office then means an Uber, and there was nothing within walking distance of this one, no restaurants or shops at all.
|
||||
|
||||
The days themselves were fantastic. I got to brainstorm in person with people up and down the org chart, tour some of the labs I'll be writing software for, wander the building, and get to know the folks who have that office as their home base. Spending the time immersed in the company's culture did more for me than weeks of video calls could. I want to make these visits a regular thing.
|
||||
|
||||
## Read
|
||||
|
||||
I read more documentation this week, a lot of which I wrote myself. Going back over it again and again is how it all settles into my head, so I don't mind the repetition. I also read a bit more of [*Starter Villain*](https://books.kestrelsnest.social/book/27758/s/starter-villain), though I'm having a strange time concentrating on fiction lately, and the stack of books I want to get to keeps growing taller behind it.
|
||||
|
||||
## Played
|
||||
|
||||
Saturday I spent the whole day with friends from Atlanta and some mutual Athens friends at our favorite brewery, doing nothing but catching up. Nearly all of them are bioscience professionals, some of them at the CDC, and nearly all of them are feeling the current political climate in a direct way, so a good part of the day went to commiserating. I'm a bioscience professional myself now, and I found I could join that conversation in ways I couldn't have before. We finished the night at a newish Czech restaurant I'd been meaning to try, which more than lived up to the wait. I'm already looking forward to the next time.
|
||||
|
||||
Against my better judgment I also started into Mass Effect. I said up in Read that I'm struggling with fiction, which apparently isn't quite true, because I stop to read every scrap of lore the game hands me, and there's already been a novel's worth of it. The depth of the world-building is genuinely impressive, especially for the first game in a franchise, though I suppose that's what you expect from BioWare. Their stories have always had a grip on me.
|
||||
|
||||
Friday evening, to close out the week, I packed up my paints and the figures for my retired and upcoming Gloomhaven characters and drove over to a new game store on my side of town, The Painted Heretic. They're a home for tabletop battle games like Warhammer, RPGs like D&D, and weekly nights for things like Magic: The Gathering, and they kindly gave me some table space to sit and paint. I have a ridiculous number of little bottles of paint at home, but they were fully stocked with my favorite brand, and being able to walk over to the display and pull the exact shades I wanted was both lovely and a little dangerous. Fifteen dollars for five new bottles and an evening in a great room was well worth it. I'll be back.
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
## Cooked
|
||||
|
||||
Wings were on sale, so Monday I cooked up a big pile of them and put a second batch of raw ones in the freezer for another day.
|
||||
|
||||

|
||||
|
||||
## Noticed
|
||||
|
||||
I've never had to travel often for work, and I could never afford to do much of it for fun, so this is fairly new territory for me. I like seeing new places quite a lot, even when the act of traveling is a pain. One of the quieter hopes I have for this job is that it gets me genuinely comfortable with travel, and that it eventually lets me take the people I love somewhere new, in a calm and comfortable way.
|
||||
|
||||
## Thinking About
|
||||
|
||||
I'm chewing on how to absorb everything I'm learning right now as efficiently as I can, and, more to the point, how to get it back out again. Learning it for my own sake isn't enough. I need to be able to talk about these topics intelligently with other people, and especially with the actual experts in them, and to have those experts walk away from the conversation with full confidence in me. I haven't had to operate at that level since my last years of college, and the prospect is honestly a little daunting.
|
||||
|
||||
## What's Next
|
||||
|
||||
Gloomhaven next weekend, with my new mercenary Hail. We're closing in on the endgame fast, so I may only get a session or two with her before the whole campaign wraps.
|
||||
|
||||
My youngest, Juniper, has been invited out to Charlotte next weekend for HeroesCon, one of the bigger comics conventions. Last year, as a high school freshman, she won a scholarship for her comics through the Comics Appreciation Society, and now she gets passes to the show and spends some time working the society's booth. I'd assumed I would have to miss it for a work trip, but that trip got postponed, so it looks like I can go up for at least a day and let her show me around.
|
||||
|
After Width: | Height: | Size: 393 KiB |
|
After Width: | Height: | Size: 510 KiB |
@@ -0,0 +1,42 @@
|
||||
---
|
||||
title: "Weeknotes: June 6–12, 2026"
|
||||
date: 2026-06-13T09:00:00-04:00
|
||||
draft: false
|
||||
description: "The Gloomhaven campaign's sudden finish line, a great day at Athens Pride, painting Hail one more time, and a cherry Rock and Rye worth picking up."
|
||||
tags:
|
||||
- weeknotes
|
||||
- work
|
||||
- games
|
||||
- cooking
|
||||
- family
|
||||
---
|
||||
|
||||
*A quiet week of absorbing at work, with the end of a years-long campaign suddenly in sight.*
|
||||
|
||||
## Shipped
|
||||
|
||||
Nothing major shipped this week, unless you count the expense reports from my Austin trip. Otherwise the work week was spent quietly processing everything I took in while I was out there and preparing to move into a brand new domain.
|
||||
|
||||
## Read
|
||||
|
||||
Reading has really taken a back seat somehow. I'll get back to it some day.
|
||||
|
||||
## Played
|
||||
|
||||
Saturday was my first Gloomhaven session actually playing Hail, and she is a lot of fun. We got through two scenarios and then came to a realization: we only have two more to go, which makes our next session our very last one. This campaign has lasted a couple of years, and I knew we were near the end, but the suddenness caught us all off-guard. Not to fear, though. I've got Frosthaven sitting on the shelf and ready to go.
|
||||
|
||||
After gaming I raced downtown to our annual Pride festival. I arrived late but found parking right near the parade endpoint, and got there just as the first float pulled up. I walked the route backwards and managed to see everyone by the time I reached the front. Afterwards I hung out with friends for a bit and wandered through the many artists and other vendors who turned out. It was a great day.
|
||||
|
||||
Tuesday at Tiki Taco Tuesday I packed up my painting supplies, and several of my Gloomhaven players met me there to paint our minis. Hail is looking great, and I can't believe I only get one more session with her.
|
||||
|
||||
## Cooked
|
||||
|
||||
I love a good Old Fashioned and usually have all the ingredients for one at home. I also find Hochstadter's Rock and Rye to be a tasty and inexpensive ready-made alternative. They have a new cherry version that finally made it to Athens, and I picked up a bottle. Consider it Eric-approved.
|
||||
|
||||
## Thinking About
|
||||
|
||||
I've been tasked with automating both our release process and all of the documentation that goes into safely releasing software into a medical lab. What we're doing is incredibly disruptive to the established way of doing things, and just the opposite for our users in the lab. They're used to downtime that lasts for hours, with long stretches between updates, and we can now give them near-continuous updates without them even noticing we've done anything.
|
||||
|
||||
## What's Next
|
||||
|
||||
Juniper will be spending the weekend at HeroesCon in Charlotte, thanks to the comics scholarship she won a couple of years ago. She befriended Bob Camp, the co-creator of *Ren & Stimpy*, at the same convention last year, and I'm looking forward to spending a day with her as she shows me around.
|
||||
@@ -0,0 +1,111 @@
|
||||
---
|
||||
title: "A ghost hand and an audience of one"
|
||||
date: 2026-07-18T09:00:00-04:00
|
||||
draft: false
|
||||
description: "I taught my software test suite to record training videos, first for an app with exactly one user."
|
||||
tags:
|
||||
- projects
|
||||
- ai
|
||||
- video
|
||||
---
|
||||
|
||||
{{< callout type="info" title="TL;DR" >}}
|
||||
I taught my software test suite to record training videos. They star the real application, they narrate themselves with captions, and when the app changes, I rebuild them with one command instead of re-filming anything. The system was invented in an evening to teach one person her own custom software; the next night it made a forty-nine-video library for LocallyGrown.net. This is the story; a [companion post](/posts/2026-07-18-your-test-suite-is-a-video-studio/) has the technical details.
|
||||
{{< /callout >}}
|
||||
|
||||
Modern software developers write automated tests for their applications that use the app the way a person does. The tests open a browser, sign in, tap the buttons, fill in the forms, and check that the right things happened. Normally all of this runs invisibly, in what's called a headless browser, which is exactly what it sounds like: a browser with no window, doing its work where nobody can see. You just get a list of green checkmarks at the end.
|
||||
|
||||
Earlier this week I gave the tests for one of my projects a visible mode, mostly out of curiosity, so I could watch them run instead of trusting the checkmarks. It was oddly satisfying. A ghost hand glided through the app, tapping and typing, doing in seconds what would take me a minute. And watching it, I sent a message to the AI coding assistant I work with that said, nearly verbatim, "I watched the tests run and they're nice! It made me wonder what it would take to generate a training video I could give to Carol."
|
||||
|
||||
The answer turned out to be about an hour.
|
||||
|
||||
---
|
||||
|
||||
## Built for Carol
|
||||
|
||||
Carol is my partner, and Refreshing Spaces is her professional organizing and decluttering business. This week, I built her an app from scratch to run it, covering her clients and visits, invoices and payments, mileage, and the before-and-after photos that really show off all of her work. She works from her phone in clients' homes, so everything is designed for a phone screen. The app's entire user base is one person, and she is not a software developer.
|
||||
|
||||
That makes for a strangely pure version of a problem every software company faces: how do you teach someone to use a thing? There is no manual for an app that exists nowhere else, and nobody has recorded a YouTube tutorial for software with one user. If the app was going to be learnable, I had to make it teach itself. And in my household, as in most, "watch this forty-second video" beats "read this document I wrote."
|
||||
|
||||
---
|
||||
|
||||
## What a generated video looks like
|
||||
|
||||
{{< rawhtml >}}
|
||||
<video controls playsinline preload="metadata" style="display: block; margin: 2rem auto; width: auto; max-width: 50%; max-height: 50vh; border-radius: 8px;">
|
||||
<source src="logging-a-visit.mp4" type="video/mp4">
|
||||
</video>
|
||||
{{< /rawhtml >}}
|
||||
|
||||
Each video is under a minute. It opens on the app already signed in, with a caption bar across the bottom narrating each step in a sentence. A small blue ring plays the part of a finger, gliding to a button and pulsing when it taps, moving at a pace you can follow. Text gets typed one character at a time, the way a person types. Everything on screen is the real application, doing real work.
|
||||
|
||||
Now, viewers of my YouTube cooking series will know I'm no stranger to the world of video editing, but still I found this part genuinely pleasing: no camera was involved, and no video editor either. The videos are recorded by the same kind of automation that runs the tests. A short script says, in effect, "show this caption, tap this button, type this name, wait for the save," and the browser does it while recording itself. The video file that comes out is less like a film and more like a compiled program. The script is the source; the video is just what it produces today.
|
||||
|
||||
That one property makes maintaining these a dream, and I'll come back to it at the end.
|
||||
|
||||
The videos are silent on purpose, though that wasn't the original plan. The plan was voiceover, and each recording still produces a little narration script with timestamps for a voice to read. Some day I might play around with automated voice generation (synthesized speech continues to improve by leaps and bounds) but while creating these I realized the silent, captioned versions were the better product anyway. Carol can watch one in a client's living room without headphones, in a spare minute, with nothing to unmute.
|
||||
|
||||
All fifteen are public, so Carol can brag about her bespoke software to her friends: [refreshingspaces.life/help/videos](https://refreshingspaces.life/help/videos). They're grouped by the shape of her workday, with sections like "Start here," "Getting paid," and "Out and about," because when there's one user, the natural organization is her day, and I know exactly what her day looks like.
|
||||
|
||||
---
|
||||
|
||||
## Feeling human
|
||||
|
||||
Getting the recordings to feel like a person showing you something, rather than a machine executing steps, took several rounds of watching them and wincing.
|
||||
|
||||
The first version jumped from place to place, the way automation naturally moves, because a computer has no reason to scroll gradually when it can arrive instantly. A human would scroll and swipe with smooth motions, so every movement became a smooth, eased glide. Jump-cuts read as a machine. Eased motion reads as someone showing you.
|
||||
|
||||
The second problem only revealed itself on watching: the caption bar sits at the bottom of the phone screen, which is exactly where apps put their most important buttons, so several taps happened underneath the caption. Now, when a tap is headed for the bottom of the screen, the caption politely hops to the top for that moment. It's a small behavior, and it's the difference between a video you trust and one that visibly hides the thing it's teaching.
|
||||
|
||||
The third appeared on camera inside an error message. A form arrived with an amount already filled in, the automation typed a new amount on top of it, and the video proudly displayed "36.5012.00." The typing now clears a field before it fills it, the way you would.
|
||||
|
||||
All three of these were caught by a human (me!) watching the finished video, not by any automated check, and that's worth repeating for anyone leaning on automation for anything: reviewing your own generated material is the quality control, and it doesn't automate away.
|
||||
|
||||
One more choice from this stage, which I think about more than I expected to. I sprinkled Carol's business tagline throughout her app, but I chose to leave it out of the training captions. It belongs to her voice, and somehow it felt wrong seeing her voice in the caption bar narrating to herself where to tap. Somebody has to decide where a brand's voice does and doesn't go, and it turns out that's true even when the brand is your partner's and the audience is her.
|
||||
|
||||
---
|
||||
|
||||
## Porting it to LocallyGrown
|
||||
|
||||
The prototype was built in an evening for one person. The next night I brought the system to LocallyGrown.net, the farmers market platform I've built and run since 2002, and everything became plural. Shoppers, growers, and market managers each needed their own walkthroughs. People often shop on phones while managers often run markets on laptops (but not always!), so most walkthroughs got recorded twice, at phone size and at computer size. Twenty-five walkthroughs now produce forty-nine videos at [locallygrown.net/docs/videos](https://locallygrown.net/docs/videos), and short "watch" links sit inside the written documentation, right next to the paragraphs they illustrate.
|
||||
|
||||
{{< rawhtml >}}
|
||||
<div class="figure-row">
|
||||
<figure>
|
||||
<img src="refreshing-spaces-gallery.jpeg" alt="Phone screenshot of the Refreshing Spaces training videos page. Below the heading and a short introduction, a section labeled "Start here" lists four videos, each with a play button, a title, a one-sentence description, and a duration: Finding your way around, Adding a new client, Logging a visit, and Using the timer." />
|
||||
<figcaption>Carol's gallery is organized around her workday, not the app's feature list.</figcaption>
|
||||
</figure>
|
||||
<figure>
|
||||
<img src="locallygrown-gallery.jpeg" alt="Phone screenshot of LocallyGrown's video gallery in a cream and green color scheme. Under the heading "For shoppers," three cards titled Watch: Placing your first order, Watch: Searching your market, and Watch: Browsing by category, each with a play button, a description, and a duration." />
|
||||
<figcaption>LocallyGrown's gallery has to ask who you are first, because strangers need to find their section.</figcaption>
|
||||
</figure>
|
||||
</div>
|
||||
{{< /rawhtml >}}
|
||||
|
||||
The two video libraries came out differently in a way I didn't plan, and the difference says something about audiences. LocallyGrown's is grouped by role, because strangers arriving at a documentation page need to find their section. Carol's is grouped by her workday, because there are no roles when there's one user. You can see it in the writing, too. LocallyGrown's descriptions say things like "read the story behind the farm before you buy," addressed to a stranger, while Carol's say "a receipt when she pays," because I know exactly who "she" is.
|
||||
|
||||
Scaling up also meant the demo data had to grow up. The recordings run against a make-believe farmers market, seeded into the system the way test data always is, which just means the database gets filled with plausible fictional things before the cameras roll: a market called Sweetwater, farms with products, shoppers with baskets. One video introduces shoppers to their growers, and it's only as good as the fictional grower it features. The seeded farm turned out to have contradictory placeholder junk, including a "uses synthetic chemicals" badge on a market that bans them, a one-line bio, and an empty photo frame. So I wrote the farm properly: a real three-paragraph story, a coherent certification, a photo. Quality time writing fiction changed how real every frame of that video feels. Good demo data is a form of writing. And once I started I kept going, because this turned out to be my favorite part of the whole project. The fixtures grew into a small fictional world: growers with histories, neighbors who buy their food from each other, a recipe that travels from one kitchen to another, dinner made for family and friends. I spent nineteen years running a real market like that, so writing the fictional one drew on a deep well. Sweetwater became an aspirational story about what a market can be, and every video is a little window into it.
|
||||
|
||||
And one walkthrough turned out to be impossible to record. The video for adding a photo to a product showed nothing happening at the moment the photo should appear, because nothing did happen. The automation and the app's user interface framework disagree, in a subtle and interesting way, about what counts as "the user picked a file," so the automated recording genuinely cannot do what a real person's finger does. Real growers are unaffected. Rather than fake it, I dropped the video and kept the written guide. The [companion post](/posts/2026-07-18-your-test-suite-is-a-video-studio/) will be full of technical dives into issues like this, if that's your bag.
|
||||
|
||||
---
|
||||
|
||||
## A cautionary tale about junk drawers
|
||||
|
||||
The whole pitch of this system is that it records the real application with realistic data, and that pitch has a failure mode worth knowing about before you point a camera at a development database.
|
||||
|
||||
While getting Carol's videos ready for public sharing, I noticed that one table in the development copy of her database still held real information. A few days earlier, I had imported her actual mileage spreadsheet to test that feature, and the import was still sitting there. Nothing in it was sensitive, and none of it ever reached the public site, but it was real data in a database I treat as entirely fictional, and I only noticed because I happened to look.
|
||||
|
||||
A development database is a junk drawer. Things get put into it for good reasons and never taken out, and after enough time nobody remembers what's in the back. So a rule came out of the moment, and it's now built into the recording process: before recording anything meant for sharing, verify that every screen the camera visits shows only fictional data. The rule is deliberately not "check the data you remember putting in," because the whole problem is the data you forgot.
|
||||
|
||||
The fiction has to be constructed with some care in the other direction, too. Some features don't appear at all until they're configured, so the demo data has to be rich enough to unlock everything the videos teach. The one-tap mileage button needs a home-base address before it will show itself, so the demo data includes one. For the address I picked a brewery a few streets over that we like to hang out at, which satisfies both halves of the rule: rich enough to unlock the feature, and verifiably fake.
|
||||
|
||||
---
|
||||
|
||||
## Living with it
|
||||
|
||||
Here's why "the video is a compiled program" matters. Every product help video ever filmed starts rotting the day it's published. The interface changes, the video is now wrong, and re-filming is so tedious that mostly nobody does, which is why the internet is full of tutorials for versions of software that no longer exist.
|
||||
|
||||
My videos still rot. Literally within two hours of shipping, I had redesigned one page, added a label to another, and added a whole new tab to the navigation, and three videos went stale almost immediately. What changed is what happens next. Each fix was one command and a couple of minutes, and the freshly rebuilt videos simply replaced the old ones. There was one wrinkle where the internet itself kept showing everyone the old copies for a while (networks keep cached copies of files to serve them faster, and mine didn't know the videos had changed) but that too had a small, boring fix, covered in the [companion post](/posts/2026-07-18-your-test-suite-is-a-video-studio/).
|
||||
|
||||
The discipline that emerged is a single question, asked after every change I ship: which videos show the screens I just touched? I rebuild those and move on. Nothing reminds me to ask it yet, and the system only stays cheap as long as someone does, so the question is headed for a checklist. In the meantime, every rebuild means I get to watch the ghost hand glide through the apps again, and that hasn't gotten old yet.
|
||||
|
After Width: | Height: | Size: 70 KiB |
|
After Width: | Height: | Size: 79 KiB |
@@ -0,0 +1,285 @@
|
||||
---
|
||||
title: "Your test suite is a video studio"
|
||||
date: 2026-07-18T09:00:00-04:00
|
||||
draft: false
|
||||
description: "How a Playwright end-to-end harness became a training-video studio: the helpers, the overlay, the fixtures, and the walkthrough that couldn't be recorded."
|
||||
tags:
|
||||
- projects
|
||||
- programming
|
||||
- playwright
|
||||
- svelte
|
||||
---
|
||||
|
||||
{{< callout type="info" title="TL;DR" >}}
|
||||
The technical companion to [part one](/posts/2026-07-18-a-ghost-hand-and-an-audience-of-one/). If your web app has a Playwright end-to-end suite, you already own most of a training-video studio. This post walks through what it takes to turn one into the other: a small helper vocabulary, an injected overlay, honest fixtures, and a build pipeline, with the lessons I picked up at each step.
|
||||
{{< /callout >}}
|
||||
|
||||
Part one told the story; this post is the how. The pitch, one more time for the people who came straight here: I generate silent, captioned training videos for two web applications by driving them with browser automation, and when an interface changes I rebuild the affected videos with one command. If you want to see the output first, the two libraries live at [locallygrown.net/docs/videos](https://locallygrown.net/docs/videos) and [refreshingspaces.life/help/videos](https://refreshingspaces.life/help/videos). Everything below is what you'd need to know to do the same for your own project.
|
||||
|
||||
---
|
||||
|
||||
## One insight starts it
|
||||
|
||||
Playwright can record video of everything that happens in a browser context. Turn that option on, and every test you already have becomes a screen recording. That's the whole foundation, and it's why this took an evening rather than a month: an end-to-end test and a walkthrough video are the same sequence of navigations and interactions. A test asserts at each step. A walkthrough narrates at each step. Everything else is presentation.
|
||||
|
||||
So the first thing to build isn't video infrastructure at all. It's a flow: a name, a viewport, a starting URL, and an async function that performs the steps. Flows are data, which means the generator can run one, several, or all of them, and your video library has a single source of truth you can read like a table of contents.
|
||||
|
||||
```ts
|
||||
export type Flow = {
|
||||
title: string;
|
||||
description: string;
|
||||
/** Per-role storageState path (playwright/.auth/<role>.json) — reused from
|
||||
* the e2e global-setup so recordings start already signed in. Omit for an
|
||||
* anonymous (signed-out) walkthrough. */
|
||||
storageState?: string;
|
||||
/** Routes to pre-compile on an unrecorded page before recording, so the
|
||||
* cold vite dev-server compile of the first page isn't recorded as dead
|
||||
* air. List every route the flow navigates to. */
|
||||
warmup?: string[];
|
||||
/** Optional fixture work on an unrecorded page (full speed, no captions). */
|
||||
prepare?: (page: Page) => Promise<void>;
|
||||
/** Recording viewport. 'desktop' uses 1280×800 + a desktop UA + centered,
|
||||
* larger captions; omit (or 'mobile') for the default phone size. */
|
||||
device?: 'mobile' | 'desktop';
|
||||
record: (page: Page) => Promise<void>;
|
||||
};
|
||||
```
|
||||
|
||||
A flow itself reads like stage directions:
|
||||
|
||||
```ts
|
||||
'01-place-an-order': {
|
||||
title: 'Placing your first order',
|
||||
description: 'Browse your market, add fresh items to your basket, and reserve them for pickup.',
|
||||
username: 'e2e-customer', // Casey Customer
|
||||
warmup: ['/market', '/cart/review'],
|
||||
async record(page) {
|
||||
await goto(page, '/market');
|
||||
await say(page, "Welcome to your market — let's place an order.");
|
||||
|
||||
// Skim down to the fresh items, the way you would on your phone.
|
||||
await scrollBy(page, 620);
|
||||
await say(page, "Scroll through what's fresh from your local growers.");
|
||||
await scrollBy(page, 380);
|
||||
|
||||
// First item — tap() smooth-scrolls it to center before the finger
|
||||
// lands. Anchor the cart write so the basket persists before we leave.
|
||||
const tomatoes = productCard(page, 'Heirloom Tomatoes');
|
||||
await say(page, 'Found something you like? Tap Add to Order.');
|
||||
let flushed = page.waitForResponse(
|
||||
(r) => r.url().includes('/api/cart/batch-update') && r.request().method() === 'POST'
|
||||
);
|
||||
await tap(page, tomatoes.getByRole('button', { name: 'Add to Order' }));
|
||||
await flushed;
|
||||
await say(page, "It's reserved — add as much as you like, from any grower.");
|
||||
|
||||
// … eggs from a second grower, the basket review, and Reserve Now
|
||||
// follow the same shape …
|
||||
},
|
||||
},
|
||||
```
|
||||
|
||||
One configuration rule I'll hand you up front, because I learned it by squinting at blurry output: the recording size must exactly equal the viewport size. The comment in the harness says it with the authority of experience:
|
||||
|
||||
```ts
|
||||
const context = await browser.newContext({
|
||||
...contextOpts,
|
||||
// recordVideo.size MUST equal the viewport 1:1 — a larger canvas
|
||||
// letterboxes the page into a corner instead of scaling up.
|
||||
recordVideo: { dir: OUT_DIR, size: viewport },
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## A vocabulary of four helpers
|
||||
|
||||
Nearly everything the flows do goes through four helpers, and building these well is most of the work.
|
||||
|
||||
The navigation helper goes to a URL and waits for the app to hydrate rather than merely load. A SvelteKit page is visible before it's interactive, and early on I had recordings that clicked during that gap: the video shows a confident tap and then nothing, which reads as broken software. Waiting for hydration is the difference. Mine watches for an attribute the app sets when it's ready:
|
||||
|
||||
```ts
|
||||
/** Navigate and wait for client hydration (the app sets body[data-hydrated]). */
|
||||
export async function goto(page: Page, path: string) {
|
||||
await page.goto(`${BASE}${path}`);
|
||||
await page.locator('body[data-hydrated]').waitFor({ timeout: 30_000 });
|
||||
}
|
||||
```
|
||||
|
||||
The caption helper puts a sentence on screen and holds it long enough to read. My first version used a flat delay, and watching those drafts taught me that a flat delay is wrong in both directions: short captions overstay and long captions flash past. Hold time now scales with word count, and each caption is also logged with a timestamp for the narration cue sheet:
|
||||
|
||||
```ts
|
||||
/** Show a caption and hold it on screen long enough to comfortably read
|
||||
* before anything moves. Hold time scales with length (a flat delay left
|
||||
* longer lines flashing past); pass readMs to override. */
|
||||
export async function say(page: Page, text: string, readMs?: number) {
|
||||
const words = text.trim().split(/\s+/).length;
|
||||
const hold = readMs ?? Math.max(2800, 1000 + words * 360);
|
||||
narration.push({ atMs: Date.now() - recordingStart, text });
|
||||
await page.evaluate((t) => {
|
||||
const el = document.getElementById('tv-caption');
|
||||
if (el) {
|
||||
// Reset to the bottom each caption — a previous tap may have dodged
|
||||
// it to the top; the next caption should read from its home spot.
|
||||
el.classList.remove('top');
|
||||
el.textContent = t;
|
||||
el.classList.add('show');
|
||||
}
|
||||
}, text);
|
||||
await pause(page, hold);
|
||||
}
|
||||
```
|
||||
|
||||
The tap helper is where the humanity lives. It smooth-scrolls the target toward the center of the viewport with an eased animation, then glides a synthetic finger to it over two dozen interpolated steps, pulses, and clicks. The glide exists because my first recordings jumped from place to place, the way automation naturally moves, and watching them I had the same reaction every time: it feels like a machine. Easing fixed it, and I never had that reaction again. Nobody else saw a single video until I was happy with them, which is its own small lesson — you are the first audience for your generated material, and if it makes you wince, that's a signal. Sending your material out into the world sight unseen guarantees someone else will be wincing on your behalf.
|
||||
|
||||
```ts
|
||||
export async function tap(page: Page, locator: Locator) {
|
||||
// Bring the target into view within whatever scrolls it — a modal's own
|
||||
// overflow container, not just the window (smoothCenter only scrolls the
|
||||
// window). Without this, a button below the fold of a tall modal is never
|
||||
// reachable and the tap lands nowhere.
|
||||
await locator.scrollIntoViewIfNeeded().catch(() => {});
|
||||
// Smooth-scroll the target toward center (a human glide), then read its
|
||||
// post-scroll box so the finger lands accurately.
|
||||
await smoothCenter(page, locator);
|
||||
const box = await locator.boundingBox();
|
||||
if (!box) throw new Error('tap target not visible');
|
||||
const viewportHeight = page.viewportSize()?.height ?? VIEWPORT.height;
|
||||
const nearBottom = box.y + box.height / 2 > viewportHeight - 150;
|
||||
await page.evaluate(
|
||||
(toTop) => document.getElementById('tv-caption')?.classList.toggle('top', toTop),
|
||||
nearBottom
|
||||
);
|
||||
await page.mouse.move(box.x + box.width / 2, box.y + box.height / 2, { steps: 24 });
|
||||
await pause(page, 260);
|
||||
await page.mouse.down();
|
||||
await pause(page, 90);
|
||||
await page.mouse.up();
|
||||
}
|
||||
```
|
||||
|
||||
The typing helper selects a field's contents before it types, character by character at human speed. The select-first rule arrived by way of a video that proudly displayed "36.5012.00" after typing a new amount into a prefilled field. Your users clear fields without thinking about it; your automation won't unless you tell it to.
|
||||
|
||||
```ts
|
||||
/** Tap a field, then type at a human pace — select-all first to overwrite any
|
||||
* prefilled value. */
|
||||
export async function type(page: Page, locator: Locator, text: string) {
|
||||
await tap(page, locator);
|
||||
await page.keyboard.press('ControlOrMeta+a');
|
||||
await page.keyboard.type(text, { delay: 45 });
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## An overlay that isn't part of your app
|
||||
|
||||
The caption bar and the finger are injected DOM: a hundred-odd lines of markup, CSS, and script added to every recorded page at load time, riding on top of the untouched application. This matters more than it might seem. Your app needs zero knowledge that it's being filmed, which means no test-mode conditionals bleeding into production code, and the overlay works identically across every page and, it turned out, across two entirely different applications.
|
||||
|
||||
The finger is a ring that shadows the mouse and pulses on every press; the caption bar is a fixed element the `say()` helper talks to:
|
||||
|
||||
```ts
|
||||
#tv-cursor { position: fixed; z-index: 2147483646; width: 28px; height: 28px;
|
||||
border-radius: 50%; background: rgba(58,107,53,0.30);
|
||||
border: 2px solid rgba(58,107,53,0.85); pointer-events: none;
|
||||
transform: translate(-50%,-50%); left: -100px; top: -100px; }
|
||||
#tv-cursor.tap { animation: tv-tap 0.4s ease-out; }
|
||||
@keyframes tv-tap { 0% { transform: translate(-50%,-50%) scale(1); }
|
||||
40% { transform: translate(-50%,-50%) scale(1.7); }
|
||||
100% { transform: translate(-50%,-50%) scale(1); } }
|
||||
```
|
||||
|
||||
```ts
|
||||
addEventListener('mousemove', (e) => {
|
||||
cursor.style.left = e.clientX + 'px';
|
||||
cursor.style.top = e.clientY + 'px';
|
||||
}, true);
|
||||
addEventListener('mousedown', () => {
|
||||
cursor.classList.remove('tap');
|
||||
void cursor.offsetWidth;
|
||||
cursor.classList.add('tap');
|
||||
}, true);
|
||||
```
|
||||
|
||||
The overlay taught me a fun small lesson on adaptability. The caption bar sits at the bottom of the viewport, which is exactly where mobile interfaces put their primary buttons, and several early recordings tapped buttons hidden behind the caption. Now, when a tap target sits in the bottom portion of the screen, the caption hops to the top for that beat — you can see it in `tap()` above, in the `nearBottom` check. It's four lines of logic, and it's the difference between a video you trust and a video that covers the control it's teaching.
|
||||
|
||||
---
|
||||
|
||||
## Fixtures are writing
|
||||
|
||||
The flows run against the same seeded fixtures your test suite already uses, and sign-in happens through the same stored authentication state, which is why every video opens already signed in instead of spending ten seconds on a login form.
|
||||
|
||||
But video asks more of fixtures than tests do, and this is the part I'd underline for anyone building one of these. A test only needs data that's valid. A video needs data that's believable, because the camera lingers on it. LocallyGrown's "meet your growers" walkthrough is only as good as the fictional farm it features, and my seeded farm turned out to have a one-line bio, an empty photo frame, and a "uses synthetic chemicals" badge on a market that bans them. I spent an evening hour writing great fixtures that show off the system and that farm properly, such as a three-paragraph story and a coherent certification and a photo, and it changed how real every frame feels. Budget time for fixture writing. It's writing. That farm was the first of many; the fixtures kept growing until the demo data told the story of a whole small community.
|
||||
|
||||
Two disciplines go with it. Before recording anything for sharing, verify that every table your camera visits holds only fictional data, and make that a check the system runs rather than a thing you remember, because development databases accumulate real imports over time and my own had one. And seed richly enough to unlock every feature you're teaching, since plenty of interface doesn't render until settings exist: a home-base address for the one-tap mileage button, payment handles for the tap-to-pay buttons. I use a separate test database that gets torn down and rebuilt from scratch every run, but if you use a single database shared between development and testing, this can become a thorny problem.
|
||||
|
||||
One more fixture-adjacent choice: each recording also emits a timecoded narration cue sheet, caption text mapped to timestamps. Mine were meant for a learning exercise in AI voiceover that I ended up not wanting to get into just yet, but they cost nothing to keep producing, and they'd make captions translatable or narratable later without touching a single flow.
|
||||
|
||||
---
|
||||
|
||||
## From recording to serving
|
||||
|
||||
Playwright records webm; the web wants mp4. The generator transcodes with ffmpeg as its last step, and the files are all gitignored, because they're build artifacts. The flow catalog is the source; the videos are what it compiles to today.
|
||||
|
||||
```ts
|
||||
// webm → mp4 (yuv420p for Safari/iOS, +faststart so playback starts before
|
||||
// the file finishes downloading). Best-effort: keep the webm regardless.
|
||||
execFileSync(
|
||||
'ffmpeg',
|
||||
['-y', '-i', webm, '-c:v', 'libx264', '-pix_fmt', 'yuv420p', '-movflags', '+faststart', mp4],
|
||||
{ stdio: 'pipe' }
|
||||
);
|
||||
```
|
||||
|
||||
Serving them had two lessons waiting, both one-line fixes. My ignore entry read `training/`, which also matched `src/routes/training/`, the route that serves the videos, so production returned 404 for every video while my laptop happily played them all. One day I'll have gitignore syntax correctly stored in my brain. The anchored pattern `/training/` fixed it, and the commit title is the honest summary: "Ship the /training/[file] route the gitignore swallowed." Then Cloudflare, which fronts the app, kept serving old copies after regeneration, because the route sends a one-day cache header and re-uploading a file changes nothing an edge cache cares about. Versioned URLs fixed that one: every source renders as `/training/{video.file}?v={VIDEO_VERSION}`, and the version bumps after each upload. If mutable files live behind an edge cache, the URL has to change when the bytes do.
|
||||
|
||||
Regeneration reviews, by the way, don't require watching every video end to end. For a change confined to one screen, I can extract a single frame at the timestamp where that screen is on camera, and that answers the only question that matters.
|
||||
|
||||
---
|
||||
|
||||
## Scaling from one user to three audiences
|
||||
|
||||
The prototype was a single eleven-hundred-line file, phone-only, built in an evening for an app with one user. Porting it to LocallyGrown the next night is what forced real structure: the flow catalog, harness, overlay, and orchestration each became a module, and flows gained a device dimension, because shoppers use phones while market managers use laptops, so most flows record twice at different viewports.
|
||||
|
||||
Desktop mode had a subtle surprise for me. The app needed to know it was rendering at desktop size before first paint, and the obvious approach, setting an attribute on the root element from an init script, silently did nothing. The comment in the harness explains why, better than I could from memory:
|
||||
|
||||
```ts
|
||||
// Let the overlay adapt captions (centered + larger) for desktop recordings.
|
||||
// Set a window global rather than an attribute: init scripts run before the
|
||||
// HTML is parsed, and the parser replaces the initial <html> element, so an
|
||||
// attribute set here is silently discarded. The overlay installer runs at
|
||||
// DOMContentLoaded and stamps data-tv-device from this global.
|
||||
if (desktop) {
|
||||
await page.addInitScript(() => {
|
||||
(window as unknown as { __tvDevice?: string }).__tvDevice = 'desktop';
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
The two-device pipeline also taught me the project's most transferable operational lesson. My first version recorded the phone pass, reseeded the database, and reused the still-running dev server for the desktop pass, and that server held stale module state and stale row IDs. Sometimes the run crashed; sometimes it completed and produced videos of stale data, which is quieter and therefore worse. Each device pass now gets its own full server lifecycle: start, seed, record, stop. And the fix got better on review — I always have AI coding assistants do several review passes over their own work, and it caught that the stop step returned silently when the port never freed, letting a wedged server survive into the next pass. It now escalates to a hard kill and throws if the port is still listening. A cleanup step that always reports success is a specific example of a surprisingly common gotcha: never trust anything that always reports success.
|
||||
|
||||
---
|
||||
|
||||
## A walkthrough I couldn't record
|
||||
|
||||
I'll close with the one that got away, because it's always nice to have a puzzle I can come back to.
|
||||
|
||||
The LocallyGrown walkthrough for adding a photo to a product produced a video in which the photo never appears. The flow ran cleanly, the form saved, and the recording shows a save with no photo, because that's what actually happened.
|
||||
|
||||
Probing it layer by layer: Playwright's `setInputFiles` does exactly what it documents. After the call, the input's `files` property contains the file, and a native change event fires. The application's handler simply never runs. The app is Svelte 5, which delegates common events to a single root listener rather than attaching handlers per element, and that delegation doesn't invoke the component's `onchange` for this synthetic selection. App state never updates, the preview never renders, and the form honestly submits without a photo.
|
||||
|
||||
This is a known class of behavior, not a novel bug: [sveltejs/svelte issue 12147](https://github.com/sveltejs/svelte/issues/12147) documents the same shape, where programmatically dispatched events with native names reach handlers under the legacy `on:change` syntax and stop reaching them under the delegated `onchange` syntax. A real person operating a real file picker generates a trusted, browser-initiated event, which delegation handles normally, so no actual grower has ever hit this. The failure exists only for synthetic events. A recording rig is made of synthetic events.
|
||||
|
||||
Every workaround I considered was a form of lying. Dispatch a hand-rolled event, call the handler directly, patch the state from outside: each produces a video of something the application doesn't do. So the flow is gone from the catalog and the written photo guide remains. The commit message stands as the summary of record: "A recorded demo can't show a real photo upload... Real growers using a real file picker are unaffected — there's just no way to film it truthfully."
|
||||
|
||||
I find I like what this says about the whole approach. The system's entire value is that it refuses to fake anything: real app, real fixtures, real interactions, and a recording of whatever truly happens. The same property that makes the videos trustworthy is the one that made this flow unrecordable, for now. If you build one of these, you'll eventually meet your own version of this walkthrough, and I'd encourage you to let it win.
|
||||
|
||||
---
|
||||
|
||||
## What two evenings bought
|
||||
|
||||
The part I keep marveling at is how little of this system I actually had to build. Playwright already knew how to record video. The e2e suite already knew how to seed a believable market and sign in as anyone. ffmpeg already knew how to make Safari happy. The new code is a thin layer of glue and taste: four helpers, an overlay, and a catalog of flows that read like stage directions. That's why the first version took an evening and the port took another one, and it's a low-risk reason to try this yourself if you have a test suite sitting there: the expensive parts are already on your machine.
|
||||
|
||||
What it bought is out of proportion to that effort. Both applications are better documented than anything I've shipped in twenty-plus years of building software, and more approachable too. A stranger landing on LocallyGrown can watch themselves place an order before they've read a word, and Carol can answer "how do I do that again?" in under a minute, standing in a client's kitchen.
|
||||
|
||||
And my favorite part of the whole project was the least automated one. Writing the fixtures grew into writing a world: farmers bringing what they grow to a common market, neighbors filling baskets, recipes traveling from one kitchen to another, meals made for family and friends. What started as believable data in a database became an aspirational story about a community, and every video is a little window into it. The machine records what it sees, and it fell to me to make what it sees worth looking at. I could happily do more of that.
|
||||
@@ -0,0 +1,3 @@
|
||||
---
|
||||
title: AI
|
||||
---
|
||||
@@ -0,0 +1,3 @@
|
||||
---
|
||||
title: DMARC
|
||||
---
|
||||
@@ -0,0 +1,3 @@
|
||||
---
|
||||
title: LocallyGrown
|
||||
---
|
||||
@@ -0,0 +1,3 @@
|
||||
---
|
||||
title: n8n
|
||||
---
|
||||
@@ -7,6 +7,6 @@ DIR=../../var/www/blog/www # the directory where your web site files should go
|
||||
rm -rf public resources
|
||||
|
||||
# Build site and index, then deploy
|
||||
hugo && pagefind --site public && rsync -avz --no-t --no-p --delete public/ ${HOST}:~/${DIR} # this will delete everything on the server that's not in the local public folder
|
||||
hugo && pagefind --site public && rsync -avz --no-t --no-p --delete --exclude=badges/ public/ ${HOST}:~/${DIR} # this will delete everything on the server that's not in the local public folder
|
||||
|
||||
exit 0
|
||||
@@ -0,0 +1,770 @@
|
||||
{
|
||||
"art_urls": {
|
||||
"104590:117256": {
|
||||
"full": "https://cf.geekdo-images.com/ZI_7riAQtSAb9T3bodOVKA__original/img/S5nr9djtA4cUDvi7dmtTiYhut1Y=/0x0/filters:format(jpeg)/pic6966120.jpg",
|
||||
"thumb": "https://cf.geekdo-images.com/ZI_7riAQtSAb9T3bodOVKA__small/img/VzbEp7j_wYr0KiXfvsYITTAhnSk=/fit-in/200x150/filters:strip_icc()/pic6966120.jpg"
|
||||
},
|
||||
"104710:117685": {
|
||||
"full": "https://cf.geekdo-images.com/yXbWhfIuwKFJfPNg5SgPOQ__original/img/C-3SceTz2k75LaIYt3sQWl7g7lU=/0x0/filters:format(jpeg)/pic1222733.jpg",
|
||||
"thumb": "https://cf.geekdo-images.com/yXbWhfIuwKFJfPNg5SgPOQ__small/img/PFBGm2T4kgP7_ARhb7Zcr5j_uZU=/fit-in/200x150/filters:strip_icc()/pic1222733.jpg"
|
||||
},
|
||||
"116:28201": {
|
||||
"full": "https://cf.geekdo-images.com/gDbDLBdIZfkU9rc8rK4g2g__original/img/Ul4DqoFNac_z1sTA2yhY4O_b5MI=/0x0/filters:format(jpeg)/pic9528692.jpg",
|
||||
"thumb": "https://cf.geekdo-images.com/gDbDLBdIZfkU9rc8rK4g2g__small/img/xiIZBdWcTWTT4EBd1ZFwaTcl1V0=/fit-in/200x150/filters:strip_icc()/pic9528692.jpg"
|
||||
},
|
||||
"1197:78624": {
|
||||
"full": "https://cf.geekdo-images.com/3dIGGmZpEj8ND0WwBWGtDw__original/img/6EifG7D8YEKnT65VGKfbyc6sSss=/0x0/filters:format(png)/pic8946171.png",
|
||||
"thumb": "https://cf.geekdo-images.com/3dIGGmZpEj8ND0WwBWGtDw__small/img/gLyiJhJBqsqiwMwRSlleuFC92L0=/fit-in/200x150/filters:strip_icc()/pic8946171.png"
|
||||
},
|
||||
"1219:488621": {
|
||||
"full": "https://cf.geekdo-images.com/qpL225YNN5iKRPotB7Q7MA__original/img/5ZGo501PquASeGxpQLBIglcVSYs=/0x0/filters:format(jpeg)/pic6173371.jpg",
|
||||
"thumb": "https://cf.geekdo-images.com/qpL225YNN5iKRPotB7Q7MA__small/img/JZR5MVC8xYrboBrYMsZmWtjJTu0=/fit-in/200x150/filters:strip_icc()/pic6173371.jpg"
|
||||
},
|
||||
"124548:165214": {
|
||||
"full": "https://cf.geekdo-images.com/SRNm7BPGq1L0aFo4aEcBhA__original/img/5ZHqB34M-wHKGNMJF20U5waS-EA=/0x0/filters:format(jpeg)/pic2454377.jpg",
|
||||
"thumb": "https://cf.geekdo-images.com/SRNm7BPGq1L0aFo4aEcBhA__small/img/F8dAQlcGebZuPnA9TK4VN1X5F-w=/fit-in/200x150/filters:strip_icc()/pic2454377.jpg"
|
||||
},
|
||||
"125472:629302": {
|
||||
"full": "https://cf.geekdo-images.com/pzsWiHixQ3QLEq364gcn8A__original/img/uXfTJe7bZAF3h8Hz8t31Mc6I850=/0x0/filters:format(jpeg)/pic1328448.jpg",
|
||||
"thumb": "https://cf.geekdo-images.com/pzsWiHixQ3QLEq364gcn8A__small/img/YUEf0HzIH7633QCUjqnRGJHIf1g=/fit-in/200x150/filters:strip_icc()/pic1328448.jpg"
|
||||
},
|
||||
"125675:167657": {
|
||||
"full": "https://cf.geekdo-images.com/abNcykgcvAIvyZpmZY9Lew__original/img/gt-hLDZIqUhCNJDfVyBqbcHb7m0=/0x0/filters:format(jpeg)/pic1327331.jpg",
|
||||
"thumb": "https://cf.geekdo-images.com/abNcykgcvAIvyZpmZY9Lew__small/img/l91104qfHunxC48wvhlD6zDqsgM=/fit-in/200x150/filters:strip_icc()/pic1327331.jpg"
|
||||
},
|
||||
"125921:476263": {
|
||||
"full": "https://cf.geekdo-images.com/t2L_dlodn04yyS7CmwnREQ__original/img/HmP2e4IuZWEWpG56C08zYnvBFvU=/0x0/filters:format(jpeg)/pic4741220.jpg",
|
||||
"thumb": "https://cf.geekdo-images.com/t2L_dlodn04yyS7CmwnREQ__small/img/DtXpiQdqTHdmWmYRMm9RTVmEEis=/fit-in/200x150/filters:strip_icc()/pic4741220.jpg"
|
||||
},
|
||||
"1260:156689": {
|
||||
"full": "https://cf.geekdo-images.com/lKJgntNaJAa8A7QStjRvzA__original/img/DB0f2rKbHv0aLUQpVQyBIsEsz_E=/0x0/filters:format(jpeg)/pic394433.jpg",
|
||||
"thumb": "https://cf.geekdo-images.com/lKJgntNaJAa8A7QStjRvzA__small/img/e6VbhPlXJgY86AMG-NsuS6zC-fw=/fit-in/200x150/filters:strip_icc()/pic394433.jpg"
|
||||
},
|
||||
"1269:29705": {
|
||||
"full": "https://cf.geekdo-images.com/aaUJua2zRXA6lqbVbNcBGg__original/img/dWxG7nwxGBijXQ1849TFwtym06I=/0x0/filters:format(jpeg)/pic8204244.jpg",
|
||||
"thumb": "https://cf.geekdo-images.com/aaUJua2zRXA6lqbVbNcBGg__small/img/81Ix-m3uliKZ43ouEhnOzUivMj4=/fit-in/200x150/filters:strip_icc()/pic8204244.jpg"
|
||||
},
|
||||
"128554:207529": {
|
||||
"full": "https://cf.geekdo-images.com/0Sa8hpkdz947r104XjGijw__original/img/R5k-Fa4h1heKyv-q6InDW0nDio8=/0x0/filters:format(jpeg)/pic1591249.jpg",
|
||||
"thumb": "https://cf.geekdo-images.com/0Sa8hpkdz947r104XjGijw__small/img/8MrEnv-5IvQixU7aVIMmt2Y--Z4=/fit-in/200x150/filters:strip_icc()/pic1591249.jpg"
|
||||
},
|
||||
"1339:34390": {
|
||||
"full": "https://cf.geekdo-images.com/fvhkcw4s6Q4-nMwwok1J6A__original/img/WJFmTRWQRzOInBnWAjH39F3RyMg=/0x0/filters:format(png)/pic6569894.png",
|
||||
"thumb": "https://cf.geekdo-images.com/fvhkcw4s6Q4-nMwwok1J6A__small/img/zXW1s2ZXq1dZkvbAzYdr9o1xRNQ=/fit-in/200x150/filters:strip_icc()/pic6569894.png"
|
||||
},
|
||||
"137909:216474": {
|
||||
"full": "https://cf.geekdo-images.com/RyeZQU9LkyEUtEtlwjSM9w__original/img/u0lfUWc7-bREM7t_6YmX7i_ViUY=/0x0/filters:format(jpeg)/pic3891987.jpg",
|
||||
"thumb": "https://cf.geekdo-images.com/RyeZQU9LkyEUtEtlwjSM9w__small/img/VLWYy1d6eq09iA_wopAJy-8LKcM=/fit-in/200x150/filters:strip_icc()/pic3891987.jpg"
|
||||
},
|
||||
"139443:202519": {
|
||||
"full": "https://cf.geekdo-images.com/4ih18dd_9SZv3yn-YazMIw__original/img/VOTdHizWmvwJnOFMw6YMWj5sJSg=/0x0/filters:format(jpeg)/pic2933903.jpg",
|
||||
"thumb": "https://cf.geekdo-images.com/4ih18dd_9SZv3yn-YazMIw__small/img/i2BNig3lkYu3MZPhzKRIygg7RJU=/fit-in/200x150/filters:strip_icc()/pic2933903.jpg"
|
||||
},
|
||||
"13:347121": {
|
||||
"full": "https://cf.geekdo-images.com/0XODRpReiZBFUffEcqT5-Q__original/img/oRc0AomWA9ZtFqQDZiZbIyKE1j0=/0x0/filters:format(png)/pic9156909.png",
|
||||
"thumb": "https://cf.geekdo-images.com/0XODRpReiZBFUffEcqT5-Q__small/img/SNVfF23OQafv3u8xdFolJnMkBoM=/fit-in/200x150/filters:strip_icc()/pic9156909.png"
|
||||
},
|
||||
"142402:657689": {
|
||||
"full": "https://cf.geekdo-images.com/2OuiXWuDhdQmOjkRGjYnmQ__original/img/qclrtAPBsxakggyswrJikcBHSKk=/0x0/filters:format(jpeg)/pic1746008.jpg",
|
||||
"thumb": "https://cf.geekdo-images.com/2OuiXWuDhdQmOjkRGjYnmQ__small/img/uqJquslTCu6xwrlhrS4tjNpkT6w=/fit-in/200x150/filters:strip_icc()/pic1746008.jpg"
|
||||
},
|
||||
"143884:453051": {
|
||||
"full": "https://cf.geekdo-images.com/_lI2eUDHfesfe5SswJiFAg__original/img/H4XHVffQ4SOxgWoG396I9LUrwg8=/0x0/filters:format(png)/pic4783831.png",
|
||||
"thumb": "https://cf.geekdo-images.com/_lI2eUDHfesfe5SswJiFAg__small/img/tCTP0XMMmsAfNqzb0iYmp0b-YUc=/fit-in/200x150/filters:strip_icc()/pic4783831.png"
|
||||
},
|
||||
"14535:29075": {
|
||||
"full": "https://cf.geekdo-images.com/H18uaF6-yKcuLKZp5O-U6w__original/img/3pLncRodYrEqcEJ-YwYIV__YCCo=/0x0/filters:format(jpeg)/pic58989.jpg",
|
||||
"thumb": "https://cf.geekdo-images.com/H18uaF6-yKcuLKZp5O-U6w__small/img/wmgZxGMwb44sb_Hg3JBeUdHrHvk=/fit-in/200x150/filters:strip_icc()/pic58989.jpg"
|
||||
},
|
||||
"147370:231664": {
|
||||
"full": "https://cf.geekdo-images.com/SRJcEXLEBnj5WxcSmW22hA__original/img/Dzz2RP84t8CLjMaLoLbGB5tbzFc=/0x0/filters:format(jpeg)/pic2047441.jpg",
|
||||
"thumb": "https://cf.geekdo-images.com/SRJcEXLEBnj5WxcSmW22hA__small/img/cpax1CjpG61IOX1zhXrKuW9Z7yU=/fit-in/200x150/filters:strip_icc()/pic2047441.jpg"
|
||||
},
|
||||
"150925:225659": {
|
||||
"full": "https://cf.geekdo-images.com/F_TURkqGxlE_tT0DKMgTSQ__original/img/Wts6qCYZ32-K7fxGyhDsg1Jjd74=/0x0/filters:format(jpeg)/pic1841265.jpg",
|
||||
"thumb": "https://cf.geekdo-images.com/F_TURkqGxlE_tT0DKMgTSQ__small/img/VRRTdrDjmcFOc_cNDR1b5RAUxjk=/fit-in/200x150/filters:strip_icc()/pic1841265.jpg"
|
||||
},
|
||||
"153999:280845": {
|
||||
"full": "https://cf.geekdo-images.com/Ft08woV-KG3HHfO-ET6S4Q__original/img/x7YkL7sqFAt2LAxgPx3MEv3ZMNI=/0x0/filters:format(jpeg)/pic2717772.jpg",
|
||||
"thumb": "https://cf.geekdo-images.com/Ft08woV-KG3HHfO-ET6S4Q__small/img/8NF5KOA3oPkiJkqtOrfi0anm5BI=/fit-in/200x150/filters:strip_icc()/pic2717772.jpg"
|
||||
},
|
||||
"156994:236280": {
|
||||
"full": "https://cf.geekdo-images.com/nPwffbp8-RD4SWwwgwGIfg__original/img/WMxqMqdSTRVpuUAjjUVOIOLzBx4=/0x0/filters:format(jpeg)/pic9686810.jpg",
|
||||
"thumb": "https://cf.geekdo-images.com/nPwffbp8-RD4SWwwgwGIfg__small/img/aPiWIz7W1yg4ZWuMVjn7X4yzJpE=/fit-in/200x150/filters:strip_icc()/pic9686810.jpg"
|
||||
},
|
||||
"157969:237864": {
|
||||
"full": "https://cf.geekdo-images.com/BBgLFKUzr6tcKtlIM2JSFw__original/img/fll2MuTBPVKb7fyBJ8D-4_c1XHM=/0x0/filters:format(jpeg)/pic2075830.jpg",
|
||||
"thumb": "https://cf.geekdo-images.com/BBgLFKUzr6tcKtlIM2JSFw__small/img/JD5L_XrtkaKINUj8ILZqFMGcTsE=/fit-in/200x150/filters:strip_icc()/pic2075830.jpg"
|
||||
},
|
||||
"160477:269392": {
|
||||
"full": "https://cf.geekdo-images.com/GJKuOCy5HGAYgVH_Rei5mA__original/img/oRsIxp72iv58128KFdqvjG6Gz64=/0x0/filters:format(jpeg)/pic7070194.jpg",
|
||||
"thumb": "https://cf.geekdo-images.com/GJKuOCy5HGAYgVH_Rei5mA__small/img/Q3W4poMS4btD-HZLervgqbSCtmE=/fit-in/200x150/filters:strip_icc()/pic7070194.jpg"
|
||||
},
|
||||
"161928:245124": {
|
||||
"full": "https://cf.geekdo-images.com/_cEji3j4Ugxo14Oi1Fwvnw__original/img/gimsO3oRax9nnACCgkf9Yq6-jtE=/0x0/filters:format(jpeg)/pic7167060.jpg",
|
||||
"thumb": "https://cf.geekdo-images.com/_cEji3j4Ugxo14Oi1Fwvnw__small/img/Y1xt0osums_2WHXGYevD1uZp8s8=/fit-in/200x150/filters:strip_icc()/pic7167060.jpg"
|
||||
},
|
||||
"163412:495998": {
|
||||
"full": "https://cf.geekdo-images.com/xNSaIHCKr_cc7Q2rQSSJPQ__original/img/HJJdVosBV35kj8pQ6wmQMiJRhVA=/0x0/filters:format(jpeg)/pic9273518.jpg",
|
||||
"thumb": "https://cf.geekdo-images.com/xNSaIHCKr_cc7Q2rQSSJPQ__small/img/9Kf3YKiVIJxd_EbqJEJFdlGfj7I=/fit-in/200x150/filters:strip_icc()/pic9273518.jpg"
|
||||
},
|
||||
"164:28465": {
|
||||
"full": "https://cf.geekdo-images.com/CMYnGVcqpdlN8D8Fi64Dpg__original/img/MkRBpDEL4Hw-81uG-IvH9ZHLsk0=/0x0/filters:format(jpeg)/pic3051016.jpg",
|
||||
"thumb": "https://cf.geekdo-images.com/CMYnGVcqpdlN8D8Fi64Dpg__small/img/7rrSNbVpY5TOPBax-G6Pj4DesDA=/fit-in/200x150/filters:strip_icc()/pic3051016.jpg"
|
||||
},
|
||||
"165986:277137": {
|
||||
"full": "https://cf.geekdo-images.com/Un-yHPPUaH7IpTfiG7ffkw__original/img/rOD_UORh5dzMisuJFBP7548XbkU=/0x0/filters:format(jpeg)/pic3021582.jpg",
|
||||
"thumb": "https://cf.geekdo-images.com/Un-yHPPUaH7IpTfiG7ffkw__small/img/h9a43sj1YW-dZCKU9wKvHvFSy5U=/fit-in/200x150/filters:strip_icc()/pic3021582.jpg"
|
||||
},
|
||||
"166109:253058": {
|
||||
"full": "https://cf.geekdo-images.com/mVC1JCvb488GJFhNF1idsA__original/img/G1UroUSm25FSY7LQXpPu6f6--20=/0x0/filters:format(png)/pic2256875.png",
|
||||
"thumb": "https://cf.geekdo-images.com/mVC1JCvb488GJFhNF1idsA__small/img/Bx9UJsNZV8eF7kebFJlFoCqvtUw=/fit-in/200x150/filters:strip_icc()/pic2256875.png"
|
||||
},
|
||||
"166976:410382": {
|
||||
"full": "https://cf.geekdo-images.com/HxW4curp8PnTfAZpqMuEDQ__original/img/Qd6WvEQhwV3ST8sFgJjP_E6cgxg=/0x0/filters:format(jpeg)/pic4172245.jpg",
|
||||
"thumb": "https://cf.geekdo-images.com/HxW4curp8PnTfAZpqMuEDQ__small/img/WznGAoMLcG3BGUEmP37vktZ2G9A=/fit-in/200x150/filters:strip_icc()/pic4172245.jpg"
|
||||
},
|
||||
"168857:657857": {
|
||||
"full": "https://cf.geekdo-images.com/HEc3AoMbW2giNA2NjTpRpg__original/img/5N7gCQ1Nt9YeQL3XzkEqYdnE4zc=/0x0/filters:format(png)/pic2423335.png",
|
||||
"thumb": "https://cf.geekdo-images.com/HEc3AoMbW2giNA2NjTpRpg__small/img/KEZVtyubwWZOl-HDUnxW1MuNIs4=/fit-in/200x150/filters:strip_icc()/pic2423335.png"
|
||||
},
|
||||
"169124:451577": {
|
||||
"full": "https://cf.geekdo-images.com/uTrqB91Kzvqtb4Ophk1BPw__original/img/8A3BjcqOLKDl_f3yuT6Gfb392js=/0x0/filters:format(jpeg)/pic5095159.jpg",
|
||||
"thumb": "https://cf.geekdo-images.com/uTrqB91Kzvqtb4Ophk1BPw__small/img/uYpJnYTjRgevqAtujITGFBdHNU4=/fit-in/200x150/filters:strip_icc()/pic5095159.jpg"
|
||||
},
|
||||
"169784:260454": {
|
||||
"full": "https://cf.geekdo-images.com/ne5GT5tNupCbGeN4maV0aw__original/img/Pe0unjKPwaO0cJPd9QI_Eo-xuRU=/0x0/filters:format(jpeg)/pic6967876.jpg",
|
||||
"thumb": "https://cf.geekdo-images.com/ne5GT5tNupCbGeN4maV0aw__small/img/RTQtsNpxLidMnDPDGTTf-AiP9OQ=/fit-in/200x150/filters:strip_icc()/pic6967876.jpg"
|
||||
},
|
||||
"172225:296012": {
|
||||
"full": "https://cf.geekdo-images.com/N8bL53-pRU7zaXDTrEaYrw__original/img/0ciN1VZYifUd0qIDO0e8cGXmiss=/0x0/filters:format(png)/pic2691976.png",
|
||||
"thumb": "https://cf.geekdo-images.com/N8bL53-pRU7zaXDTrEaYrw__small/img/3tH4pIc1Udzkd0tXc6MgVQ59BC0=/fit-in/200x150/filters:strip_icc()/pic2691976.png"
|
||||
},
|
||||
"172503:265107": {
|
||||
"full": "https://cf.geekdo-images.com/BXqdHw39CPenDAKWDm_Vxw__original/img/pzO7kVTqFPx5mGYQXPxC39x4kL8=/0x0/filters:format(png)/pic2555236.png",
|
||||
"thumb": "https://cf.geekdo-images.com/BXqdHw39CPenDAKWDm_Vxw__small/img/RgBmCUFY-rCCHNC3uWAE3u8lTmA=/fit-in/200x150/filters:strip_icc()/pic2555236.png"
|
||||
},
|
||||
"173057:266044": {
|
||||
"full": "https://cf.geekdo-images.com/jfXGKha8QeY20sVRNCb3eQ__original/img/3tC9fwYOv0erI0w1KsKctWf4pR0=/0x0/filters:format(png)/pic9579726.png",
|
||||
"thumb": "https://cf.geekdo-images.com/jfXGKha8QeY20sVRNCb3eQ__small/img/r9bZLsBX19nqNIorrtuWbH4u0h4=/fit-in/200x150/filters:strip_icc()/pic9579726.png"
|
||||
},
|
||||
"173634:266954": {
|
||||
"full": "https://cf.geekdo-images.com/qUHk5BNYauq6qjmv7RLVoQ__original/img/b5I_w_KHanNze4ewkicl2GfZGl8=/0x0/filters:format(png)/pic2614687.png",
|
||||
"thumb": "https://cf.geekdo-images.com/qUHk5BNYauq6qjmv7RLVoQ__small/img/e4pRbDphYf-sekPljHLEuLAdki4=/fit-in/200x150/filters:strip_icc()/pic2614687.png"
|
||||
},
|
||||
"174430:520054": {
|
||||
"full": "https://cf.geekdo-images.com/sZYp_3BTDGjh2unaZfZmuA__original/img/7d-lj5Gd1e8PFnD97LYFah2c45M=/0x0/filters:format(jpeg)/pic2437871.jpg",
|
||||
"thumb": "https://cf.geekdo-images.com/sZYp_3BTDGjh2unaZfZmuA__small/img/veqFeP4d_3zNhFc3GNBkV95rBEQ=/fit-in/200x150/filters:strip_icc()/pic2437871.jpg"
|
||||
},
|
||||
"174922:657858": {
|
||||
"full": "https://cf.geekdo-images.com/NqR4UvZn9wnnjii1NENRvA__original/img/1mUVCMHS-yZQvQMH7DfNkz6Nceo=/0x0/filters:format(png)/pic2618519.png",
|
||||
"thumb": "https://cf.geekdo-images.com/NqR4UvZn9wnnjii1NENRvA__small/img/KImlr0ENQE5Fl0-aWE9O-thUBxU=/fit-in/200x150/filters:strip_icc()/pic2618519.png"
|
||||
},
|
||||
"176:27673": {
|
||||
"full": "https://cf.geekdo-images.com/OlPw-qCHEgl-83A8rWf9IA__original/img/4ulSIYQhzgyhzzxlg6tKjis70kY=/0x0/filters:format(jpeg)/pic984692.jpg",
|
||||
"thumb": "https://cf.geekdo-images.com/OlPw-qCHEgl-83A8rWf9IA__small/img/uQwxOsY1lZ2D5T2OTQ0Q5t52MZo=/fit-in/200x150/filters:strip_icc()/pic984692.jpg"
|
||||
},
|
||||
"177:24972": {
|
||||
"full": "https://cf.geekdo-images.com/9ROB7NpxefRUmpR9UroQ8w__original/img/--qlhvNNj8zF9cWLlihdTg0jzmE=/0x0/filters:format(jpeg)/pic87459.jpg",
|
||||
"thumb": "https://cf.geekdo-images.com/9ROB7NpxefRUmpR9UroQ8w__small/img/XDH667tVl2KjQ6szNjT7ceEmh5E=/fit-in/200x150/filters:strip_icc()/pic87459.jpg"
|
||||
},
|
||||
"1784:110926": {
|
||||
"full": "https://cf.geekdo-images.com/1Yiabh6DNZcIn_1WJ_DnjQ__original/img/5plSWWkOlUtYe1nsD8MaJXQTmGc=/0x0/filters:format(jpeg)/pic4978.jpg",
|
||||
"thumb": "https://cf.geekdo-images.com/1Yiabh6DNZcIn_1WJ_DnjQ__small/img/KDoWL5IkgGLQhm9bM__20pBzwmE=/fit-in/200x150/filters:strip_icc()/pic4978.jpg"
|
||||
},
|
||||
"181304:536289": {
|
||||
"full": "https://cf.geekdo-images.com/1nQ3ZKudtDeAP7IiKE-kNg__original/img/6igmXolQesapNU9yEnexip6m9B8=/0x0/filters:format(jpeg)/pic8625343.jpg",
|
||||
"thumb": "https://cf.geekdo-images.com/1nQ3ZKudtDeAP7IiKE-kNg__small/img/sW38512N2r1ZuuPE2TUD2LNfoxM=/fit-in/200x150/filters:strip_icc()/pic8625343.jpg"
|
||||
},
|
||||
"18755:284056": {
|
||||
"full": "https://cf.geekdo-images.com/e-BzuUA1I2GNrVcNvYdSWg__original/img/c28GucWuSkcq5SUdjlQwY6JiSeY=/0x0/filters:format(jpeg)/pic2654066.jpg",
|
||||
"thumb": "https://cf.geekdo-images.com/e-BzuUA1I2GNrVcNvYdSWg__small/img/cX0JYq0WVxWNuPcs7I2p6eVYCUY=/fit-in/200x150/filters:strip_icc()/pic2654066.jpg"
|
||||
},
|
||||
"188614:779792": {
|
||||
"full": "https://cf.geekdo-images.com/1DW2I_hYs6l5BHuQPGj9dw__original/img/Lg0wZJUxF071jdWcRqOFno2FHgo=/0x0/filters:format(jpeg)/pic8089317.jpg",
|
||||
"thumb": "https://cf.geekdo-images.com/1DW2I_hYs6l5BHuQPGj9dw__small/img/Go_iakh-eYWSEIhz7Gkjmod2fE8=/fit-in/200x150/filters:strip_icc()/pic8089317.jpg"
|
||||
},
|
||||
"1927:28144": {
|
||||
"full": "https://cf.geekdo-images.com/J-ts3MW0UhDzs621TR6cog__original/img/FbqPPCilgZKND2xmhWJgkfjZiYE=/0x0/filters:format(jpeg)/pic1871016.jpg",
|
||||
"thumb": "https://cf.geekdo-images.com/J-ts3MW0UhDzs621TR6cog__small/img/8hVkpMC5pDLr6ARI_4gI4N3aF5M=/fit-in/200x150/filters:strip_icc()/pic1871016.jpg"
|
||||
},
|
||||
"193485:300446": {
|
||||
"full": "https://cf.geekdo-images.com/-REkdnanVzP-C2wV396cxQ__original/img/PSYRA1XV_dhwX8bS33g8OZGHYf0=/0x0/filters:format(png)/pic3069274.png",
|
||||
"thumb": "https://cf.geekdo-images.com/-REkdnanVzP-C2wV396cxQ__small/img/eabj_Z1NZllzWROj0xKKlcuTD7k=/fit-in/200x150/filters:strip_icc()/pic3069274.png"
|
||||
},
|
||||
"193621:300682": {
|
||||
"full": "https://cf.geekdo-images.com/ohrUtQ0FGHPSFxfSX2Ltmg__original/img/ksIb5Fgrn9Tka1FGVnL8iMlGAjA=/0x0/filters:format(png)/pic6411897.png",
|
||||
"thumb": "https://cf.geekdo-images.com/ohrUtQ0FGHPSFxfSX2Ltmg__small/img/r9PhVsbOjPDDLfVKBhwrPnqV4LI=/fit-in/200x150/filters:strip_icc()/pic6411897.png"
|
||||
},
|
||||
"194655:668826": {
|
||||
"full": "https://cf.geekdo-images.com/665Ply7Ho1WVf1v1iZlWeg__original/img/4q6RCxd7st-yW2MyrJwVC_d3ajk=/0x0/filters:format(png)/pic3283110.png",
|
||||
"thumb": "https://cf.geekdo-images.com/665Ply7Ho1WVf1v1iZlWeg__small/img/BKnSb6kAhpVFHV8cyUXrs3EF_dw=/fit-in/200x150/filters:strip_icc()/pic3283110.png"
|
||||
},
|
||||
"195314:525268": {
|
||||
"full": "https://cf.geekdo-images.com/0HHqYFhJvOwk8Z0Dt-EvTA__original/img/PUZECnFJTnvhRL48lpUzR5TR48w=/0x0/filters:format(jpeg)/pic7083274.jpg",
|
||||
"thumb": "https://cf.geekdo-images.com/0HHqYFhJvOwk8Z0Dt-EvTA__small/img/EwiBLR6QywbISk0aGe-Vya4zXws=/fit-in/200x150/filters:strip_icc()/pic7083274.jpg"
|
||||
},
|
||||
"196105:304900": {
|
||||
"full": "https://cf.geekdo-images.com/CGCYY9KMI4ohm5fYQH1n_g__original/img/1iIrbGykzNTIAsvNkCLxt9e-km4=/0x0/filters:format(png)/pic2944475.png",
|
||||
"thumb": "https://cf.geekdo-images.com/CGCYY9KMI4ohm5fYQH1n_g__small/img/V_azlMIVuWUW7vgGE1eMYULSzzY=/fit-in/200x150/filters:strip_icc()/pic2944475.png"
|
||||
},
|
||||
"201248:313478": {
|
||||
"full": "https://cf.geekdo-images.com/1rvefz1PSj1c3wC1UIGgnQ__original/img/oDJn48M5XslwbPb_Vdh5GWHYxGQ=/0x0/filters:format(jpeg)/pic3042047.jpg",
|
||||
"thumb": "https://cf.geekdo-images.com/1rvefz1PSj1c3wC1UIGgnQ__small/img/hEcG7RPDUc5M7VsZcOqUMJQaNhM=/fit-in/200x150/filters:strip_icc()/pic3042047.jpg"
|
||||
},
|
||||
"202982:316090": {
|
||||
"full": "https://cf.geekdo-images.com/-QoI6kPW2ElFu_3fFZtYgA__original/img/l_gjzYBIgPga5D8pga5Tb1K_rRQ=/0x0/filters:format(jpeg)/pic3717559.jpg",
|
||||
"thumb": "https://cf.geekdo-images.com/-QoI6kPW2ElFu_3fFZtYgA__small/img/rxL2Dpj7WlfQ2tMHrechY03n8pY=/fit-in/200x150/filters:strip_icc()/pic3717559.jpg"
|
||||
},
|
||||
"2058:28041": {
|
||||
"full": "https://cf.geekdo-images.com/dZTkm-3BNL2bJdwRdCskFg__original/img/TZY-wEU7ZCgv_L-tBzIkOB12D88=/0x0/filters:format(jpeg)/pic668686.jpg",
|
||||
"thumb": "https://cf.geekdo-images.com/dZTkm-3BNL2bJdwRdCskFg__small/img/OuJ1ek2SBxYhDLnx0mohC58cBvg=/fit-in/200x150/filters:strip_icc()/pic668686.jpg"
|
||||
},
|
||||
"207830:344075": {
|
||||
"full": "https://cf.geekdo-images.com/uR7RqGbua7h_Z74fmz_-ng__original/img/WXzrC9-ob09Qbxt9jh5_dLLjFGE=/0x0/filters:format(jpeg)/pic9630053.jpg",
|
||||
"thumb": "https://cf.geekdo-images.com/uR7RqGbua7h_Z74fmz_-ng__small/img/AFRWc9kb0CH17kGzrJEghKNlOnQ=/fit-in/200x150/filters:strip_icc()/pic9630053.jpg"
|
||||
},
|
||||
"208428:325455": {
|
||||
"full": "https://cf.geekdo-images.com/b_Jftm8zKB2ItfIdDZSR-g__original/img/XPkfFiWXkGvqvpCqgbGhAt30tyM=/0x0/filters:format(jpeg)/pic3174067.jpg",
|
||||
"thumb": "https://cf.geekdo-images.com/b_Jftm8zKB2ItfIdDZSR-g__small/img/ECPrjPcNpwBZELxzwxX1usQe9f4=/fit-in/200x150/filters:strip_icc()/pic3174067.jpg"
|
||||
},
|
||||
"20866:40025": {
|
||||
"full": "https://cf.geekdo-images.com/dI9Vddu66CxKzOUm4VW4yA__original/img/F572I-iNI1DfMSXReF2ZZ3QMixA=/0x0/filters:format(jpeg)/pic164567.jpg",
|
||||
"thumb": "https://cf.geekdo-images.com/dI9Vddu66CxKzOUm4VW4yA__small/img/6jQKMrDvProjgrGGrb3yV4Tkhh8=/fit-in/200x150/filters:strip_icc()/pic164567.jpg"
|
||||
},
|
||||
"212662:367695": {
|
||||
"full": "https://cf.geekdo-images.com/tQ30J_b7ZS83TtBwTgdFmw__original/img/8ols8_laTY7dhZbuo05Mgbn8rMA=/0x0/filters:format(jpeg)/pic3653424.jpg",
|
||||
"thumb": "https://cf.geekdo-images.com/tQ30J_b7ZS83TtBwTgdFmw__small/img/HWGDSyuzXa0o4pAAYtNAAIEpp1w=/fit-in/200x150/filters:strip_icc()/pic3653424.jpg"
|
||||
},
|
||||
"2197:42495": {
|
||||
"full": "https://cf.geekdo-images.com/SziKU_czapNhgznSS2J0Jg__original/img/BOwgrRde9S08t6GkITYnPozx1gQ=/0x0/filters:format(jpeg)/pic94595.jpg",
|
||||
"thumb": "https://cf.geekdo-images.com/SziKU_czapNhgznSS2J0Jg__small/img/qYKWfnTE8KoIsFB6GZjFQ8LD55o=/fit-in/200x150/filters:strip_icc()/pic94595.jpg"
|
||||
},
|
||||
"220199:369805": {
|
||||
"full": "https://cf.geekdo-images.com/BWKD42FXZxyY3iGg0MdEhQ__original/img/eNk-qRaZg3d5RkrKiZcwZMu9s48=/0x0/filters:format(jpeg)/pic3619180.jpg",
|
||||
"thumb": "https://cf.geekdo-images.com/BWKD42FXZxyY3iGg0MdEhQ__small/img/_-qsvWQbJKc8SFFVIYURqyQEjlE=/fit-in/200x150/filters:strip_icc()/pic3619180.jpg"
|
||||
},
|
||||
"224:20727": {
|
||||
"full": "https://cf.geekdo-images.com/2pr-83ghWQUlwXSLuSTCtw__original/img/_MRG_pnmZQwScJWus84gwaXNflE=/0x0/filters:format(jpeg)/pic287451.jpg",
|
||||
"thumb": "https://cf.geekdo-images.com/2pr-83ghWQUlwXSLuSTCtw__small/img/4XPSkcdNf6CXGWsDGSRXrP7VUBc=/fit-in/200x150/filters:strip_icc()/pic287451.jpg"
|
||||
},
|
||||
"226065:353389": {
|
||||
"full": "https://cf.geekdo-images.com/hEaB_93DtpQE3E2rE9oDjw__original/img/zvja_LKbnIkodC6Sc0d5mFQ8__Y=/0x0/filters:format(png)/pic3689950.png",
|
||||
"thumb": "https://cf.geekdo-images.com/hEaB_93DtpQE3E2rE9oDjw__small/img/hR4IF-t_4cHyJgSZPzVEd8kHDiw=/fit-in/200x150/filters:strip_icc()/pic3689950.png"
|
||||
},
|
||||
"231302:361880": {
|
||||
"full": "https://cf.geekdo-images.com/A07HOEIY81M961ejepCidA__original/img/LcynDDk9pp1zzzj2CsLybJGeZws=/0x0/filters:format(jpeg)/pic3641655.jpg",
|
||||
"thumb": "https://cf.geekdo-images.com/A07HOEIY81M961ejepCidA__small/img/kW_C_eVz7fURxeIzS9ifEKMwZ4M=/fit-in/200x150/filters:strip_icc()/pic3641655.jpg"
|
||||
},
|
||||
"2387:91428": {
|
||||
"full": "https://cf.geekdo-images.com/fPep3Ze_haawiA6wu979YQ__original/img/QlHSWYyF2JAXiPitbjELs_m4kkQ=/0x0/filters:format(jpeg)/pic353210.jpg",
|
||||
"thumb": "https://cf.geekdo-images.com/fPep3Ze_haawiA6wu979YQ__small/img/ACFXxWqeRMZ-lD5B1TuH28ZR9B8=/fit-in/200x150/filters:strip_icc()/pic353210.jpg"
|
||||
},
|
||||
"240:24621": {
|
||||
"full": "https://cf.geekdo-images.com/-M-1zs8oMttiUS2xd8qK4A__original/img/Hil6BWCATeo_bcRqRMHGhAsx_mw=/0x0/filters:format(jpeg)/pic106711.jpg",
|
||||
"thumb": "https://cf.geekdo-images.com/-M-1zs8oMttiUS2xd8qK4A__small/img/qjfnfPFYpTCgmvV9Yon2BDdy88Q=/fit-in/200x150/filters:strip_icc()/pic106711.jpg"
|
||||
},
|
||||
"24310:374258": {
|
||||
"full": "https://cf.geekdo-images.com/RCXedcfgn5LSfH4-kOUvLA__original/img/Fr16k-_UzhrIB6_yre7SrMhkDxU=/0x0/filters:format(jpeg)/pic291048.jpg",
|
||||
"thumb": "https://cf.geekdo-images.com/RCXedcfgn5LSfH4-kOUvLA__small/img/fw7C7ruhaqXTqrWZFHCXdQpNKRs=/fit-in/200x150/filters:strip_icc()/pic291048.jpg"
|
||||
},
|
||||
"244258:421810": {
|
||||
"full": "https://cf.geekdo-images.com/KhSqEP_vIBwhOBH30VFCNA__original/img/A9mr-8g1BsActvH8LLEADL-0xaY=/0x0/filters:format(png)/pic4146595.png",
|
||||
"thumb": "https://cf.geekdo-images.com/KhSqEP_vIBwhOBH30VFCNA__small/img/dpfl247FtV71mrgdg5H_FbuCRBo=/fit-in/200x150/filters:strip_icc()/pic4146595.png"
|
||||
},
|
||||
"2452:288640": {
|
||||
"full": "https://cf.geekdo-images.com/YH5C06snaoJtDqKjwMROPw__original/img/gB1mfMGbSkXgYxU5FA4QWForCZk=/0x0/filters:format(jpeg)/pic5140451.jpg",
|
||||
"thumb": "https://cf.geekdo-images.com/YH5C06snaoJtDqKjwMROPw__small/img/cSHIiN_mZQEqKMI1G7qRbTMp4nk=/fit-in/200x150/filters:strip_icc()/pic5140451.jpg"
|
||||
},
|
||||
"252153:404702": {
|
||||
"full": "https://cf.geekdo-images.com/FmElPbRBjK5RmrAC9so5Gw__original/img/oZvUwcb2HsJHq8RfZ9UucPrfC-8=/0x0/filters:format(png)/pic4109518.png",
|
||||
"thumb": "https://cf.geekdo-images.com/FmElPbRBjK5RmrAC9so5Gw__small/img/DBGCgJ7Ynbs3XUEGQjf63AB9QXE=/fit-in/200x150/filters:strip_icc()/pic4109518.png"
|
||||
},
|
||||
"2524:219137": {
|
||||
"full": "https://cf.geekdo-images.com/8PIdXg87V8l-VuCZAplQlQ__original/img/JRuDUwwK8S1jG9gZISv_Hwz5Kog=/0x0/filters:format(jpeg)/pic567384.jpg",
|
||||
"thumb": "https://cf.geekdo-images.com/8PIdXg87V8l-VuCZAplQlQ__small/img/OTL4rEL5eQ-8jJ_JubPwIq9LBIQ=/fit-in/200x150/filters:strip_icc()/pic567384.jpg"
|
||||
},
|
||||
"2529:98655": {
|
||||
"full": "https://cf.geekdo-images.com/U4aLOWpOLNtUjQ5BirlGLA__original/img/5m1hrtRgt6lst9tajA4o169D4wA=/0x0/filters:format(jpeg)/pic806423.jpg",
|
||||
"thumb": "https://cf.geekdo-images.com/U4aLOWpOLNtUjQ5BirlGLA__small/img/thPXwIIE3knD9igpLusCdeHVsMI=/fit-in/200x150/filters:strip_icc()/pic806423.jpg"
|
||||
},
|
||||
"255984:701034": {
|
||||
"full": "https://cf.geekdo-images.com/Zdt8l4oTBpFICsMyNof7Jg__original/img/-okZ810RkA8fKKhIeZyX4cnqmbE=/0x0/filters:format(png)/pic5975244.png",
|
||||
"thumb": "https://cf.geekdo-images.com/Zdt8l4oTBpFICsMyNof7Jg__small/img/71w4Tt3xOnuDEDTCG8ni63QUhVE=/fit-in/200x150/filters:strip_icc()/pic5975244.png"
|
||||
},
|
||||
"257:520364": {
|
||||
"full": "https://cf.geekdo-images.com/HzSvdAtAiktVmdTfDEIayA__original/img/hdvDZLMGdx5EDOQFlKj8GcZkTp8=/0x0/filters:format(jpeg)/pic2923257.jpg",
|
||||
"thumb": "https://cf.geekdo-images.com/HzSvdAtAiktVmdTfDEIayA__small/img/fIMPwA6ZEVr9bSf7QFrsezXjafg=/fit-in/200x150/filters:strip_icc()/pic2923257.jpg"
|
||||
},
|
||||
"258849:449806": {
|
||||
"full": "https://cf.geekdo-images.com/KypGSMwOACTbKnZNk1_nXQ__original/img/Blui7nSyq2Vr-0LpZS0C7AYD3xU=/0x0/filters:format(png)/pic4634784.png",
|
||||
"thumb": "https://cf.geekdo-images.com/KypGSMwOACTbKnZNk1_nXQ__small/img/h1F5b8CM6T2TkjT9U60TuAHfhvk=/fit-in/200x150/filters:strip_icc()/pic4634784.png"
|
||||
},
|
||||
"258:20840": {
|
||||
"full": "https://cf.geekdo-images.com/ZBQHRlhd32wt4F_zgeic6w__original/img/6-0pkob0RUXgRyxGPJ2ZKyYObxw=/0x0/filters:format(jpeg)/pic2405641.jpg",
|
||||
"thumb": "https://cf.geekdo-images.com/ZBQHRlhd32wt4F_zgeic6w__small/img/MjQ7iYoOXuU9AUrTojjYE8sdyTk=/fit-in/200x150/filters:strip_icc()/pic2405641.jpg"
|
||||
},
|
||||
"269564:439948": {
|
||||
"full": "https://cf.geekdo-images.com/L37TzOe9711go8n0IdRrkw__original/img/jTCsGiIn0XaQMik0RhYIZs_YEoM=/0x0/filters:format(jpeg)/pic4519833.jpg",
|
||||
"thumb": "https://cf.geekdo-images.com/L37TzOe9711go8n0IdRrkw__small/img/-vXxB24cuZC4TguLO5GJbCJFJa4=/fit-in/200x150/filters:strip_icc()/pic4519833.jpg"
|
||||
},
|
||||
"26:28607": {
|
||||
"full": "https://cf.geekdo-images.com/hNg-cufowH-csaTfzC_nPw__original/img/FXsJRqYH2tFltJrLqSlWdoZaJs8=/0x0/filters:format(jpeg)/pic5682587.jpg",
|
||||
"thumb": "https://cf.geekdo-images.com/hNg-cufowH-csaTfzC_nPw__small/img/M3hGcwq2PbwUmWE8anROZLBckKc=/fit-in/200x150/filters:strip_icc()/pic5682587.jpg"
|
||||
},
|
||||
"273240:446069": {
|
||||
"full": "https://cf.geekdo-images.com/uQlYk9U6qddWKyFQLBdrkA__original/img/l-4Sp5lbQYGuXTzaJCtaBK-jlHM=/0x0/filters:format(png)/pic5095199.png",
|
||||
"thumb": "https://cf.geekdo-images.com/uQlYk9U6qddWKyFQLBdrkA__small/img/WAwPiG02xkrr6i40oPsA2uNnWkM=/fit-in/200x150/filters:strip_icc()/pic5095199.png"
|
||||
},
|
||||
"27710:606657": {
|
||||
"full": "https://cf.geekdo-images.com/lNVa_4IQ3pBHuh5CRzSe-w__original/img/nDWd2-dY7D1ZoFPZhXvDqQs_BA8=/0x0/filters:format(jpeg)/pic3661032.jpg",
|
||||
"thumb": "https://cf.geekdo-images.com/lNVa_4IQ3pBHuh5CRzSe-w__small/img/ICEhSw8IrhG7i-zNf9k7E3I3rrs=/fit-in/200x150/filters:strip_icc()/pic3661032.jpg"
|
||||
},
|
||||
"280794:458300": {
|
||||
"full": "https://cf.geekdo-images.com/K1yVmbbWCsQuiWk-7x-V-Q__original/img/4l1YMMVL91dNV8KU9cAGADUBY68=/0x0/filters:format(png)/pic7455627.png",
|
||||
"thumb": "https://cf.geekdo-images.com/K1yVmbbWCsQuiWk-7x-V-Q__small/img/AfTJAQPJAcIWxK-w2-GExwmsTmA=/fit-in/200x150/filters:strip_icc()/pic7455627.png"
|
||||
},
|
||||
"283155:462049": {
|
||||
"full": "https://cf.geekdo-images.com/qGkU6XsF1448F_A4P8TY5Q__original/img/sm-qIkkIoKuYRUgkfP6kkXTm1QY=/0x0/filters:format(jpeg)/pic8124431.jpg",
|
||||
"thumb": "https://cf.geekdo-images.com/qGkU6XsF1448F_A4P8TY5Q__small/img/l5ODCWYHD8_1x-WFqf-TJZhXT38=/fit-in/200x150/filters:strip_icc()/pic8124431.jpg"
|
||||
},
|
||||
"283766:463140": {
|
||||
"full": "https://cf.geekdo-images.com/l0BQVDtQ5St4IK2nOO8-gA__original/img/N9lvu7iz02rzv_O9Z1mMqLAXeP4=/0x0/filters:format(jpeg)/pic5908969.jpg",
|
||||
"thumb": "https://cf.geekdo-images.com/l0BQVDtQ5St4IK2nOO8-gA__small/img/K18q5waDJlIXU_Jrrzuh9w2O_qc=/fit-in/200x150/filters:strip_icc()/pic5908969.jpg"
|
||||
},
|
||||
"290840:474978": {
|
||||
"full": "https://cf.geekdo-images.com/XUWGdPg-EMto9ODlBnGE3w__original/img/FuJq6qLrxLqgelZyD0exXecPgMk=/0x0/filters:format(jpeg)/pic7021586.jpg",
|
||||
"thumb": "https://cf.geekdo-images.com/XUWGdPg-EMto9ODlBnGE3w__small/img/DrAvXaXk1ougO-jbsulRG34e4qo=/fit-in/200x150/filters:strip_icc()/pic7021586.jpg"
|
||||
},
|
||||
"291126:475383": {
|
||||
"full": "https://cf.geekdo-images.com/kMb10YpuAZJZrT7ijArCbQ__original/img/si0dJo9JHr9d7h3r1QMKU6Uc-y8=/0x0/filters:format(jpeg)/pic5055223.jpg",
|
||||
"thumb": "https://cf.geekdo-images.com/kMb10YpuAZJZrT7ijArCbQ__small/img/iMH7IdUdahv6Rxx_jsc4rE5kxvs=/fit-in/200x150/filters:strip_icc()/pic5055223.jpg"
|
||||
},
|
||||
"291130:475387": {
|
||||
"full": "https://cf.geekdo-images.com/irNTpn32rkT1BbR0cvbcvg__original/img/ASaUeBCashp4Y2PtS1NFMmxaAh0=/0x0/filters:format(jpeg)/pic5792667.jpg",
|
||||
"thumb": "https://cf.geekdo-images.com/irNTpn32rkT1BbR0cvbcvg__small/img/6TczoQDunTYpVZAU6pAf_FzCsW8=/fit-in/200x150/filters:strip_icc()/pic5792667.jpg"
|
||||
},
|
||||
"291131:475388": {
|
||||
"full": "https://cf.geekdo-images.com/f7y3OmX7gPDntwh3MoILVQ__original/img/1difMUpNk3Plm2gRU9XoDDFsw4s=/0x0/filters:format(jpeg)/pic7021519.jpg",
|
||||
"thumb": "https://cf.geekdo-images.com/f7y3OmX7gPDntwh3MoILVQ__small/img/hMaVrSJU8FkIC49I8qN4p1dQ4OU=/fit-in/200x150/filters:strip_icc()/pic7021519.jpg"
|
||||
},
|
||||
"291457:476060": {
|
||||
"full": "https://cf.geekdo-images.com/_HhIdavYW-hid20Iq3hhmg__original/img/PBzsLRqNKQKJxGnzpb7o3qLWPQM=/0x0/filters:format(jpeg)/pic5055631.jpg",
|
||||
"thumb": "https://cf.geekdo-images.com/_HhIdavYW-hid20Iq3hhmg__small/img/OMkN_E5eyWrkID_cHCnQEbIixGM=/fit-in/200x150/filters:strip_icc()/pic5055631.jpg"
|
||||
},
|
||||
"291847:476820": {
|
||||
"full": "https://cf.geekdo-images.com/hZ-AiLfLCNSUbeTE8AupWg__original/img/LVkesfXjzA7WbVDXnKSfAsoziMI=/0x0/filters:format(png)/pic6746296.png",
|
||||
"thumb": "https://cf.geekdo-images.com/hZ-AiLfLCNSUbeTE8AupWg__small/img/M-0r7CRVrYaOF4844fUYB2nGOuo=/fit-in/200x150/filters:strip_icc()/pic6746296.png"
|
||||
},
|
||||
"295770:484007": {
|
||||
"full": "https://cf.geekdo-images.com/cwUgf-f-qwri8UHBUnifuQ__original/img/Tk7wFDJuaU8RPjNkmyC3AWYOPpU=/0x0/filters:format(png)/pic5092291.png",
|
||||
"thumb": "https://cf.geekdo-images.com/cwUgf-f-qwri8UHBUnifuQ__small/img/3o2Hc_N5mmfpOt8s9kP0ap8Jti8=/fit-in/200x150/filters:strip_icc()/pic5092291.png"
|
||||
},
|
||||
"2980:138013": {
|
||||
"full": "https://cf.geekdo-images.com/vcibhFq6RIEVMgknZN9nkQ__original/img/M7rw-SIUpSn9LfdD4N4qTZiGCKw=/0x0/filters:format(jpeg)/pic94813.jpg",
|
||||
"thumb": "https://cf.geekdo-images.com/vcibhFq6RIEVMgknZN9nkQ__small/img/_2U5jeZlYIOvbx26lWMm2tzXy44=/fit-in/200x150/filters:strip_icc()/pic94813.jpg"
|
||||
},
|
||||
"302181:629307": {
|
||||
"full": "https://cf.geekdo-images.com/iMF99GTMWoI0TGL2MwR_Vw__original/img/84ex4NEFtwR1A4RBx493YyIYtzY=/0x0/filters:format(png)/pic5843049.png",
|
||||
"thumb": "https://cf.geekdo-images.com/iMF99GTMWoI0TGL2MwR_Vw__small/img/bsm9TZhwegxp-LDhuOk132-6kGs=/fit-in/200x150/filters:strip_icc()/pic5843049.png"
|
||||
},
|
||||
"3090:25782": {
|
||||
"full": "https://cf.geekdo-images.com/3l-VQuk2dGc2Vjjz93wNVA__original/img/aJ9sEoCgiZIWHZ3cXATF8iayKhU=/0x0/filters:format(jpeg)/pic14058.jpg",
|
||||
"thumb": "https://cf.geekdo-images.com/3l-VQuk2dGc2Vjjz93wNVA__small/img/w82krrS4f639MQb2Qf2Eo8Bd1xg=/fit-in/200x150/filters:strip_icc()/pic14058.jpg"
|
||||
},
|
||||
"309202:507805": {
|
||||
"full": "https://cf.geekdo-images.com/-_IQS09ZDOz2zE2C0YQPcw__original/img/3Xhfx0WTSNDRr3bKqGH4lcgklME=/0x0/filters:format(jpeg)/pic5930951.jpg",
|
||||
"thumb": "https://cf.geekdo-images.com/-_IQS09ZDOz2zE2C0YQPcw__small/img/TLrgJs2e_qxcJHLi1HQQuyY9peE=/fit-in/200x150/filters:strip_icc()/pic5930951.jpg"
|
||||
},
|
||||
"311654": {
|
||||
"full": "https://cf.geekdo-images.com/211476emISgQLsa2h3BAYw__original/img/nGOu9LrFF5udRimbJrei6HExde8=/0x0/filters:format(png)/pic5625807.png",
|
||||
"thumb": "https://cf.geekdo-images.com/211476emISgQLsa2h3BAYw__small/img/g9QMstNAY2uOVSR1rH5Hx7Gxquk=/fit-in/200x150/filters:strip_icc()/pic5625807.png"
|
||||
},
|
||||
"31260:297589": {
|
||||
"full": "https://cf.geekdo-images.com/3L6ZtOll9W5O6-3-EwSMyw__original/img/V37KuMJlCzpxAilzN39BzeLvc9Q=/0x0/filters:format(jpeg)/pic1899157.jpg",
|
||||
"thumb": "https://cf.geekdo-images.com/3L6ZtOll9W5O6-3-EwSMyw__small/img/TpNF65wCIb1n3EGW18eJJ3MlRMo=/fit-in/200x150/filters:strip_icc()/pic1899157.jpg"
|
||||
},
|
||||
"312786:514050": {
|
||||
"full": "https://cf.geekdo-images.com/CDlBjXSPT-3Zyrj8ENBkvQ__original/img/FgAyx5Q1pe7TyHsYskhbab_S_os=/0x0/filters:format(jpeg)/pic5702797.jpg",
|
||||
"thumb": "https://cf.geekdo-images.com/CDlBjXSPT-3Zyrj8ENBkvQ__small/img/0ON18UcLepuwPLhwkRtRUjhvAZ8=/fit-in/200x150/filters:strip_icc()/pic5702797.jpg"
|
||||
},
|
||||
"3181:87931": {
|
||||
"full": "https://cf.geekdo-images.com/CKRu4CRpDan3G2tgvEEVrA__original/img/hMn8ZFnL4U2n51C2UrHtEfAYTY4=/0x0/filters:format(jpeg)/pic3624966.jpg",
|
||||
"thumb": "https://cf.geekdo-images.com/CKRu4CRpDan3G2tgvEEVrA__small/img/QfDqSbD-0wNsF9e6xbXKYiOtUgU=/fit-in/200x150/filters:strip_icc()/pic3624966.jpg"
|
||||
},
|
||||
"320202:528008": {
|
||||
"full": "https://cf.geekdo-images.com/GQ638rM8czzgXw3Q_CRa0w__original/img/A15NUwP2HoW3Ri_rfF6GLJ2rluk=/0x0/filters:format(jpeg)/pic5676578.jpg",
|
||||
"thumb": "https://cf.geekdo-images.com/GQ638rM8czzgXw3Q_CRa0w__small/img/egNXbJX2G74i-8ZI2rdT2M0etto=/fit-in/200x150/filters:strip_icc()/pic5676578.jpg"
|
||||
},
|
||||
"324937:537230": {
|
||||
"full": "https://cf.geekdo-images.com/bMcEMM5qurvnJxcosswjWg__original/img/Qpbm-L1d7fSMkfZaTX-5bLdchcg=/0x0/filters:format(jpeg)/pic5799068.jpg",
|
||||
"thumb": "https://cf.geekdo-images.com/bMcEMM5qurvnJxcosswjWg__small/img/Tjj681UWYL-zhdmd6KaUSvguHQY=/fit-in/200x150/filters:strip_icc()/pic5799068.jpg"
|
||||
},
|
||||
"329230:547518": {
|
||||
"full": "https://cf.geekdo-images.com/Oy7Yw7xT-OwweasGIwaskQ__original/img/t4H2LWrBNAStukWltN2r3z0-b5E=/0x0/filters:format(jpeg)/pic5929884.jpg",
|
||||
"thumb": "https://cf.geekdo-images.com/Oy7Yw7xT-OwweasGIwaskQ__small/img/ANfmrWMc9JcC8TnyY44xyG9n1AQ=/fit-in/200x150/filters:strip_icc()/pic5929884.jpg"
|
||||
},
|
||||
"330097:557415": {
|
||||
"full": "https://cf.geekdo-images.com/DA_qffgMoU54XdB2zVhq4A__original/img/vvpmOyOz1bBzQcTMd3zRrPT1_pY=/0x0/filters:format(png)/pic6979257.png",
|
||||
"thumb": "https://cf.geekdo-images.com/DA_qffgMoU54XdB2zVhq4A__small/img/as5aQqXhIWyzpToEr4rCeBoV0X8=/fit-in/200x150/filters:strip_icc()/pic6979257.png"
|
||||
},
|
||||
"332398:553061": {
|
||||
"full": "https://cf.geekdo-images.com/AkYQwEPr0fq_summgnx4wA__original/img/7KI0eciK8zImSiBn1N_9MF4Ws_o=/0x0/filters:format(png)/pic6010050.png",
|
||||
"thumb": "https://cf.geekdo-images.com/AkYQwEPr0fq_summgnx4wA__small/img/K_2DTzS-XA-QePfSVNcyBVGZMoQ=/fit-in/200x150/filters:strip_icc()/pic6010050.png"
|
||||
},
|
||||
"334011:701315": {
|
||||
"full": "https://cf.geekdo-images.com/EqqXUmjIuGspHNwt1UnDEQ__original/img/zROVukvZZ-GZR2Rm8JfzLOzGKnE=/0x0/filters:format(png)/pic7769466.png",
|
||||
"thumb": "https://cf.geekdo-images.com/EqqXUmjIuGspHNwt1UnDEQ__small/img/qKGfLe9Y83j2-UEzfRy1bIbNosI=/fit-in/200x150/filters:strip_icc()/pic7769466.png"
|
||||
},
|
||||
"334065:759453": {
|
||||
"full": "https://cf.geekdo-images.com/c1XGn5ryzbqgMOHzObZpQw__original/img/RWppBKjLU5v6Pl-uW6IIxkCD4U4=/0x0/filters:format(png)/pic6094080.png",
|
||||
"thumb": "https://cf.geekdo-images.com/c1XGn5ryzbqgMOHzObZpQw__small/img/QJq-vJsWq6D0DMrDFVno-0Lu00o=/fit-in/200x150/filters:strip_icc()/pic6094080.png"
|
||||
},
|
||||
"33451:629314": {
|
||||
"full": "https://cf.geekdo-images.com/IN7_CaqjdA1y-wk83q4Ttg__original/img/uyi-5ug8cC7IGrGofB9gC3OsliY=/0x0/filters:format(jpeg)/pic8623829.jpg",
|
||||
"thumb": "https://cf.geekdo-images.com/IN7_CaqjdA1y-wk83q4Ttg__small/img/65cyRKdoTWo6ukznkq74qSrQmj8=/fit-in/200x150/filters:strip_icc()/pic8623829.jpg"
|
||||
},
|
||||
"340420:567183": {
|
||||
"full": "https://cf.geekdo-images.com/_mGtR5kr2eMU1sIzauuLfw__original/img/rN-2_xDMxvJApCWsMmB7uhEjbNE=/0x0/filters:format(jpeg)/pic7073069.jpg",
|
||||
"thumb": "https://cf.geekdo-images.com/_mGtR5kr2eMU1sIzauuLfw__small/img/IMcOJmFc6NA53c1Nha9jnygrOOs=/fit-in/200x150/filters:strip_icc()/pic7073069.jpg"
|
||||
},
|
||||
"351544:588185": {
|
||||
"full": "https://cf.geekdo-images.com/4mOB7GMg4bWVtcNui4QV2w__original/img/NM8npv-uifjP1EbjyzPojJQjrAE=/0x0/filters:format(jpeg)/pic7021596.jpg",
|
||||
"thumb": "https://cf.geekdo-images.com/4mOB7GMg4bWVtcNui4QV2w__small/img/DmUWOsnpF6-II8ZpaOYOfRoBweU=/fit-in/200x150/filters:strip_icc()/pic7021596.jpg"
|
||||
},
|
||||
"352819:591147": {
|
||||
"full": "https://cf.geekdo-images.com/0eEF_yF58VcADXL6qe3olQ__original/img/Tkrr_ZJcHUU7eAv20ZMVkucQ3Gg=/0x0/filters:format(jpeg)/pic6566164.jpg",
|
||||
"thumb": "https://cf.geekdo-images.com/0eEF_yF58VcADXL6qe3olQ__small/img/kSEY6vPAGflPvqdHdBsaP4mtGkM=/fit-in/200x150/filters:strip_icc()/pic6566164.jpg"
|
||||
},
|
||||
"35505:781669": {
|
||||
"full": "https://cf.geekdo-images.com/nWcihyfWeAXo9SuSANPepw__original/img/P77A3nELOXRHK3JxCRLCTsSqOFo=/0x0/filters:format(jpeg)/pic2196826.jpg",
|
||||
"thumb": "https://cf.geekdo-images.com/nWcihyfWeAXo9SuSANPepw__small/img/UeAabtDenC1sLH02VF30QK-DtmI=/fit-in/200x150/filters:strip_icc()/pic2196826.jpg"
|
||||
},
|
||||
"355873:597861": {
|
||||
"full": "https://cf.geekdo-images.com/yHRhOGjndW2ZiAr0kajCrw__original/img/41Q110pcbM3sQ8rZO6wjk0tBp1Q=/0x0/filters:format(png)/pic9122177.png",
|
||||
"thumb": "https://cf.geekdo-images.com/yHRhOGjndW2ZiAr0kajCrw__small/img/-bs7koBkaPPACqWNQneRVctXzIc=/fit-in/200x150/filters:strip_icc()/pic9122177.png"
|
||||
},
|
||||
"358320:602661": {
|
||||
"full": "https://cf.geekdo-images.com/r72BZoC3whophBzKi45Rgg__original/img/hg-JfmpC3sDLY7B3Tb-FJW4cCs4=/0x0/filters:format(jpeg)/pic6737677.jpg",
|
||||
"thumb": "https://cf.geekdo-images.com/r72BZoC3whophBzKi45Rgg__small/img/rwyDCAEtK8uSahZXdLu8COHzVV0=/fit-in/200x150/filters:strip_icc()/pic6737677.jpg"
|
||||
},
|
||||
"3585:211154": {
|
||||
"full": "https://cf.geekdo-images.com/NpauF7CJudER5H5nj-z-gA__original/img/eGfEPINMJA_0pAKWAYyjJaXmaQo=/0x0/filters:format(jpeg)/pic10078.jpg",
|
||||
"thumb": "https://cf.geekdo-images.com/NpauF7CJudER5H5nj-z-gA__small/img/Vo85InhIcQUg1myZBiHCL22uu8A=/fit-in/200x150/filters:strip_icc()/pic10078.jpg"
|
||||
},
|
||||
"358880:603694": {
|
||||
"full": "https://cf.geekdo-images.com/MuyXJ2FuhnJI7nX9Y7kebA__original/img/LQI6P0oskmAEN7OqsqMyN5S820s=/0x0/filters:format(jpeg)/pic6746347.jpg",
|
||||
"thumb": "https://cf.geekdo-images.com/MuyXJ2FuhnJI7nX9Y7kebA__small/img/H7zAHIwzSiQNcvy42M9Ja8mZdWw=/fit-in/200x150/filters:strip_icc()/pic6746347.jpg"
|
||||
},
|
||||
"362205:612448": {
|
||||
"full": "https://cf.geekdo-images.com/pNQImPAMcRD_8vVdPwnjSw__original/img/OYLJctuXubzsWliJm0qF0pNlzu8=/0x0/filters:format(jpeg)/pic8037477.jpg",
|
||||
"thumb": "https://cf.geekdo-images.com/pNQImPAMcRD_8vVdPwnjSw__small/img/NXHl4pu_6HkAGQPp9LQ1JpUe4xQ=/fit-in/200x150/filters:strip_icc()/pic8037477.jpg"
|
||||
},
|
||||
"380459:743266": {
|
||||
"full": "https://cf.geekdo-images.com/GzhipcGBA1UC8CzH85hF_A__original/img/wNgYFo-rrT0ZCYYXKLCQPEXclDY=/0x0/filters:format(jpeg)/pic8511647.jpg",
|
||||
"thumb": "https://cf.geekdo-images.com/GzhipcGBA1UC8CzH85hF_A__small/img/UN0XWQ2UCXl0GSBANfZnjBozURU=/fit-in/200x150/filters:strip_icc()/pic8511647.jpg"
|
||||
},
|
||||
"380837:650545": {
|
||||
"full": "https://cf.geekdo-images.com/keC0oy0nbpewpRTdcte42Q__original/img/QNK35DZ__DvatgNqOkXKdZF5QM0=/0x0/filters:format(png)/pic7361181.png",
|
||||
"thumb": "https://cf.geekdo-images.com/keC0oy0nbpewpRTdcte42Q__small/img/HZKE-1leOFpU4Ny_dVpzPITexXs=/fit-in/200x150/filters:strip_icc()/pic7361181.png"
|
||||
},
|
||||
"380868:800879": {
|
||||
"full": "https://cf.geekdo-images.com/0FgUYfx9EXagzibb_DSAcw__original/img/2ncyqX25AJlYuUN6rnkvg452wSQ=/0x0/filters:format(png)/pic7371963.png",
|
||||
"thumb": "https://cf.geekdo-images.com/0FgUYfx9EXagzibb_DSAcw__small/img/jPv25ZXjNZjBA0uN7JOvZrbOKCs=/fit-in/200x150/filters:strip_icc()/pic7371963.png"
|
||||
},
|
||||
"381209:651213": {
|
||||
"full": "https://cf.geekdo-images.com/_T6cKdo8PfKVt175Sjqh9w__original/img/LEbdjO9H10aW4tDOKNef3nHCukU=/0x0/filters:format(jpeg)/pic7370593.jpg",
|
||||
"thumb": "https://cf.geekdo-images.com/_T6cKdo8PfKVt175Sjqh9w__small/img/L1aHDe7zKnLeVeYsdc6FUqDNMtY=/fit-in/200x150/filters:strip_icc()/pic7370593.jpg"
|
||||
},
|
||||
"386805:658656": {
|
||||
"full": "https://cf.geekdo-images.com/32-Zex3SY2iONm6YoHFOXg__original/img/xSAPlaWAm-eOBDA1mjyaNV1zaR4=/0x0/filters:format(jpeg)/pic8124079.jpg",
|
||||
"thumb": "https://cf.geekdo-images.com/32-Zex3SY2iONm6YoHFOXg__small/img/aongQ-Yn_oftxbk14Zs2G9NW2aY=/fit-in/200x150/filters:strip_icc()/pic8124079.jpg"
|
||||
},
|
||||
"389113:662372": {
|
||||
"full": "https://cf.geekdo-images.com/SEi9iqh77XAC8tisuhOWXw__original/img/BGEBivLN5VA_se4DGuFWc0RLFuQ=/0x0/filters:format(png)/pic8687420.png",
|
||||
"thumb": "https://cf.geekdo-images.com/SEi9iqh77XAC8tisuhOWXw__small/img/etKxGXITuKZWrRrNYHeylULhdr8=/fit-in/200x150/filters:strip_icc()/pic8687420.png"
|
||||
},
|
||||
"393672:669782": {
|
||||
"full": "https://cf.geekdo-images.com/Z6MMX39UEAZT3-f-DlnHsA__original/img/0t6A_tOIToHUkrBkhd2vUKDfric=/0x0/filters:format(jpeg)/pic8114705.jpg",
|
||||
"thumb": "https://cf.geekdo-images.com/Z6MMX39UEAZT3-f-DlnHsA__small/img/5Bf6oWIJTdHYpLAodTPqbDxh_Bc=/fit-in/200x150/filters:strip_icc()/pic8114705.jpg"
|
||||
},
|
||||
"39856:296529": {
|
||||
"full": "https://cf.geekdo-images.com/J0PlHArkZDJ57H-brXW2Fw__original/img/jt3kFCHJ3HJ2079dMLwipFZqdQg=/0x0/filters:format(jpeg)/pic6738336.jpg",
|
||||
"thumb": "https://cf.geekdo-images.com/J0PlHArkZDJ57H-brXW2Fw__small/img/QVwPXskFikQwBQlrdLJBAiRGgdg=/fit-in/200x150/filters:strip_icc()/pic6738336.jpg"
|
||||
},
|
||||
"403503:712612": {
|
||||
"full": "https://cf.geekdo-images.com/2F3s0L8rJIcVSi4V6LFgUA__original/img/Tf2AEBMBRSz__3JqWPjPVMWBizw=/0x0/filters:format(png)/pic7796760.png",
|
||||
"thumb": "https://cf.geekdo-images.com/2F3s0L8rJIcVSi4V6LFgUA__small/img/_z3mKychtGi-UYnJbBbZiWooUp8=/fit-in/200x150/filters:strip_icc()/pic7796760.png"
|
||||
},
|
||||
"40508:644326": {
|
||||
"full": "https://cf.geekdo-images.com/xSYdrpdM9hcr8QvY2UmudQ__original/img/yPcr2B3krYOr7gNFSaqMfUA40NY=/0x0/filters:format(jpeg)/pic7833020.jpg",
|
||||
"thumb": "https://cf.geekdo-images.com/xSYdrpdM9hcr8QvY2UmudQ__small/img/4jyxPgjZaWNgXRtIOn_CDymYQ3c=/fit-in/200x150/filters:strip_icc()/pic7833020.jpg"
|
||||
},
|
||||
"40529:27692": {
|
||||
"full": "https://cf.geekdo-images.com/bFAM5SbKCMp7qdwQyj0uiA__original/img/anmO_7hg4QsU_kI3BVR1Wx2kckA=/0x0/filters:format(jpeg)/pic455214.jpg",
|
||||
"thumb": "https://cf.geekdo-images.com/bFAM5SbKCMp7qdwQyj0uiA__small/img/Fy6EL7tvz-Ng8RudwDEa_OXK6LI=/fit-in/200x150/filters:strip_icc()/pic455214.jpg"
|
||||
},
|
||||
"40692:294178": {
|
||||
"full": "https://cf.geekdo-images.com/aoPM07XzoceB-RydLh08zA__original/img/bYLzrwZO9u-3h2wyU-PHc8aTOY4=/0x0/filters:format(jpeg)/pic428828.jpg",
|
||||
"thumb": "https://cf.geekdo-images.com/aoPM07XzoceB-RydLh08zA__small/img/o3Bw9heVDJRgPYlI_PksCvLAgnM=/fit-in/200x150/filters:strip_icc()/pic428828.jpg"
|
||||
},
|
||||
"417060:706506": {
|
||||
"full": "https://cf.geekdo-images.com/Thr7ZNBbZ2kf4y8qqfKYqg__original/img/Vhi-077ViAlIuOA8YMFJUHiP1Rc=/0x0/filters:format(png)/pic8117004.png",
|
||||
"thumb": "https://cf.geekdo-images.com/Thr7ZNBbZ2kf4y8qqfKYqg__small/img/fYdmsnAVXxGi1w89YOY2SSVmVuQ=/fit-in/200x150/filters:strip_icc()/pic8117004.png"
|
||||
},
|
||||
"417064:706510": {
|
||||
"full": "https://cf.geekdo-images.com/5u5FSmS_xeOUCLjsHjGgjA__original/img/doWGuH_qMnyHdtEaPoJiPXmX3C0=/0x0/filters:format(jpeg)/pic8117006.jpg",
|
||||
"thumb": "https://cf.geekdo-images.com/5u5FSmS_xeOUCLjsHjGgjA__small/img/cI0wddxm7l4HPIOyDWirdulArek=/fit-in/200x150/filters:strip_icc()/pic8117006.jpg"
|
||||
},
|
||||
"419687:711400": {
|
||||
"full": "https://cf.geekdo-images.com/BhZ3X_Zg6eIttfKIxf_b8w__original/img/cTfvj0tlLdc-0pzKGJMzyewptfQ=/0x0/filters:format(jpeg)/pic8158661.jpg",
|
||||
"thumb": "https://cf.geekdo-images.com/BhZ3X_Zg6eIttfKIxf_b8w__small/img/aI4rVBxf_NAn7igLVwUTZO8hAos=/fit-in/200x150/filters:strip_icc()/pic8158661.jpg"
|
||||
},
|
||||
"42063:191736": {
|
||||
"full": "https://cf.geekdo-images.com/XcHEdkno2QQsdafKzATxoQ__original/img/EmPbH2r5VWo7Bq6Kxqixyfb4c4I=/0x0/filters:format(jpeg)/pic1884930.jpg",
|
||||
"thumb": "https://cf.geekdo-images.com/XcHEdkno2QQsdafKzATxoQ__small/img/5R21LS5icdbQWUcW1sJLBpyZhlc=/fit-in/200x150/filters:strip_icc()/pic1884930.jpg"
|
||||
},
|
||||
"4324:26859": {
|
||||
"full": "https://cf.geekdo-images.com/i8ryx8pqPonNNUXyRWjQaw__original/img/jD6_3u-Z2e54Szi3I8p6Ma74Wc4=/0x0/filters:format(jpeg)/pic265703.jpg",
|
||||
"thumb": "https://cf.geekdo-images.com/i8ryx8pqPonNNUXyRWjQaw__small/img/0-JW3OU-6XDoUKqhQudJIbRX0Bc=/fit-in/200x150/filters:strip_icc()/pic265703.jpg"
|
||||
},
|
||||
"432858:734890": {
|
||||
"full": "https://cf.geekdo-images.com/C1Sv2zok3q8TpOtDeZBMhg__original/img/Qq4UtOCirSrPrm1DbDHJaq2Od7Q=/0x0/filters:format(jpeg)/pic8535016.jpg",
|
||||
"thumb": "https://cf.geekdo-images.com/C1Sv2zok3q8TpOtDeZBMhg__small/img/17-lW6rcxahVoo2m-rv3clp6iKU=/fit-in/200x150/filters:strip_icc()/pic8535016.jpg"
|
||||
},
|
||||
"43443:103068": {
|
||||
"full": "https://cf.geekdo-images.com/ryPiCH15spkrdtvUI-utuA__original/img/RYiSrrenTejCl149T6tc2d9CuwA=/0x0/filters:format(jpeg)/pic6966125.jpg",
|
||||
"thumb": "https://cf.geekdo-images.com/ryPiCH15spkrdtvUI-utuA__small/img/2MMQHuG-dSRngk0E2EKeqyE8e9o=/fit-in/200x150/filters:strip_icc()/pic6966125.jpg"
|
||||
},
|
||||
"442312:752105": {
|
||||
"full": "https://cf.geekdo-images.com/rIuj0DTErN0ADgUGy3ouqA__original/img/rJt3cu3yYurw847n4-oXODCqr3w=/0x0/filters:format(jpeg)/pic9222474.jpg",
|
||||
"thumb": "https://cf.geekdo-images.com/rIuj0DTErN0ADgUGy3ouqA__small/img/PRAbb-Dn1UjvEDD1w_GrrXSweCo=/fit-in/200x150/filters:strip_icc()/pic9222474.jpg"
|
||||
},
|
||||
"442637:752778": {
|
||||
"full": "https://cf.geekdo-images.com/d-OikNeqC_2SXRStAed06w__original/img/VaiaFa5u_lvDREjBjzeR-YeeaqQ=/0x0/filters:format(png)/pic8795782.png",
|
||||
"thumb": "https://cf.geekdo-images.com/d-OikNeqC_2SXRStAed06w__small/img/RwOBjjYI-RW2KVf4dEthcO6tMK8=/fit-in/200x150/filters:strip_icc()/pic8795782.png"
|
||||
},
|
||||
"452833:770724": {
|
||||
"full": "https://cf.geekdo-images.com/4W4e1gDmGX092V4qijUp8g__original/img/2Ex8yq2WUUwCqP1fNXB9Vf-4Zik=/0x0/filters:format(jpeg)/pic9066288.jpg",
|
||||
"thumb": "https://cf.geekdo-images.com/4W4e1gDmGX092V4qijUp8g__small/img/uYxDfY5I64aPP14glGP3z53XsiY=/fit-in/200x150/filters:strip_icc()/pic9066288.jpg"
|
||||
},
|
||||
"453845:772660": {
|
||||
"full": "https://cf.geekdo-images.com/E7twFOiN0K_-A5631GhfgQ__original/img/ufOMwOIHxkCaMG4cynKWJ5QfXN4=/0x0/filters:format(jpeg)/pic9415760.jpg",
|
||||
"thumb": "https://cf.geekdo-images.com/E7twFOiN0K_-A5631GhfgQ__small/img/DKu8C5l7hHXx9l4JJBeixLachhg=/fit-in/200x150/filters:strip_icc()/pic9415760.jpg"
|
||||
},
|
||||
"455834:791302": {
|
||||
"full": "https://cf.geekdo-images.com/_Q5oVZtUQ809i9ENP9nv0A__original/img/tLtECXLbsKmryUw3jyahO26P4O0=/0x0/filters:format(png)/pic9406794.png",
|
||||
"thumb": "https://cf.geekdo-images.com/_Q5oVZtUQ809i9ENP9nv0A__small/img/bud3IvzdsqZ2KtFAlgjmR4ticGQ=/fit-in/200x150/filters:strip_icc()/pic9406794.png"
|
||||
},
|
||||
"483:48522": {
|
||||
"full": "https://cf.geekdo-images.com/CGJihifkrZSqW40zElgXkQ__original/img/m38u19fIxQceN0fJtLU3O31MhXw=/0x0/filters:format(jpeg)/pic7376874.jpg",
|
||||
"thumb": "https://cf.geekdo-images.com/CGJihifkrZSqW40zElgXkQ__small/img/_Ar945YdaXSz3iFIR_aOCCcf6zc=/fit-in/200x150/filters:strip_icc()/pic7376874.jpg"
|
||||
},
|
||||
"50381:134539": {
|
||||
"full": "https://cf.geekdo-images.com/nYLrPiI9gnvlrwOrKQ4_CA__original/img/EgMpdhoG38LUTApk0kyFFsmoQPk=/0x0/filters:format(jpeg)/pic2909692.jpg",
|
||||
"thumb": "https://cf.geekdo-images.com/nYLrPiI9gnvlrwOrKQ4_CA__small/img/fIhQLjWueNPYZhCZ_LzjAIhPm5U=/fit-in/200x150/filters:strip_icc()/pic2909692.jpg"
|
||||
},
|
||||
"57215:295423": {
|
||||
"full": "https://cf.geekdo-images.com/D_SKR1RenJ-A96W4AcpVZw__original/img/AYoJl-zbL8OLzsx0xiC7FjvMIrQ=/0x0/filters:format(jpeg)/pic3052922.jpg",
|
||||
"thumb": "https://cf.geekdo-images.com/D_SKR1RenJ-A96W4AcpVZw__small/img/eJdxkVo28a-PvuEY5wBzbkuBAW4=/fit-in/200x150/filters:strip_icc()/pic3052922.jpg"
|
||||
},
|
||||
"589:27352": {
|
||||
"full": "https://cf.geekdo-images.com/TjTRyJv5BQRo9GTy3OA6Jw__original/img/-E47TiT6MiucjjUwRstlOwDo6yU=/0x0/filters:format(jpeg)/pic184442.jpg",
|
||||
"thumb": "https://cf.geekdo-images.com/TjTRyJv5BQRo9GTy3OA6Jw__small/img/Tlfd2Jdg2WTuJN_lQiqLp6mL0zM=/fit-in/200x150/filters:strip_icc()/pic184442.jpg"
|
||||
},
|
||||
"65244:31208": {
|
||||
"full": "https://cf.geekdo-images.com/JgAkEBUaiHOsOS94iRMs2w__original/img/H5d4I5z_HSpPEu7EAl0DqLt9_pM=/0x0/filters:format(jpeg)/pic646458.jpg",
|
||||
"thumb": "https://cf.geekdo-images.com/JgAkEBUaiHOsOS94iRMs2w__small/img/5aPR8-KMbgucI_XkPE2PJaMQQsg=/fit-in/200x150/filters:strip_icc()/pic646458.jpg"
|
||||
},
|
||||
"66510:629315": {
|
||||
"full": "https://cf.geekdo-images.com/v_A9anux7cv1kea3inNKFQ__original/img/oZ6oWb_8Dm46gT_Wl3c1vei1hK0=/0x0/filters:format(jpeg)/pic1022815.jpg",
|
||||
"thumb": "https://cf.geekdo-images.com/v_A9anux7cv1kea3inNKFQ__small/img/j4u56DHgOY4DYld9kSymbhPPJg0=/fit-in/200x150/filters:strip_icc()/pic1022815.jpg"
|
||||
},
|
||||
"71:24006": {
|
||||
"full": "https://cf.geekdo-images.com/tUdVpt5daNITDrLXqN931Q__original/img/121amP5yjflTHIFW017EGPe5Rjk=/0x0/filters:format(jpeg)/pic114473.jpg",
|
||||
"thumb": "https://cf.geekdo-images.com/tUdVpt5daNITDrLXqN931Q__small/img/AI7xWgZxUCdXZHPH5WXCXWSAKUI=/fit-in/200x150/filters:strip_icc()/pic114473.jpg"
|
||||
},
|
||||
"822:187468": {
|
||||
"full": "https://cf.geekdo-images.com/peUgu3A20LRmAXAMyDQfpQ__original/img/bP18m_PYjyFOv1IBGgMOteQUneA=/0x0/filters:format(jpeg)/pic8621446.jpg",
|
||||
"thumb": "https://cf.geekdo-images.com/peUgu3A20LRmAXAMyDQfpQ__small/img/oEEslN-EGqh82sNI6Aj4_MFXYg0=/fit-in/200x150/filters:strip_icc()/pic8621446.jpg"
|
||||
},
|
||||
"9209:294188": {
|
||||
"full": "https://cf.geekdo-images.com/kdWYkW-7AqG63HhqPL6ekA__original/img/rWF8r4JXXCQQ7QhiWHhmT-rQ3Pc=/0x0/filters:format(jpeg)/pic8937637.jpg",
|
||||
"thumb": "https://cf.geekdo-images.com/kdWYkW-7AqG63HhqPL6ekA__small/img/5G46jv8MFh_BfX67iMSouTMhKxc=/fit-in/200x150/filters:strip_icc()/pic8937637.jpg"
|
||||
},
|
||||
"95386:290572": {
|
||||
"full": "https://cf.geekdo-images.com/LWLRKZPO9B5ITz5f6Linhg__original/img/feTFDIvserg3DrmG6QnsDgtZrF0=/0x0/filters:format(jpeg)/pic2776998.jpg",
|
||||
"thumb": "https://cf.geekdo-images.com/LWLRKZPO9B5ITz5f6Linhg__small/img/beldZHdnD-u49LMy5X6fEJW2F_8=/fit-in/200x150/filters:strip_icc()/pic2776998.jpg"
|
||||
},
|
||||
"9666:215137": {
|
||||
"full": "https://cf.geekdo-images.com/DjZo9eunX6okcHJQ9jcY4A__original/img/AzaZKBcbN2rpXPCngQB1y_7lHIA=/0x0/filters:format(jpeg)/pic46138.jpg",
|
||||
"thumb": "https://cf.geekdo-images.com/DjZo9eunX6okcHJQ9jcY4A__small/img/O6QrclVzklwhvVe4c8AFM4DZw-I=/fit-in/200x150/filters:strip_icc()/pic46138.jpg"
|
||||
},
|
||||
"97903:231971": {
|
||||
"full": "https://cf.geekdo-images.com/loyFNu06XcKcRcoouNkn5Q__original/img/Dg5JwmmkDHJ4b4WDAU7WREHbBdw=/0x0/filters:format(jpeg)/pic2482302.jpg",
|
||||
"thumb": "https://cf.geekdo-images.com/loyFNu06XcKcRcoouNkn5Q__small/img/w5H9B8agVcPdAfNYgvN1qoFjLsc=/fit-in/200x150/filters:strip_icc()/pic2482302.jpg"
|
||||
}
|
||||
},
|
||||
"slugs": {
|
||||
"104590:117256": "castle-panic-the-wizard-s-tower",
|
||||
"104710:117685": "wiz-war-eighth-edition",
|
||||
"116:28201": "guillotine",
|
||||
"1197:78624": "blink",
|
||||
"1219:488621": "labyrinth",
|
||||
"124548:165214": "the-doom-that-came-to-atlantic-city",
|
||||
"125472:629302": "the-red-dragon-inn-allies-pooky",
|
||||
"125675:167657": "doctor-who-the-card-game",
|
||||
"125921:476263": "catan-junior",
|
||||
"1260:156689": "rook",
|
||||
"1269:29705": "skip-bo",
|
||||
"128554:207529": "v-lusp",
|
||||
"1339:34390": "dungeon",
|
||||
"137909:216474": "bugs-in-the-kitchen",
|
||||
"139443:202519": "superfight",
|
||||
"13:347121": "catan",
|
||||
"142402:657689": "the-red-dragon-inn-4",
|
||||
"143884:453051": "machi-koro",
|
||||
"14535:29075": "spanc-space-pirate-amazon-ninja-catgirls",
|
||||
"147370:231664": "robot-turtles",
|
||||
"150925:225659": "hold-your-breath",
|
||||
"153999:280845": "and-then-we-held-hands",
|
||||
"156994:236280": "skylanders-portal-master",
|
||||
"157969:237864": "sheriff-of-nottingham",
|
||||
"160477:269392": "onitama",
|
||||
"161928:245124": "utter-nonsense",
|
||||
"163412:495998": "patchwork",
|
||||
"164:28465": "before-i-kill-you-mister-spy",
|
||||
"165986:277137": "royals",
|
||||
"166109:253058": "meteor",
|
||||
"166976:410382": "consentacle",
|
||||
"168857:657857": "the-red-dragon-inn-allies-wrench",
|
||||
"169124:451577": "flick-em-up",
|
||||
"169784:260454": "castle-panic-the-dark-titan",
|
||||
"172225:296012": "exploding-kittens",
|
||||
"172503:265107": "mage-wars-academy",
|
||||
"173057:266044": "go-fish-yourself",
|
||||
"173634:266954": "the-red-dragon-inn-5-the-character-trove",
|
||||
"174430:520054": "gloomhaven",
|
||||
"174922:657858": "the-red-dragon-inn-allies-halden-the-unhinged",
|
||||
"176:27673": "give-me-the-brain",
|
||||
"177:24972": "advanced-civilization",
|
||||
"1784:110926": "dark-cults",
|
||||
"181304:536289": "mysterium",
|
||||
"18755:284056": "pie-face",
|
||||
"188614:779792": "simon-s-cat-card-game",
|
||||
"1927:28144": "munchkin",
|
||||
"193485:300446": "dastardly-dirigibles",
|
||||
"193621:300682": "joking-hazard",
|
||||
"194655:668826": "santorini",
|
||||
"195314:525268": "herbaceous",
|
||||
"196105:304900": "speechless",
|
||||
"201248:313478": "evolution-the-beginning",
|
||||
"202982:316090": "scrawl",
|
||||
"2058:28041": "civilization-west-extension-map",
|
||||
"207830:344075": "5-minute-dungeon",
|
||||
"208428:325455": "no-thank-you-evil",
|
||||
"20866:40025": "the-rocky-horror-trivia-game",
|
||||
"212662:367695": "the-red-dragon-inn-6-villains",
|
||||
"2197:42495": "kensington",
|
||||
"220199:369805": "snappy-dressers",
|
||||
"224:20727": "history-of-the-world",
|
||||
"226065:353389": "sheriff-of-nottingham-merry-men",
|
||||
"231302:361880": "the-cat-game",
|
||||
"2387:91428": "huggermugger",
|
||||
"240:24621": "britannia",
|
||||
"24310:374258": "the-red-dragon-inn",
|
||||
"244258:421810": "the-red-dragon-inn-7-the-tavern-crew",
|
||||
"2452:288640": "jenga",
|
||||
"252153:404702": "tang-garden",
|
||||
"2524:219137": "starforce-alpha-centauri-interstellar-conflict-in-the-25th-century",
|
||||
"2529:98655": "flat-top",
|
||||
"255984:701034": "sleeping-gods",
|
||||
"257:520364": "kill-doctor-lucky",
|
||||
"258849:449806": "parsely",
|
||||
"258:20840": "fluxx",
|
||||
"269564:439948": "captain-marvel-secret-skrulls",
|
||||
"26:28607": "age-of-renaissance",
|
||||
"273240:446069": "the-red-dragon-inn-smorgasbox",
|
||||
"27710:606657": "catan-dice-game",
|
||||
"280794:458300": "etherfields",
|
||||
"283155:462049": "calico",
|
||||
"283766:463140": "sleeping-gods-tides-of-ruin",
|
||||
"290840:474978": "etherfields-sphinx-campaign",
|
||||
"291126:475383": "etherfields-thorn-knight",
|
||||
"291130:475387": "etherfields-5th-player-expansion",
|
||||
"291131:475388": "etherfields-funeral-witch-campaign",
|
||||
"291457:476060": "gloomhaven-jaws-of-the-lion",
|
||||
"291847:476820": "mantis-falls",
|
||||
"295770:484007": "frosthaven",
|
||||
"2980:138013": "the-simpsons-trivia-game",
|
||||
"302181:629307": "the-red-dragon-inn-allies-evil-pooky",
|
||||
"3090:25782": "doctor-who-the-game-of-time-space",
|
||||
"309202:507805": "etherfields-search-mini-deck",
|
||||
"311654": "alice-is-missing",
|
||||
"31260:297589": "agricola",
|
||||
"312786:514050": "poetry-for-neanderthals",
|
||||
"3181:87931": "farkle",
|
||||
"320202:528008": "dragon-land",
|
||||
"324937:537230": "wiz-war-9th-edition",
|
||||
"329230:547518": "bluffaneer",
|
||||
"330097:557415": "the-red-dragon-inn-8-pub-crawl",
|
||||
"332398:553061": "everdell-the-complete-collection",
|
||||
"334011:701315": "a-gentle-rain",
|
||||
"334065:759453": "verdant",
|
||||
"33451:629314": "the-red-dragon-inn-2",
|
||||
"340420:567183": "throw-throw-avocado",
|
||||
"351544:588185": "etherfields-stretch-goals-harpy-she-wolf-campaigns",
|
||||
"352819:591147": "the-office-trivia-game",
|
||||
"35505:781669": "walk-the-plank",
|
||||
"355873:597861": "etherfields-awakened-items-actions",
|
||||
"358320:602661": "sleeping-gods-distant-skies",
|
||||
"3585:211154": "sorcerer-the-game-of-magical-conflict",
|
||||
"358880:603694": "etherfields-kittenburg-expansion",
|
||||
"362205:612448": "sleeping-gods-primeval-peril",
|
||||
"380459:743266": "alice-is-missing-silent-falls",
|
||||
"380837:650545": "botany",
|
||||
"380868:800879": "the-red-dragon-inn-9-the-undercity",
|
||||
"381209:651213": "etherfields-shifting-story-promo-deck",
|
||||
"386805:658656": "etherfields-story-dice-expansion",
|
||||
"389113:662372": "rivals",
|
||||
"393672:669782": "gloomhaven-buttons-bugs",
|
||||
"39856:296529": "dixit",
|
||||
"403503:712612": "the-red-dragon-inn-allies-melvyn-vs-marah",
|
||||
"40508:644326": "scrabble-slam",
|
||||
"40529:27692": "cosmic-encounter",
|
||||
"40692:294178": "small-world",
|
||||
"417060:706506": "botany-tantalizing-trees",
|
||||
"417064:706510": "botany-perilous-perfumes",
|
||||
"419687:711400": "munchkin-big-box",
|
||||
"42063:191736": "code-master",
|
||||
"4324:26859": "risk-the-lord-of-the-rings",
|
||||
"432858:734890": "artistry",
|
||||
"43443:103068": "castle-panic",
|
||||
"442312:752105": "deadly-doodles",
|
||||
"442637:752778": "the-red-dragon-inn-allies-jin-vs-greppa",
|
||||
"452833:770724": "artistry-delightful-doorways-mini-expansion",
|
||||
"453845:772660": "murder-at-the-manor",
|
||||
"455834:791302": "the-red-dragon-inn-10",
|
||||
"483:48522": "diplomacy",
|
||||
"50381:134539": "cards-against-humanity",
|
||||
"57215:295423": "googly-eyes",
|
||||
"589:27352": "wiz-war",
|
||||
"65244:31208": "forbidden-island",
|
||||
"66510:629315": "the-red-dragon-inn-3",
|
||||
"71:24006": "civilization",
|
||||
"822:187468": "carcassonne",
|
||||
"9209:294188": "ticket-to-ride",
|
||||
"95386:290572": "tem-purr-a",
|
||||
"9666:215137": "greed-quest",
|
||||
"97903:231971": "terror-in-meeple-city",
|
||||
"local:cat crimes:IMG_4499.jpeg;IMG_4505.jpeg;shelf-20260803-215723.jpg": "cat-crimes",
|
||||
"local:cat stax:IMG_4544.jpeg": "cat-stax",
|
||||
"local:dungeons and dragons fantasy game rules:IMG_4519.jpeg": "dungeons-dragons-fantasy-game-rules",
|
||||
"local:dungeons and dragons:IMG_4519.jpeg": "dungeons-dragons",
|
||||
"local:elite sportz equipment table top curling:IMG_4526.jpeg": "elite-sportz-equipment-table-top-curling",
|
||||
"local:gravity maze:IMG_4510.jpeg": "gravity-maze",
|
||||
"local:laser maze beam bending logic game:IMG_4516.jpeg": "laser-maze-beam-bending-logic-game",
|
||||
"local:ouija:IMG_4529.jpeg": "ouija",
|
||||
"local:super mario checkers collector s edition:IMG_4508.jpeg": "super-mario-checkers-collector-s-edition"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>5-Minute Dungeon · The Game Shelves</title>
|
||||
<meta name="description" content="5-Minute Dungeon in The Game Shelves">
|
||||
<meta property="og:title" content="5-Minute Dungeon · The Game Shelves">
|
||||
<meta property="og:image" content="../art/207830-344075.jpg">
|
||||
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>🎲</text></svg>">
|
||||
<link rel="stylesheet" href="../style.css">
|
||||
<header class="site"><a href=".././">The Game Shelves</a>
|
||||
<span class="sub">a board game library</span></header>
|
||||
<main>
|
||||
<p><a href="../">← the whole library</a> · <a href="https://boardgamegeek.com/boardgame/207830" rel="noopener">view on BGG ↗</a></p>
|
||||
<h1>5-Minute Dungeon <span class="meta">(2017)</span></h1>
|
||||
<div class="detail">
|
||||
<div class="art"><img src="../art/207830-344075.jpg" alt="box art for 5-Minute Dungeon"></div>
|
||||
<div class="facts"><div class="factrow"><b>players</b> 2–5 players (best at 4, 5)</div><div class="factrow"><b>playing time</b> 5–30 min</div><div class="factrow"><b>ages</b> 8+</div><div class="factrow"><b>weight</b> 1.13 / 5</div><div class="factrow"><b>BGG rank</b> 1135</div><div class="factrow"><b>BGG rating</b> 6.90</div><div class="factrow"><b>designers</b> <span class="chip">Connor Reid</span></div><div class="factrow"><b>artists</b> <span class="chip">Alex Diochon</span></div><div class="factrow"><b>publishers</b> <span class="chip">Wiggles 3D</span> <span class="chip">(Web published)</span> <span class="chip">DV Games</span> <span class="chip">Galápagos Jogos</span> <span class="chip">Korea Boardgames</span> <span class="chip">KOSMOS</span></div><div class="factrow"><b>categories</b> <span class="chip">Card Game</span> <span class="chip">Fantasy</span> <span class="chip">Fighting</span> <span class="chip">Print & Play</span> <span class="chip">Real-time</span></div><div class="factrow"><b>mechanics</b> <span class="chip">Card Play Conflict Resolution</span> <span class="chip">Cooperative Game</span> <span class="chip">Elapsed Real Time Ending</span> <span class="chip">Hand Management</span> <span class="chip">Move Through Deck</span> <span class="chip">Real-Time</span> <span class="chip">Variable Player Powers</span></div></div>
|
||||
</div>
|
||||
<div class="card"><div class="factrow"><b>my copy</b> Wiggles English Kickstarter edition · 2017</div><div class="factrow"><b>publishers</b> <span class="chip">Wiggles 3D</span></div><div class="factrow"><b>languages</b> <span class="chip">English</span></div></div>
|
||||
<div class="card desc">5-Minute Dungeon is a chaotic, co-operative, real-time card game in which players have only five minutes to escape the randomized dungeon. Communication and teamwork are critical to survival because there's no time to form a carefully considered plan — and no predicting what dangers lie ahead.
|
||||
|
||||
In more detail, players assume the role of one of ten heroes, each with special cards and abilities. Once the five-minute timer starts, the race is on to defeat all the monsters inside the dungeon. In order to defeat a monster, players must match symbols from their hand with ones on the monster's card. At the end of each dungeon is a powerful dungeon boss — and after the first boss is defeated, the campaign continues to the second boss. Each boss, and each randomized dungeon, gets harder until players reach the fifth and final boss.
|
||||
|
||||
There is a Standard Edition and a Dungeon Master Edition (Kickstarted Exclusive Cover).
|
||||
|
||||
Both Kickstarter editions includes:
|
||||
- 2 New Bosses
|
||||
- A Deck with 25 new cards [15 Doors + 10 Challenge Cards]
|
||||
- A Baby Barbarian Figurine (with blood in the sword)</div><div class="card"><div class="factrow"><b>where it lives</b> Library Kallax · C1</div></div>
|
||||
<footer>
|
||||
<p><a class="pbb" href="https://boardgamegeek.com" rel="noopener"><img src="../art/powered-by-bgg.png" alt="Powered by BGG" height="40"></a></p>
|
||||
<p>The Game Shelves · cataloged from shelf photos by
|
||||
<a href="https://git.kestrelsnest.social/eric/bggpipe"
|
||||
rel="noopener">bggpipe</a></p>
|
||||
<p>Game data and cover images from <a href="https://boardgamegeek.com"
|
||||
rel="noopener">BoardGameGeek</a>. BoardGameGeek and BGG are trademarks of
|
||||
BoardGameGeek, LLC; this page is an independent project, not affiliated
|
||||
with or endorsed by BoardGameGeek.</p>
|
||||
</footer>
|
||||
</main>
|
||||
</html>
|
||||
@@ -0,0 +1,41 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>A Gentle Rain · The Game Shelves</title>
|
||||
<meta name="description" content="A Gentle Rain in The Game Shelves">
|
||||
<meta property="og:title" content="A Gentle Rain · The Game Shelves">
|
||||
<meta property="og:image" content="../art/334011-701315.png">
|
||||
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>🎲</text></svg>">
|
||||
<link rel="stylesheet" href="../style.css">
|
||||
<header class="site"><a href=".././">The Game Shelves</a>
|
||||
<span class="sub">a board game library</span></header>
|
||||
<main>
|
||||
<p><a href="../">← the whole library</a> · <a href="https://boardgamegeek.com/boardgame/334011" rel="noopener">view on BGG ↗</a></p>
|
||||
<h1>A Gentle Rain <span class="meta">(2021)</span></h1>
|
||||
<div class="detail">
|
||||
<div class="art"><img src="../art/334011-701315.png" alt="box art for A Gentle Rain"></div>
|
||||
<div class="facts"><div class="factrow"><b>players</b> 1 player (best at 1)</div><div class="factrow"><b>playing time</b> 15 min</div><div class="factrow"><b>ages</b> 8+</div><div class="factrow"><b>weight</b> 1.10 / 5</div><div class="factrow"><b>BGG rank</b> 972</div><div class="factrow"><b>BGG rating</b> 7.62</div><div class="factrow"><b>designers</b> <span class="chip">Kevin Wilson</span></div><div class="factrow"><b>artists</b> <span class="chip">Chris Bilheimer</span> <span class="chip">Wiktor Kozyra</span></div><div class="factrow"><b>publishers</b> <span class="chip">Incredible Dream Studios</span> <span class="chip">GaGa Games</span> <span class="chip">MeepleMood</span> <span class="chip">Mondo Games</span> <span class="chip">Present Is Future Games (PIF GAMES)</span></div><div class="factrow"><b>categories</b> <span class="chip">Puzzle</span></div><div class="factrow"><b>mechanics</b> <span class="chip">Pattern Building</span> <span class="chip">Solo / Solitaire Game</span> <span class="chip">Tile Placement</span></div></div>
|
||||
</div>
|
||||
<div class="card"><div class="factrow"><b>my copy</b> English Bloom edition · 2024</div><div class="factrow"><b>publishers</b> <span class="chip">Incredible Dream Studios</span></div><div class="factrow"><b>languages</b> <span class="chip">English</span></div></div>
|
||||
<div class="card desc">TAKE A DEEP BREATH, AND RELAX...
|
||||
|
||||
You have come to the lake hoping to see a rare and beautiful sight. The lilies of the lake only open their blossoms in the rain, and only rarely do all eight kinds of lily bloom at once. The goal of A Gentle Rain is to place the lake tiles in such a way to cause all eight types of lilies to bloom before you run out of tiles and the rain ends.
|
||||
|
||||
Place each new tile you draw next to a tile already in play, making sure to match the colors of all the tile edges touching the tile you are placing. Each time you manage to complete a square of four touching tiles, a blossom opens between them.
|
||||
|
||||
Keep Score, or don’t.
|
||||
|
||||
—description from the publisher</div><div class="card"><div class="factrow"><b>where it lives</b> Library Kallax · C1</div></div>
|
||||
<footer>
|
||||
<p><a class="pbb" href="https://boardgamegeek.com" rel="noopener"><img src="../art/powered-by-bgg.png" alt="Powered by BGG" height="40"></a></p>
|
||||
<p>The Game Shelves · cataloged from shelf photos by
|
||||
<a href="https://git.kestrelsnest.social/eric/bggpipe"
|
||||
rel="noopener">bggpipe</a></p>
|
||||
<p>Game data and cover images from <a href="https://boardgamegeek.com"
|
||||
rel="noopener">BoardGameGeek</a>. BoardGameGeek and BGG are trademarks of
|
||||
BoardGameGeek, LLC; this page is an independent project, not affiliated
|
||||
with or endorsed by BoardGameGeek.</p>
|
||||
</footer>
|
||||
</main>
|
||||
</html>
|
||||
@@ -0,0 +1,35 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Advanced Civilization · The Game Shelves</title>
|
||||
<meta name="description" content="Advanced Civilization in The Game Shelves">
|
||||
<meta property="og:title" content="Advanced Civilization · The Game Shelves">
|
||||
<meta property="og:image" content="../art/177-24972.jpg">
|
||||
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>🎲</text></svg>">
|
||||
<link rel="stylesheet" href="../style.css">
|
||||
<header class="site"><a href=".././">The Game Shelves</a>
|
||||
<span class="sub">a board game library</span></header>
|
||||
<main>
|
||||
<p><a href="../">← the whole library</a> · <a href="https://boardgamegeek.com/boardgame/177" rel="noopener">view on BGG ↗</a></p>
|
||||
<h1>Advanced Civilization <span class="meta">(1991)</span></h1>
|
||||
<div class="detail">
|
||||
<div class="art"><img src="../art/177-24972.jpg" alt="box art for Advanced Civilization"></div>
|
||||
<div class="facts"><div class="factrow"><b>players</b> 2–8 players (best at 6, 7, 8)</div><div class="factrow"><b>playing time</b> 360–480 min</div><div class="factrow"><b>ages</b> 12+</div><div class="factrow"><b>weight</b> 3.65 / 5</div><div class="factrow"><b>BGG rating</b> 8.01</div><div class="factrow"><b>designers</b> <span class="chip">Lauren Banerd</span> <span class="chip">Jim Eliason</span> <span class="chip">Jeff Groteboer</span> <span class="chip">Bruce Harper</span> <span class="chip">Eric Hunter</span> <span class="chip">Steven Padgett</span> <span class="chip">Gary Rapanos</span> <span class="chip">Michael Roos</span> <span class="chip">Jennifer Schlickbernd</span> <span class="chip">Jeff Suchard</span></div><div class="factrow"><b>artists</b> <span class="chip">Rodger B. MacGowan</span></div><div class="factrow"><b>publishers</b> <span class="chip">The Avalon Hill Game Co</span></div><div class="factrow"><b>categories</b> <span class="chip">Ancient</span> <span class="chip">Civilization</span> <span class="chip">Economic</span> <span class="chip">Expansion for Base-game</span> <span class="chip">Negotiation</span></div><div class="factrow"><b>mechanics</b> <span class="chip">Area Movement</span> <span class="chip">Commodity Speculation</span> <span class="chip">Set Collection</span> <span class="chip">Trading</span></div></div>
|
||||
</div>
|
||||
<div class="card"><div class="factrow"><b>my copy</b> English edition · 1991</div><div class="factrow"><b>publishers</b> <span class="chip">The Avalon Hill Game Co</span></div><div class="factrow"><b>languages</b> <span class="chip">English</span></div></div>
|
||||
<div class="card desc">The use of the word "Advanced" to describe this expansion followed existing Avalon Hill tradition. "Enhanced" is likely more appropriate. Playing with this expansion is no more difficult than playing the base Civilization game. This expansion reworks a few of the original rules, includes a reworked version of Civilization: Expansion Trade Cards Set and adds more civilization advancements to the technology tree.
|
||||
|
||||
Included are more civilization advancements (both quantity and type), which give the players new options for advancement, as well as a slightly restructured commodity trading round, which has new resources and disasters to be traded. The most significant aspect of the game is a restructuring of the advancement/victory conditions, which remove the limits on advancement cards. Some claim this makes the strategies more homogeneous while others claim the reverse is true.</div><div class="card"><div class="factrow"><b>where it lives</b> Library Kallax · D2</div></div>
|
||||
<footer>
|
||||
<p><a class="pbb" href="https://boardgamegeek.com" rel="noopener"><img src="../art/powered-by-bgg.png" alt="Powered by BGG" height="40"></a></p>
|
||||
<p>The Game Shelves · cataloged from shelf photos by
|
||||
<a href="https://git.kestrelsnest.social/eric/bggpipe"
|
||||
rel="noopener">bggpipe</a></p>
|
||||
<p>Game data and cover images from <a href="https://boardgamegeek.com"
|
||||
rel="noopener">BoardGameGeek</a>. BoardGameGeek and BGG are trademarks of
|
||||
BoardGameGeek, LLC; this page is an independent project, not affiliated
|
||||
with or endorsed by BoardGameGeek.</p>
|
||||
</footer>
|
||||
</main>
|
||||
</html>
|
||||
@@ -0,0 +1,37 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Age of Renaissance · The Game Shelves</title>
|
||||
<meta name="description" content="Age of Renaissance in The Game Shelves">
|
||||
<meta property="og:title" content="Age of Renaissance · The Game Shelves">
|
||||
<meta property="og:image" content="../art/26-28607.jpg">
|
||||
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>🎲</text></svg>">
|
||||
<link rel="stylesheet" href="../style.css">
|
||||
<header class="site"><a href=".././">The Game Shelves</a>
|
||||
<span class="sub">a board game library</span></header>
|
||||
<main>
|
||||
<p><a href="../">← the whole library</a> · <a href="https://boardgamegeek.com/boardgame/26" rel="noopener">view on BGG ↗</a></p>
|
||||
<h1>Age of Renaissance <span class="meta">(1996)</span></h1>
|
||||
<div class="detail">
|
||||
<div class="art"><img src="../art/26-28607.jpg" alt="box art for Age of Renaissance"></div>
|
||||
<div class="facts"><div class="factrow"><b>players</b> 3–6 players (best at 5, 6)</div><div class="factrow"><b>playing time</b> 120–300 min</div><div class="factrow"><b>ages</b> 12+</div><div class="factrow"><b>weight</b> 3.85 / 5</div><div class="factrow"><b>BGG rank</b> 2126</div><div class="factrow"><b>BGG rating</b> 7.09</div><div class="factrow"><b>designers</b> <span class="chip">Don Greenwood</span> <span class="chip">Jared Scarborough</span></div><div class="factrow"><b>artists</b> <span class="chip">Stephen Langmead</span> <span class="chip">Kurt Miller</span></div><div class="factrow"><b>publishers</b> <span class="chip">The Avalon Hill Game Co</span> <span class="chip">Descartes Editeur</span></div><div class="factrow"><b>categories</b> <span class="chip">Civilization</span> <span class="chip">Economic</span> <span class="chip">Medieval</span> <span class="chip">Renaissance</span></div><div class="factrow"><b>mechanics</b> <span class="chip">Area Movement</span> <span class="chip">Auction / Bidding</span> <span class="chip">Simulation</span></div></div>
|
||||
</div>
|
||||
<div class="card"><div class="factrow"><b>my copy</b> English edition · 1996</div><div class="factrow"><b>publishers</b> <span class="chip">The Avalon Hill Game Co</span></div><div class="factrow"><b>languages</b> <span class="chip">English</span></div></div>
|
||||
<div class="card desc">A game that is often described as a semi-sequel to Civilization.
|
||||
|
||||
Players attempt to build up their cultures during the Middle Ages and Renaissance periods. The game features a card deck that holds events, payout cards, and famous leaders of the particular eras. Although all cards will turn up somewhere during the game, the timing and position in which they will be played is crucial and will ensure a different game every time.
|
||||
|
||||
The main goal of each city-state is to gain points in the form of technical advances, while keeping their culture's misery level as low as possible. The payout structure is similar to those of Civilization and Advanced Civilization in that more resources of the same type yield increasingly higher payouts. This time particular payouts are triggered by card play for every player at once. An ingenious and very smooth working “combat” system is implemented to determine province – and thus resource - control.</div><div class="card"><div class="factrow"><b>where it lives</b> Library Kallax · A1</div></div>
|
||||
<footer>
|
||||
<p><a class="pbb" href="https://boardgamegeek.com" rel="noopener"><img src="../art/powered-by-bgg.png" alt="Powered by BGG" height="40"></a></p>
|
||||
<p>The Game Shelves · cataloged from shelf photos by
|
||||
<a href="https://git.kestrelsnest.social/eric/bggpipe"
|
||||
rel="noopener">bggpipe</a></p>
|
||||
<p>Game data and cover images from <a href="https://boardgamegeek.com"
|
||||
rel="noopener">BoardGameGeek</a>. BoardGameGeek and BGG are trademarks of
|
||||
BoardGameGeek, LLC; this page is an independent project, not affiliated
|
||||
with or endorsed by BoardGameGeek.</p>
|
||||
</footer>
|
||||
</main>
|
||||
</html>
|
||||
@@ -0,0 +1,41 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Agricola · The Game Shelves</title>
|
||||
<meta name="description" content="Agricola in The Game Shelves">
|
||||
<meta property="og:title" content="Agricola · The Game Shelves">
|
||||
<meta property="og:image" content="../art/31260-297589.jpg">
|
||||
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>🎲</text></svg>">
|
||||
<link rel="stylesheet" href="../style.css">
|
||||
<header class="site"><a href=".././">The Game Shelves</a>
|
||||
<span class="sub">a board game library</span></header>
|
||||
<main>
|
||||
<p><a href="../">← the whole library</a> · <a href="https://boardgamegeek.com/boardgame/31260" rel="noopener">view on BGG ↗</a></p>
|
||||
<h1>Agricola <span class="meta">(2007)</span></h1>
|
||||
<div class="detail">
|
||||
<div class="art"><img src="../art/31260-297589.jpg" alt="box art for Agricola"></div>
|
||||
<div class="facts"><div class="factrow"><b>players</b> 1–5 players (best at 3, 4)</div><div class="factrow"><b>playing time</b> 30–150 min</div><div class="factrow"><b>ages</b> 12+</div><div class="factrow"><b>weight</b> 3.64 / 5</div><div class="factrow"><b>BGG rank</b> 64</div><div class="factrow"><b>BGG rating</b> 7.86</div><div class="factrow"><b>designers</b> <span class="chip">Uwe Rosenberg</span></div><div class="factrow"><b>artists</b> <span class="chip">Klemens Franz</span></div><div class="factrow"><b>publishers</b> <span class="chip">Lookout Games</span> <span class="chip">999 Games</span> <span class="chip">Brain Games</span> <span class="chip">Compaya.hu: Gamer Café Kft.</span> <span class="chip">Devir</span> <span class="chip">Filosofia Éditions</span></div><div class="factrow"><b>categories</b> <span class="chip">Animals</span> <span class="chip">Economic</span> <span class="chip">Farming</span></div><div class="factrow"><b>mechanics</b> <span class="chip">Automatic Resource Growth</span> <span class="chip">Closed Drafting</span> <span class="chip">Enclosure</span> <span class="chip">Increase Value of Unchosen Resources</span> <span class="chip">Solo / Solitaire Game</span> <span class="chip">Turn Order: Claim Action</span> <span class="chip">Worker Placement</span></div></div>
|
||||
</div>
|
||||
<div class="card"><div class="factrow"><b>my copy</b> Z-Man Games English fifth edition · 2013</div><div class="factrow"><b>publishers</b> <span class="chip">Filosofia Éditions</span> <span class="chip">Lookout Games</span> <span class="chip">Z-Man Games</span></div><div class="factrow"><b>languages</b> <span class="chip">English</span></div></div>
|
||||
<div class="card desc">In Agricola, you're a farmer in a wooden shack with your spouse and little else. At first, on a turn, your family gets to take only two actions, one for you and one for your spouse, as might be found among all the possibilities on a farm: plowing fields; collecting materials; building fences, and so on. There are numerous choices available, and while the game progresses you'll have more and more, as each round a new action card is flipped over, offering one more possible action. You might think about having kids in order to get more work accomplished but first you'll need to expand your house to make room - and what are you going to feed all the little rug rats?
|
||||
|
||||
The game supports many levels of complexity, mainly through the distribution of cards which represent Minor Improvements and Occupations. In the beginner's version (called the Family Variant in the U.S. release) these cards are not used at all. For advanced play, the U.S. release includes three levels of both types of cards; Basic (E-deck), Interactive (I-deck), and Complex (K-deck), and the rulebook encourages players to experiment with the various decks and mixtures thereof. Aftermarket decks such as the Z-Deck and the L-Deck also exist. Each player starts with a hand of 7 Occupation cards (of more than 160 total) and 7 Minor Improvement cards (of more than 140 total) that he/she may use during the game if they fit into his/her strategy.
|
||||
|
||||
Agricola is a turn-based game, and the problem to be overcome is that each available action can be taken by only one player each round so it's important to be careful about your choices. There are countless strategies, some of which depend on your hand of cards.
|
||||
|
||||
The winner is the player with the most Victory Points from the Improvements made on his farm.
|
||||
|
||||
-description originally from BoardgameNews</div><div class="card"><div class="factrow"><b>where it lives</b> Library Kallax · D3</div></div>
|
||||
<footer>
|
||||
<p><a class="pbb" href="https://boardgamegeek.com" rel="noopener"><img src="../art/powered-by-bgg.png" alt="Powered by BGG" height="40"></a></p>
|
||||
<p>The Game Shelves · cataloged from shelf photos by
|
||||
<a href="https://git.kestrelsnest.social/eric/bggpipe"
|
||||
rel="noopener">bggpipe</a></p>
|
||||
<p>Game data and cover images from <a href="https://boardgamegeek.com"
|
||||
rel="noopener">BoardGameGeek</a>. BoardGameGeek and BGG are trademarks of
|
||||
BoardGameGeek, LLC; this page is an independent project, not affiliated
|
||||
with or endorsed by BoardGameGeek.</p>
|
||||
</footer>
|
||||
</main>
|
||||
</html>
|
||||
@@ -0,0 +1,41 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Alice is Missing: Silent Falls · The Game Shelves</title>
|
||||
<meta name="description" content="Alice is Missing: Silent Falls in The Game Shelves">
|
||||
<meta property="og:title" content="Alice is Missing: Silent Falls · The Game Shelves">
|
||||
<meta property="og:image" content="../art/380459-743266.jpg">
|
||||
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>🎲</text></svg>">
|
||||
<link rel="stylesheet" href="../style.css">
|
||||
<header class="site"><a href=".././">The Game Shelves</a>
|
||||
<span class="sub">a board game library</span></header>
|
||||
<main>
|
||||
<p><a href="../">← the whole library</a> · <a href="https://rpggeek.com/rpgitem/380459" rel="noopener">view on RPGGeek ↗</a></p>
|
||||
<h1>Alice is Missing: Silent Falls <span class="meta">(2023)</span></h1>
|
||||
<div class="detail">
|
||||
<div class="art"><img src="../art/380459-743266.jpg" alt="box art for Alice is Missing: Silent Falls"></div>
|
||||
<div class="facts"><div class="factrow"><b>BGG rating</b> 7.43</div><div class="factrow"><b>designers</b> <span class="chip">Banana Chan</span> <span class="chip">Spenser Starke</span></div><div class="factrow"><b>artists</b> <span class="chip">Julianne Griepp</span></div><div class="factrow"><b>publishers</b> <span class="chip">Alis Games</span> <span class="chip">Devir</span> <span class="chip">Hunters Entertainment</span> <span class="chip">Origames</span> <span class="chip">Renegade Game Studios</span></div><div class="factrow"><b>categories</b> <span class="chip">Crime (Mystery / Detective / Noir)</span> <span class="chip">Sourcebook (rules/options to enhance play)</span></div><div class="factrow"><b>mechanics</b> <span class="chip">Cards (Specialized)</span> <span class="chip">Description Based (Narrative more so than Dice)</span></div></div>
|
||||
</div>
|
||||
<div class="card"><div class="factrow"><b>my copy</b> Alice ha desaparecido: Expansión Silent Falls · 2024</div><div class="factrow"><b>languages</b> <span class="chip">Spanish</span></div></div>
|
||||
<div class="card desc">Publisher's blurb:
|
||||
|
||||
The Silent Falls expansion allows players to rediscover the game with additional cards that facilitate evocative new stories.
|
||||
|
||||
With the Silent Falls expansion, players are provided an opportunity to explore brand new stories with additional Suspect, Location, Character, Searching, and Clue cards, as well as new Relationship cards that help you to deepen your connections to the other characters.
|
||||
|
||||
Players have the opportunity to explore uncharted locations such as the dark, echoing spaces of the Caloma Caves, confront new Suspects like the distant father John Briarwood, and uncover thrilling new conclusions to Alice’s disappearance as they reveal the new 10-minute clue cards.
|
||||
|
||||
The Silent Falls expansion introduces 38 new cards in total, which is more than half the amount of cards in the original Alice is Missing. 4 new playable characters, 2 new Relationship cards with a total of 12 new prompts, 3 new Suspects, 3 new Locations, 4 new Searching cards and 22 new Clue cards (two additional Clue cards for each standard timestamp, as well as three additional 10 minute cards, and a replacement 90 minute card).</div><div class="card"><div class="factrow"><b>where it lives</b> Library Left Shelving · D1</div></div>
|
||||
<footer>
|
||||
<p><a class="pbb" href="https://boardgamegeek.com" rel="noopener"><img src="../art/powered-by-bgg.png" alt="Powered by BGG" height="40"></a></p>
|
||||
<p>The Game Shelves · cataloged from shelf photos by
|
||||
<a href="https://git.kestrelsnest.social/eric/bggpipe"
|
||||
rel="noopener">bggpipe</a></p>
|
||||
<p>Game data and cover images from <a href="https://boardgamegeek.com"
|
||||
rel="noopener">BoardGameGeek</a>. BoardGameGeek and BGG are trademarks of
|
||||
BoardGameGeek, LLC; this page is an independent project, not affiliated
|
||||
with or endorsed by BoardGameGeek.</p>
|
||||
</footer>
|
||||
</main>
|
||||
</html>
|
||||
@@ -0,0 +1,45 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Alice is Missing · The Game Shelves</title>
|
||||
<meta name="description" content="Alice is Missing in The Game Shelves">
|
||||
<meta property="og:title" content="Alice is Missing · The Game Shelves">
|
||||
<meta property="og:image" content="../art/311654.png">
|
||||
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>🎲</text></svg>">
|
||||
<link rel="stylesheet" href="../style.css">
|
||||
<header class="site"><a href=".././">The Game Shelves</a>
|
||||
<span class="sub">a board game library</span></header>
|
||||
<main>
|
||||
<p><a href="../">← the whole library</a> · <a href="https://rpggeek.com/rpgitem/311654" rel="noopener">view on RPGGeek ↗</a></p>
|
||||
<h1>Alice is Missing <span class="meta">(2020)</span></h1>
|
||||
<div class="detail">
|
||||
<div class="art"><img src="../art/311654.png" alt="box art for Alice is Missing"></div>
|
||||
<div class="facts"><div class="factrow"><b>weight</b> 1.96 / 5</div><div class="factrow"><b>BGG rating</b> 7.58</div><div class="factrow"><b>designers</b> <span class="chip">Spenser Starke</span></div><div class="factrow"><b>artists</b> <span class="chip">Caleb Cleveland</span> <span class="chip">Christopher De La Rosa</span> <span class="chip">Julianne Griepp</span></div><div class="factrow"><b>publishers</b> <span class="chip">Alis Games</span> <span class="chip">Choo Choo Games</span> <span class="chip">Devir</span> <span class="chip">Hunters Entertainment</span> <span class="chip">Origames</span> <span class="chip">Raven Distribution</span></div><div class="factrow"><b>categories</b> <span class="chip">Childhood</span> <span class="chip">Crime (Mystery / Detective / Noir)</span> <span class="chip">Social (Relationships / Romance)</span> <span class="chip">Core Rules (min needed to play)</span></div><div class="factrow"><b>mechanics</b> <span class="chip">Cards (Specialized)</span> <span class="chip">Description Based (Narrative more so than Dice)</span></div></div>
|
||||
</div>
|
||||
|
||||
<div class="card desc">From the kickstarter:
|
||||
|
||||
Alice is Missing, a silent role-playing game about the disappearance of Alice Briarwood, a high school junior in the small town of Silent Falls.
|
||||
...
|
||||
The game is played live and without verbal communication. Players inhabit their character for the entirety of the 90-minute play session, and instead of speaking, send text messages back and forth to the other characters in a group chat, as well as individually, as though they aren’t in the same place together.
|
||||
|
||||
Publisher's blurb:
|
||||
|
||||
The game is played live and without verbal communication. Players inhabit their character for the entirety of the 90-minute play session, and instead of speaking, send text messages back and forth to the other characters in a group chat, as well as individually, as though they aren’t in the same place together.
|
||||
|
||||
Haunting beautiful, deeply personal, and highly innovative Alice is Missing puts a strong focus on the emotional engagement between players, immersing them in a tense, dramatic mystery that unfolds organically through the text messages they send to one another. Right at home with games like Life Is Strange, Gone Home, Oxenfree, and Firewatch, it’s designed to feel as much like an event-style experience as it does a role-playing game.
|
||||
|
||||
A microbadge is available</div><div class="card"><div class="factrow"><b>where it lives</b> Library Left Shelving · D1</div></div>
|
||||
<footer>
|
||||
<p><a class="pbb" href="https://boardgamegeek.com" rel="noopener"><img src="../art/powered-by-bgg.png" alt="Powered by BGG" height="40"></a></p>
|
||||
<p>The Game Shelves · cataloged from shelf photos by
|
||||
<a href="https://git.kestrelsnest.social/eric/bggpipe"
|
||||
rel="noopener">bggpipe</a></p>
|
||||
<p>Game data and cover images from <a href="https://boardgamegeek.com"
|
||||
rel="noopener">BoardGameGeek</a>. BoardGameGeek and BGG are trademarks of
|
||||
BoardGameGeek, LLC; this page is an independent project, not affiliated
|
||||
with or endorsed by BoardGameGeek.</p>
|
||||
</footer>
|
||||
</main>
|
||||
</html>
|
||||
@@ -0,0 +1,43 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>...and then, we held hands. · The Game Shelves</title>
|
||||
<meta name="description" content="...and then, we held hands. in The Game Shelves">
|
||||
<meta property="og:title" content="...and then, we held hands. · The Game Shelves">
|
||||
<meta property="og:image" content="../art/153999-280845.jpg">
|
||||
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>🎲</text></svg>">
|
||||
<link rel="stylesheet" href="../style.css">
|
||||
<header class="site"><a href=".././">The Game Shelves</a>
|
||||
<span class="sub">a board game library</span></header>
|
||||
<main>
|
||||
<p><a href="../">← the whole library</a> · <a href="https://boardgamegeek.com/boardgame/153999" rel="noopener">view on BGG ↗</a></p>
|
||||
<h1>...and then, we held hands. <span class="meta">(2015)</span></h1>
|
||||
<div class="detail">
|
||||
<div class="art"><img src="../art/153999-280845.jpg" alt="box art for ...and then, we held hands."></div>
|
||||
<div class="facts"><div class="factrow"><b>players</b> 2 players (best at 2)</div><div class="factrow"><b>playing time</b> 30–45 min</div><div class="factrow"><b>ages</b> 12+</div><div class="factrow"><b>weight</b> 1.76 / 5</div><div class="factrow"><b>BGG rank</b> 6006</div><div class="factrow"><b>BGG rating</b> 5.99</div><div class="factrow"><b>designers</b> <span class="chip">David Chircop</span> <span class="chip">Yannick Massa</span></div><div class="factrow"><b>artists</b> <span class="chip">Marie Cardouat</span> <span class="chip">David Chircop</span> <span class="chip">Todd Sanders</span></div><div class="factrow"><b>publishers</b> <span class="chip">LudiCreations</span> <span class="chip">(Web published)</span></div><div class="factrow"><b>categories</b> <span class="chip">Card Game</span> <span class="chip">Print & Play</span></div><div class="factrow"><b>mechanics</b> <span class="chip">Communication Limits</span> <span class="chip">Cooperative Game</span> <span class="chip">Hand Management</span> <span class="chip">Layering</span> <span class="chip">Melding and Splaying</span> <span class="chip">Point to Point Movement</span></div></div>
|
||||
</div>
|
||||
<div class="card"><div class="factrow"><b>my copy</b> English edition · 2015</div><div class="factrow"><b>publishers</b> <span class="chip">LudiCreations</span></div><div class="factrow"><b>languages</b> <span class="chip">English</span></div></div>
|
||||
<div class="card desc">...and then we held hands. is a co-operative game about finding balance. To win, the two players must complete objectives and reach the center of the board.
|
||||
|
||||
The players take turns trying to fulfill the current common emotional objective by discarding emotion cards to move from node to node. They must do this without verbal communication, empathizing and always considering each other's situation when making a move.
|
||||
|
||||
A player can use their own cards or their partner's, but if their move causes their partner to be unable to move, the players lose and the game ends. While moving from node to node, their balance shifts, and they are not able to refill their hand.
|
||||
|
||||
The game features a card-splaying mechanism in which players change their "perspective" to reveal a new set of options.
|
||||
|
||||
The players win by meeting in the center while in a balanced state and within one turn of each other - something quite difficult, and therefore very rewarding when achieved.
|
||||
|
||||
...and then we held hands. features art by beloved French illustrator Marie Cardouat, known for the art found in beautiful games such as Dixit, Steam Park, and Marrakech.</div><div class="card"><div class="factrow"><b>where it lives</b> Library Kallax · B4</div></div>
|
||||
<footer>
|
||||
<p><a class="pbb" href="https://boardgamegeek.com" rel="noopener"><img src="../art/powered-by-bgg.png" alt="Powered by BGG" height="40"></a></p>
|
||||
<p>The Game Shelves · cataloged from shelf photos by
|
||||
<a href="https://git.kestrelsnest.social/eric/bggpipe"
|
||||
rel="noopener">bggpipe</a></p>
|
||||
<p>Game data and cover images from <a href="https://boardgamegeek.com"
|
||||
rel="noopener">BoardGameGeek</a>. BoardGameGeek and BGG are trademarks of
|
||||
BoardGameGeek, LLC; this page is an independent project, not affiliated
|
||||
with or endorsed by BoardGameGeek.</p>
|
||||
</footer>
|
||||
</main>
|
||||
</html>
|
||||
|
After Width: | Height: | Size: 5.3 KiB |
|
After Width: | Height: | Size: 692 KiB |
|
After Width: | Height: | Size: 9.8 KiB |
|
After Width: | Height: | Size: 111 KiB |
|
After Width: | Height: | Size: 7.0 KiB |
|
After Width: | Height: | Size: 1.6 MiB |
|
After Width: | Height: | Size: 9.7 KiB |
|
After Width: | Height: | Size: 512 KiB |
|
After Width: | Height: | Size: 9.7 KiB |
|
After Width: | Height: | Size: 119 KiB |
|
After Width: | Height: | Size: 8.7 KiB |
|
After Width: | Height: | Size: 146 KiB |
|
After Width: | Height: | Size: 6.5 KiB |
|
After Width: | Height: | Size: 83 KiB |
|
After Width: | Height: | Size: 5.4 KiB |
|
After Width: | Height: | Size: 126 KiB |
|
After Width: | Height: | Size: 7.2 KiB |
|
After Width: | Height: | Size: 173 KiB |
|
After Width: | Height: | Size: 5.6 KiB |
|
After Width: | Height: | Size: 49 KiB |
|
After Width: | Height: | Size: 6.5 KiB |
|
After Width: | Height: | Size: 133 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 1.1 MiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 371 KiB |
|
After Width: | Height: | Size: 22 KiB |
|
After Width: | Height: | Size: 692 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 242 KiB |
|
After Width: | Height: | Size: 4.1 KiB |
|
After Width: | Height: | Size: 63 KiB |
|
After Width: | Height: | Size: 8.9 KiB |
|
After Width: | Height: | Size: 116 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 227 KiB |
|
After Width: | Height: | Size: 6.4 KiB |
|
After Width: | Height: | Size: 72 KiB |
|
After Width: | Height: | Size: 8.4 KiB |
|
After Width: | Height: | Size: 61 KiB |
|
After Width: | Height: | Size: 7.3 KiB |
|
After Width: | Height: | Size: 333 KiB |
|
After Width: | Height: | Size: 4.4 KiB |
|
After Width: | Height: | Size: 423 KiB |
|
After Width: | Height: | Size: 9.4 KiB |
|
After Width: | Height: | Size: 379 KiB |
|
After Width: | Height: | Size: 8.3 KiB |
|
After Width: | Height: | Size: 584 KiB |
|
After Width: | Height: | Size: 2.7 KiB |
|
After Width: | Height: | Size: 133 KiB |
|
After Width: | Height: | Size: 6.3 KiB |
|
After Width: | Height: | Size: 469 KiB |
|
After Width: | Height: | Size: 9.4 KiB |
|
After Width: | Height: | Size: 500 KiB |
|
After Width: | Height: | Size: 6.0 KiB |