<?xml version="1.0" encoding="utf-8" standalone="yes"?><?xml-stylesheet type="text/xsl" href="https://mrmatt.io/rss.xsl"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:media="http://search.yahoo.com/mrss/"><channel><title>Matt Walker</title><link>https://mrmatt.io/</link><description>Matt Walker's personal site — writing about software engineering, AI, cloud infrastructure, photography, homelab projects, and hobbies from the Baltimore/DC area.</description><generator>Hugo</generator><language>en-us</language><lastBuildDate>Sat, 04 Apr 2026 00:00:00 -0400</lastBuildDate><atom:link href="https://mrmatt.io/index.xml" rel="self" type="application/rss+xml"/><item><title>Sculpted Bonsai Tree</title><link>https://mrmatt.io/photography/2026-04-04-sculpted-bonsai-tree/</link><pubDate>Sat, 04 Apr 2026 00:00:00 -0400</pubDate><guid>https://mrmatt.io/photography/2026-04-04-sculpted-bonsai-tree/</guid><description>This expertly shaped bonsai exemplifies the Japanese art of miniature tree cultivation, with its carefully balanced canopy and visible trunk structure. The vibrant green foliage and intricate layering showcase years of patient pruning and care, transforming a living tree into a living sculpture.</description><content:encoded/><media:content url="https://mrmatt.io/photography/2026-04-04-sculpted-bonsai-tree/photo_hu_eb50e6488acb1309.webp" medium="image"/></item><item><title>Building PitClaw: Why I'm Building My Own Smoker Controller</title><link>https://mrmatt.io/posts/building-pitclaw-why-im-building-my-own-smoker-controller/</link><pubDate>Wed, 25 Mar 2026 00:00:00 -0400</pubDate><guid>https://mrmatt.io/posts/building-pitclaw-why-im-building-my-own-smoker-controller/</guid><description>PitClaw is an in-progress attempt to build an open smoker controller that carries forward what I loved about HeaterMeter and freshens up the experience.</description><content:encoded>&lt;blockquote&gt;
&lt;p&gt;This is the first note in what may become a larger PitClaw build log.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This post is a little early because &lt;a href="https://github.com/MrMatt57/pitclaw"&gt;PitClaw&lt;/a&gt; is not done yet.&lt;/p&gt;
&lt;p&gt;I have a working simulator. I have a real web UI. I have predictive graphs, PID control logic, OTA updates, a touchscreen dashboard, and a mostly soldered pile of parts on my desk. What I do &lt;strong&gt;not&lt;/strong&gt; have, at least as I write this, is a finished controller sitting on a smoker holding a perfect brisket temp for twelve hours.&lt;/p&gt;
&lt;p&gt;So this is not a victory lap. It is more like building in public&amp;hellip; with a decent chance of scraping some plastic, reprinting parts, misreading a schematic, and learning something useful anyway.&lt;/p&gt;
&lt;p&gt;&lt;img alt="PitClaw web interface showing live pit and meat temperatures, graphing, controls, and predicted finish time" loading="lazy" src="https://mrmatt.io/posts/building-pitclaw-why-im-building-my-own-smoker-controller/pitclaw-ui.png"&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;The software side is very real already. The hardware side is still earning its keep.&lt;/em&gt;&lt;/p&gt;
&lt;h3 id="the-backstory"&gt;The backstory&lt;/h3&gt;
&lt;p&gt;I have been using a &lt;a href="https://github.com/CapnBry/HeaterMeter/wiki/An-Introduction-to-LinkMeter---HeaterMeter"&gt;HeaterMeter&lt;/a&gt; on my ugly drum smoker for years, and I mean that as a compliment. It is one of those projects that sits right at the intersection of barbecue, electronics, software, and time behind a soldering iron&amp;hellip; which is pretty much my kind of thing. I still use one today, and it has served me really well.&lt;/p&gt;
&lt;p&gt;The temperature control is solid. The graphs are useful. It does the thing I care about most, which is giving me enough information to understand the cook instead of just blinking &amp;ldquo;225&amp;rdquo; and hoping for the best.&lt;/p&gt;
&lt;p&gt;And honestly, that experience is a big part of why PitClaw exists. Using HeaterMeter for years taught me what I actually want out of a smoker controller.&lt;/p&gt;
&lt;p&gt;I am not trying to replace it out of spite. I am just ready for a refresh. I want the same spirit, the same fun, and the same hands-on hobby energy&amp;hellip; just in a package that feels more modern, more approachable, and more like something I would hand to another barbecue nerd without needing a twenty-minute preamble.&lt;/p&gt;
&lt;p&gt;Before I signed myself up for another hardware project, I did the obvious thing and looked at what I could just buy.&lt;/p&gt;
&lt;p&gt;I bought the &lt;a href="https://www.thermoworks.com/products/signals"&gt;ThermoWorks Signals&lt;/a&gt; and Billows combo because the hardware is good and ThermoWorks probes are, in my opinion, the best. I figured maybe the modern version of what I wanted was already sitting on the shelf and I could skip the whole &amp;ldquo;build my own controller&amp;rdquo; detour.&lt;/p&gt;
&lt;p&gt;That is not how it went.&lt;/p&gt;
&lt;p&gt;The Billows hardware was nice, but I found myself missing the software from the open-source build I was already using. The big one was PID and the ability to actually hold temperature well. If the thing cannot hold temperature, then it is not doing the job. I also wanted predictive info. I wanted to see where the cook was going. I wanted something smarter than &amp;ldquo;fan on, fan off.&amp;rdquo; More than anything, I wanted a controller that made barbecue feel a little more fun and a little less like I was settling.&lt;/p&gt;
&lt;p&gt;That was the moment I realized I was probably going to end up building my own.&lt;/p&gt;
&lt;h3 id="why-now"&gt;Why now&lt;/h3&gt;
&lt;p&gt;The funny part is I have wanted to build something like this for a long time. The idea was never the problem. Time was the problem.&lt;/p&gt;
&lt;p&gt;A few years ago this would have stayed in the category of &amp;ldquo;projects I would love to build someday.&amp;rdquo; Embedded firmware, web UI, simulation, hardware, enclosure design, documentation&amp;hellip; that is a lot of surface area for one hobby project.&lt;/p&gt;
&lt;p&gt;What changed was &lt;a href="https://mrmatt.io/posts/building-mrmatt-io-specs/"&gt;spec-driven development&lt;/a&gt; and agent-assisted engineering. Once I could write down what I wanted clearly enough, then iterate with an agent against that spec, this stopped feeling like a someday project and started feeling like something I could actually pull off myself.&lt;/p&gt;
&lt;p&gt;That changed the order of operations too. I did not have to start with a bench full of parts. I could start with behavior. I could have it build a simulator, then a hardware emulator, then have the emulator emit the API the real device would use. That let me work on the real software early, before the physical build was ready.&lt;/p&gt;
&lt;p&gt;With that in mind, PitClaw is my attempt to build the smoker controller I wish existed:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Open source&lt;/li&gt;
&lt;li&gt;Modern and easy to connect to&lt;/li&gt;
&lt;li&gt;Good enough hardware to leave outside on a real cook&lt;/li&gt;
&lt;li&gt;Smart enough software to show prediction, not just current temperature&lt;/li&gt;
&lt;li&gt;Friendly to low-voltage and battery-powered setups for camping and travel cooks&lt;/li&gt;
&lt;li&gt;Simple enough that someone else could actually build it without making it their whole personality for a month&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That last one matters a lot to me. I like the hobby part. I like the soldering iron, the tuning, the weird little hardware decisions, and the barbecue excuse to care about all of it. The goal here is not to reject that fun. It is to take it forward and make something more approachable.&lt;/p&gt;
&lt;h3 id="the-software-sprint"&gt;The software sprint&lt;/h3&gt;
&lt;p&gt;Once I had a clear enough spec, the software moved waaay faster than I expected. First I had it build a simulator. Then a hardware emulator. Then I had the emulator emit the same API the real device would use. That let me do the full web layer early, while the physical hardware was still imaginary. From there it was surprisingly straightforward to build out PID control, prediction, session logging, OTA updates, and the touchscreen flow.&lt;/p&gt;
&lt;p&gt;The repo history tells that story pretty clearly.&lt;/p&gt;
&lt;div style="position:relative; width:100%; aspect-ratio:3/2; margin:2rem 0;"&gt;
&lt;canvas id="pitclaw-velocity-chart"&gt;&lt;/canvas&gt;
&lt;/div&gt;
&lt;p&gt;From February 11 to February 18, the project went from scaffold to simulator, touchscreen UI, prediction, OTA, release workflow, assembly docs, and schematic cleanup. The software sprint was real. The chart is counting non-merge commits by day, grouped loosely into software, project polish, and hardware work.&lt;/p&gt;
&lt;h3 id="then-the-project-touched-the-real-world"&gt;Then the project touched the real world&lt;/h3&gt;
&lt;p&gt;This is where things got humbling.&lt;/p&gt;
&lt;p&gt;Software is happy to pretend. Hardware is not.&lt;/p&gt;
&lt;p&gt;In the simulator, the touchscreen always fits. The connector is always the connector you imagined. The enclosure hole is exactly where you drew it. The cable is the one the model said to buy. None of that survives first contact with a bench full of parts.&lt;/p&gt;
&lt;p&gt;I have been through multiple 3D print iterations already just trying to get fit, clearances, and panel holes right. I still need to take a picture of the pile of failed prints because it deserves to be documented. There is a whole little plastic graveyard forming over here.&lt;/p&gt;
&lt;p&gt;The wiring side slowed me down too. The first pass at the plan was honestly pretty good, but I hit some friction reading the schematic and translating that into actual parts on the bench. Then there was the connector issue. Claude was very confident that the display cable I needed was a JST. It was not. The WT32-SC01 Plus needed a tiny 1.25mm PicoBlade-style cable instead. I lost time there until I switched tools, got the right part identified, and ordered what I actually needed.&lt;/p&gt;
&lt;p&gt;That is not a complaint, by the way. That is the story.&lt;/p&gt;
&lt;p&gt;The software phase made me feel like embedded projects had suddenly gotten cheap. The hardware phase reminded me that atoms still have opinions.&lt;/p&gt;
&lt;h3 id="where-it-stands"&gt;Where it stands&lt;/h3&gt;
&lt;p&gt;The part I find most interesting right now is how uneven the project has been in a good way. The software side is much further along than I expected. The simulator is real. The web UI is real. The prediction and control logic are real. I can see the shape of the finished thing very clearly now.&lt;/p&gt;
&lt;p&gt;The hardware side has been slower, which is not shocking&amp;hellip; it is just the nature of physical projects. Connectors matter. Schematic details matter. Printed parts need another iteration. Bench time matters. The remaining work is not mysterious anymore, though. It is concrete.&lt;/p&gt;
&lt;p&gt;The immediate target is the &lt;a href="https://www.weber.com/US/en/grills/charcoal-grills/smokey-mountain/smokey-mountain-cooker-smoker-18/721001.html"&gt;Weber Smokey Mountain 18&lt;/a&gt;. I am in the middle of switching over from my UDS, and the WSM feels like the sweet spot: common enough to matter, affordable enough to be realistic, and serious enough that good control actually helps.&lt;/p&gt;
&lt;p&gt;This is a side project, and realistically it moves forward on weekends and whenever I can make the time. That is fine. The point of writing about it now is just to document what I have built so far, what went surprisingly fast, and where the real friction has been.&lt;/p&gt;
&lt;h3 id="the-next-stretch"&gt;The next stretch&lt;/h3&gt;
&lt;p&gt;The next stretch is getting the physical hardware fully buttoned up and then tuning it on the Weber Smokey Mountain 18.&lt;/p&gt;
&lt;p&gt;That is the fun part now. The software gave me a head start. The challenge is making the physical build catch up&amp;hellip; more wiring, more print iterations, more bench work, then real cooks.&lt;/p&gt;
&lt;p&gt;More to follow&amp;hellip;&lt;/p&gt;
&lt;script src="https://mrmatt.io/js/chart.umd.min.js"&gt;&lt;/script&gt;
&lt;script&gt;
(function() {
 fetch('/data/pitclaw-velocity.json')
 .then(function(r) { return r.json(); })
 .then(function(data) { renderChart(data); });

 function renderChart(data) {
 var isDark = document.documentElement.getAttribute('data-theme') === 'dark';
 var textColor = isDark ? '#e0e0e0' : '#090909';
 var gridColor = isDark ? 'rgba(255,255,255,0.08)' : 'rgba(0,0,0,0.06)';
 var borderColor = isDark ? 'rgba(255,255,255,0.15)' : 'rgba(0,0,0,0.12)';

 new Chart(document.getElementById('pitclaw-velocity-chart'), {
 type: 'bar',
 data: {
 labels: data.labels,
 datasets: [
 {
 label: 'Software',
 data: data.software,
 backgroundColor: isDark ? '#8db9db' : '#4c81b2',
 borderColor: isDark ? '#8db9db' : '#4c81b2',
 borderWidth: 1
 },
 {
 label: 'Project Polish',
 data: data.project,
 backgroundColor: isDark ? '#888' : '#777',
 borderColor: isDark ? '#888' : '#777',
 borderWidth: 1
 },
 {
 label: 'Hardware',
 data: data.hardware,
 backgroundColor: isDark ? '#cfa46b' : '#a96b2d',
 borderColor: isDark ? '#cfa46b' : '#a96b2d',
 borderWidth: 1
 }
 ]
 },
 options: {
 responsive: true,
 maintainAspectRatio: false,
 animation: false,
 scales: {
 x: {
 stacked: true,
 grid: { color: gridColor },
 ticks: {
 color: textColor,
 font: { family: "'Roboto Slab', serif", size: 12, weight: '300' }
 }
 },
 y: {
 stacked: true,
 beginAtZero: true,
 grid: { color: gridColor },
 ticks: {
 color: textColor,
 precision: 0,
 font: { family: "'Roboto Slab', serif", size: 12, weight: '300' }
 }
 }
 },
 plugins: {
 legend: {
 labels: {
 color: textColor,
 font: { family: "'Roboto Slab', serif", size: 12, weight: '300' }
 }
 },
 tooltip: {
 backgroundColor: isDark ? '#1d1e20' : '#fff',
 titleColor: textColor,
 bodyColor: textColor,
 borderColor: borderColor,
 borderWidth: 1,
 padding: 10,
 bodyFont: { family: "'Roboto Slab', serif", size: 13 },
 titleFont: { family: "'Roboto Slab', serif", size: 13, weight: '300' }
 }
 }
 }
 });
 }
})();
&lt;/script&gt;</content:encoded><media:content url="https://mrmatt.io/images/MattWalker-avatar.png" medium="image"/></item><item><title>Give It Access</title><link>https://mrmatt.io/posts/give-it-access/</link><pubDate>Fri, 20 Mar 2026 00:00:00 -0400</pubDate><guid>https://mrmatt.io/posts/give-it-access/</guid><description>I asked AI to help me upgrade my PC. The first thing it found was a free performance boost I'd been sitting on for years. The lesson isn't about RAM speeds.</description><content:encoded>&lt;figure&gt;
 &lt;img loading="lazy" src="https://mrmatt.io/images/give-it-access-control-board.png"
 alt="Illustration of a human at a control panel flipping an Access Granted switch while an agent inside the machine holds up a glowing component labeled The Finding"/&gt; &lt;figcaption&gt;
 &lt;p&gt;You&amp;rsquo;re the control board. Let the agent do the inspecting.&lt;/p&gt;
 &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;I asked Claude to help me plan a PC upgrade. Pretty standard stuff&amp;hellip; &amp;ldquo;pull my specs and tell me what to upgrade.&amp;rdquo; It ran a handful of system commands, built a table of my components, and started making recommendations. GPU, motherboard, the usual suspects.&lt;/p&gt;
&lt;p&gt;But the first thing it said wasn&amp;rsquo;t &amp;ldquo;buy this.&amp;rdquo; It was &amp;ldquo;your RAM is already faster than what your motherboard is running it at.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;I have 32GB of G.Skill DDR4-3600. It&amp;rsquo;s been running at 3200MHz this entire time. All I need to do is flip on XMP in my BIOS and I get a free ~10% boost in memory-sensitive workloads. No purchase required. I&amp;rsquo;ve been leaving performance on the table for&amp;hellip; I don&amp;rsquo;t even know how long. Years, probably!&lt;/p&gt;
&lt;p&gt;That one finding floored me. Not because it&amp;rsquo;s complicated (it&amp;rsquo;s a single BIOS toggle), but because of what it took to get there. The AI read my actual system specs, cross-referenced the rated speed of my RAM sticks against the motherboard&amp;rsquo;s reported clock speed, noticed the gap, and flagged it before recommending I spend a single dollar.&lt;/p&gt;
&lt;h3 id="the-pattern"&gt;The pattern&lt;/h3&gt;
&lt;p&gt;This is the thing I keep coming back to. The interesting part isn&amp;rsquo;t that AI can answer questions. It&amp;rsquo;s what happens when you give it access to read something real.&lt;/p&gt;
&lt;p&gt;I didn&amp;rsquo;t paste my specs into a chat window. I didn&amp;rsquo;t type &amp;ldquo;I have DDR4-3600 RAM running at 3200, what should I do?&amp;rdquo; I wouldn&amp;rsquo;t have known to ask that. The agent pulled the data itself, and the insight fell out of the audit.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s the pattern. Give it access. Let it read. Let it cross-reference. The value isn&amp;rsquo;t in the answers to questions you already know to ask&amp;hellip; it&amp;rsquo;s in the things it notices that you didn&amp;rsquo;t think to look for.&lt;/p&gt;
&lt;h3 id="this-works-everywhere"&gt;This works everywhere&lt;/h3&gt;
&lt;p&gt;I keep running into this. I used to have AI write my code and then I&amp;rsquo;d commit it, write the commit message, create the PR myself. Standard workflow. Then I gave the agent access to the GitHub CLI. Now it commits, creates the PR, and when review comments come back&amp;hellip; it resolves those too. The whole loop that used to bounce between me and the tool just collapsed.&lt;/p&gt;
&lt;p&gt;Same thing with application logs. Give an agent direct access to your structured logging and it stops asking you to paste stack traces. It finds the error, does the root cause analysis, applies the fix, and tests it. What used to take a dozen back-and-forth iterations happens in one pass.&lt;/p&gt;
&lt;h3 id="stop-being-the-middleman"&gt;Stop being the middleman&lt;/h3&gt;
&lt;p&gt;In &lt;a href="https://mrmatt.io/posts/the-membrane/"&gt;The Membrane&lt;/a&gt;, I wrote that any time you find yourself copying and pasting information into or out of an agent, you&amp;rsquo;re doing work that&amp;rsquo;s inside the cell. The agent can probably access it directly if you let it.&lt;/p&gt;
&lt;p&gt;Most people are still doing exactly that. The agent asks for information and they go get it. &amp;ldquo;What motherboard do you have?&amp;rdquo; So you open System Information, find the answer, type it back. You&amp;rsquo;re a copy-paste relay. A human API.&lt;/p&gt;
&lt;p&gt;Stop doing that. When it asks you for something, push back. Say &amp;ldquo;go get it yourself.&amp;rdquo; You don&amp;rsquo;t have to give it full admin control&amp;hellip; you&amp;rsquo;re still the one approving every command, every permission. You&amp;rsquo;re the control board, not the messenger. And you&amp;rsquo;ll learn more watching it work than you would doing the fetching yourself.&lt;/p&gt;
&lt;p&gt;I asked for a PC upgrade plan and got a free one I didn&amp;rsquo;t know I had. Go give something access and see what comes back.&lt;/p&gt;
&lt;p&gt;Oh, and if you&amp;rsquo;re running DDR4-3600 at 3200MHz&amp;hellip; go enable XMP. It takes 30 seconds.&lt;/p&gt;</content:encoded><media:content url="https://mrmatt.io/images/MattWalker-avatar.png" medium="image"/></item><item><title>Autoresearch for Web Performance: 20% Faster Overnight</title><link>https://mrmatt.io/posts/autoresearch-webperf/</link><pubDate>Mon, 16 Mar 2026 00:00:00 -0400</pubDate><guid>https://mrmatt.io/posts/autoresearch-webperf/</guid><description>Adapting Karpathy's autoresearch pattern to web performance: an autonomous AI agent ran 200 Lighthouse experiments on my Hugo site, reducing worst-page LCP by 20%.</description><content:encoded>&lt;figure class="post-figure"&gt;
 &lt;img
 src="https://mrmatt.io/images/ai-experiment-rig.png"
 alt="Illustration of an autonomous AI experiment loop modifying, building, measuring, and evaluating website performance under human-defined constraints"
 loading="lazy"
 /&gt;
 &lt;figcaption&gt;
 Human strategy. Autonomous execution. Measured results.
 &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Andrej Karpathy released &lt;a href="https://github.com/karpathy/autoresearch"&gt;autoresearch&lt;/a&gt; in March 2026 &amp;ndash; a pattern for letting an AI agent autonomously run ML experiments in a loop. The human writes strategy in &lt;code&gt;program.md&lt;/code&gt;, the agent handles all code changes and evaluation. Modify → train → check loss → keep or discard → repeat.&lt;/p&gt;
&lt;p&gt;I adapted this to web performance. Instead of training a neural network, the agent modifies Hugo templates and CSS, builds the site, runs Lighthouse with mobile throttling, and keeps or discards each change based on whether Largest Contentful Paint improved. The hard constraint: &lt;strong&gt;zero visual changes&lt;/strong&gt; &amp;ndash; enforced by a structural DOM hash that auto-rejects any experiment that alters the page structure.&lt;/p&gt;
&lt;p&gt;Over one session, the agent ran 200 experiments. Here&amp;rsquo;s what happened.&lt;/p&gt;
&lt;h3 id="the-setup"&gt;The setup&lt;/h3&gt;
&lt;p&gt;The evaluation harness (&lt;code&gt;evaluate.py&lt;/code&gt;) does four things per experiment:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Build the site with &lt;code&gt;hugo --minify --gc&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Measure output file sizes by category&lt;/li&gt;
&lt;li&gt;Compute a structural hash of the body DOM across three pages (homepage, about, photography)&lt;/li&gt;
&lt;li&gt;Run Lighthouse CLI with mobile throttling (4x CPU slowdown, simulated 4G, 412px viewport)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The primary metric is &lt;code&gt;worst_lcp_ms&lt;/code&gt; &amp;ndash; the highest Largest Contentful Paint across those three pages. Lower is better, same as &lt;code&gt;val_bpb&lt;/code&gt; in the original autoresearch. Each experiment takes about two minutes.&lt;/p&gt;
&lt;p&gt;The agent was allowed to modify eight files: &lt;code&gt;extend_head.html&lt;/code&gt; (resource hints), &lt;code&gt;baseof.html&lt;/code&gt; (base template), &lt;code&gt;single.html&lt;/code&gt; (post template), &lt;code&gt;index.html&lt;/code&gt; (homepage), &lt;code&gt;photography/list.html&lt;/code&gt; (gallery), &lt;code&gt;footer.html&lt;/code&gt; (scripts), &lt;code&gt;hugo.toml&lt;/code&gt; (config), and &lt;code&gt;custom.css&lt;/code&gt; (performance-only CSS properties). It could not touch content, the theme, fonts, images, or any visual CSS.&lt;/p&gt;
&lt;h3 id="lcp-over-200-experiments"&gt;LCP over 200 experiments&lt;/h3&gt;
&lt;div style="position:relative; width:100%; aspect-ratio:3/2; margin:2rem 0;"&gt;
&lt;canvas id="lcp-timeline"&gt;&lt;/canvas&gt;
&lt;/div&gt;
&lt;p&gt;The trajectory tells the story. The first few experiments delivered the biggest gains, then returns diminished. The downward staircase pattern is characteristic of optimization &amp;ndash; early wins are large and obvious, later wins are marginal and noisy. The spikes are failed experiments that got discarded.&lt;/p&gt;
&lt;p&gt;Baseline was 2,638ms. The agent got it to a stable 2,109ms &amp;ndash; a &lt;strong&gt;20% reduction&lt;/strong&gt; in worst-page LCP on simulated mobile 4G.&lt;/p&gt;
&lt;h3 id="experiment-outcomes"&gt;Experiment outcomes&lt;/h3&gt;
&lt;div style="display:grid; grid-template-columns:1fr 1fr; gap:1.5rem; margin:2rem 0;"&gt;
&lt;div style="position:relative; width:100%; aspect-ratio:1/1;"&gt;
&lt;canvas id="outcomes-chart"&gt;&lt;/canvas&gt;
&lt;/div&gt;
&lt;div style="position:relative; width:100%; aspect-ratio:1/1;"&gt;
&lt;canvas id="category-chart"&gt;&lt;/canvas&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Of 200 experiments, 147 were kept (74%) and 47 discarded (23%). Four crashed the build entirely (usually by invalidating Hugo&amp;rsquo;s image cache, triggering a rebuild timeout). The keep rate is deceptively high &amp;ndash; many &amp;ldquo;kept&amp;rdquo; changes were neutral on LCP but improved secondary metrics like FCP or build size, and about 70 were quick-mode CSS commits that skipped the full Lighthouse evaluation entirely. Only about 15 experiments delivered a measurable LCP improvement.&lt;/p&gt;
&lt;h3 id="what-actually-moved-the-needle"&gt;What actually moved the needle&lt;/h3&gt;
&lt;div style="position:relative; width:100%; aspect-ratio:3/2; margin:2rem 0;"&gt;
&lt;canvas id="top-wins"&gt;&lt;/canvas&gt;
&lt;/div&gt;
&lt;p&gt;The biggest single improvement was &lt;strong&gt;adding &lt;code&gt;content-visibility: auto&lt;/code&gt;&lt;/strong&gt; to gallery thumbnails (-222ms). This told the browser to skip rendering off-screen images entirely, which freed up main-thread time during the initial paint.&lt;/p&gt;
&lt;p&gt;The most &lt;em&gt;surprising&lt;/em&gt; win was &lt;strong&gt;removing a font preload&lt;/strong&gt; (-146ms). The font &lt;code&gt;&amp;lt;link rel=&amp;quot;preload&amp;quot;&amp;gt;&lt;/code&gt; was competing with the LCP image for bandwidth on simulated 4G. Without it, the browser loaded the font just fine via the CSS &lt;code&gt;@font-face&lt;/code&gt; &amp;ndash; slightly later, but freeing up the critical path for the image that actually determined LCP.&lt;/p&gt;
&lt;p&gt;Image quality reduction (q80 → q60 on thumbnails) and matching the preload to the new quality level delivered another -76ms by shrinking the LCP image&amp;rsquo;s byte size.&lt;/p&gt;
&lt;h3 id="the-surprise-preloads-can-hurt"&gt;The surprise: preloads can hurt&lt;/h3&gt;
&lt;div style="position:relative; width:100%; aspect-ratio:3/2; margin:2rem 0;"&gt;
&lt;canvas id="preload-chart"&gt;&lt;/canvas&gt;
&lt;/div&gt;
&lt;p&gt;Resource preloading was the most volatile optimization category. The agent discovered that &lt;strong&gt;removing&lt;/strong&gt; the font preload saved 146ms, while &lt;strong&gt;removing&lt;/strong&gt; the gallery image preload cost 527ms. Preloading three thumbnails instead of one caused contention and added 72ms. Adding &lt;code&gt;fetchpriority=high&lt;/code&gt; to the font preload added 149ms.&lt;/p&gt;
&lt;p&gt;On a throttled 4G connection, every preload competes for the same limited bandwidth. The optimal strategy turned out to be: preload exactly one critical image, preload zero fonts, and let the browser&amp;rsquo;s native priority system handle everything else.&lt;/p&gt;
&lt;h3 id="build-size-vs-lcp"&gt;Build size vs. LCP&lt;/h3&gt;
&lt;div style="position:relative; width:100%; aspect-ratio:3/2; margin:2rem 0;"&gt;
&lt;canvas id="size-chart"&gt;&lt;/canvas&gt;
&lt;/div&gt;
&lt;p&gt;Build size and LCP had almost no correlation. The site grew from ~700MB to ~949MB (from accumulated experimental CSS and config), then dropped to ~689MB after a clean rebuild &amp;ndash; with no effect on LCP. Most of the size is in processed images, which Hugo caches aggressively. The LCP improvements came entirely from resource loading strategy and rendering hints, not from reducing bytes.&lt;/p&gt;
&lt;h3 id="the-noise-problem"&gt;The noise problem&lt;/h3&gt;
&lt;div style="position:relative; width:100%; aspect-ratio:3/2; margin:2rem 0;"&gt;
&lt;canvas id="noise-chart"&gt;&lt;/canvas&gt;
&lt;/div&gt;
&lt;p&gt;Lighthouse scores on localhost are noisy. The same unchanged site can swing up to ±370ms between runs. The agent dealt with this by noting when results seemed noisy (e.g., &amp;ldquo;1961/2333 noisy&amp;rdquo;) and sometimes running a second evaluation to confirm. Several experiments were kept or discarded on what was likely noise.&lt;/p&gt;
&lt;p&gt;This is the same problem ML researchers face with &lt;code&gt;val_bpb&lt;/code&gt; &amp;ndash; the signal-to-noise ratio drops as you approach the optimum. Late in the run, the difference between a &amp;ldquo;keep&amp;rdquo; and a &amp;ldquo;discard&amp;rdquo; was often within the measurement error. The chart above shows pairs of back-to-back evaluations on the same commit, demonstrating the typical variance.&lt;/p&gt;
&lt;h3 id="css-containment-death-by-a-thousand-cuts"&gt;CSS containment: death by a thousand cuts&lt;/h3&gt;
&lt;p&gt;The agent added &lt;code&gt;contain: layout style paint&lt;/code&gt; or &lt;code&gt;content-visibility: auto&lt;/code&gt; to over 40 elements. Individually, none of these moved LCP by more than a few milliseconds. Collectively, they may have contributed to the overall improvement, but it&amp;rsquo;s impossible to isolate their effect from the noise floor.&lt;/p&gt;
&lt;p&gt;This is the long tail of web performance &amp;ndash; dozens of tiny optimizations that are individually unmeasurable but might compound. The autoresearch pattern is ideal for this kind of work because the cost of trying (and discarding) a micro-optimization is near zero.&lt;/p&gt;
&lt;h3 id="the-structural-hash-saved-the-day"&gt;The structural hash saved the day&lt;/h3&gt;
&lt;p&gt;The DOM hash caught six experiments that would have changed the site&amp;rsquo;s appearance:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;keepEndTags=false&lt;/code&gt; &amp;ndash; removed closing tags, breaking structure&lt;/li&gt;
&lt;li&gt;Photography template script removal &amp;ndash; altered the page layout&lt;/li&gt;
&lt;li&gt;&lt;code&gt;scroll-to-top&lt;/code&gt; disabled &amp;ndash; removed a DOM element&lt;/li&gt;
&lt;li&gt;Code copy button disable &amp;ndash; changed the about page structure&lt;/li&gt;
&lt;li&gt;Photo count changes on homepage &amp;ndash; altered the grid structure&lt;/li&gt;
&lt;li&gt;JSON metadata block &amp;ndash; added a new script element&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Without the hash, these changes would have been kept (some improved LCP) and the site would have silently broken. The hash acts as an automated visual regression test &amp;ndash; crude but effective.&lt;/p&gt;
&lt;h3 id="what-i-learned"&gt;What I learned&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Resource contention is everything on mobile.&lt;/strong&gt; On a fast desktop connection, adding preloads is free. On simulated 4G with 1.6 Mbps bandwidth, every preload steals from everything else. The optimal strategy is ruthless prioritization: exactly one preload for the LCP element, nothing else.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The autoresearch pattern generalizes.&lt;/strong&gt; Karpathy designed it for ML training loops, but the structure &amp;ndash; &lt;code&gt;program.md&lt;/code&gt; for strategy, a fixed evaluator, keep/discard decisions, a results log &amp;ndash; works for any optimization problem with a measurable objective. Web performance, bundle size, build time, query latency &amp;ndash; anything with a number you want to move.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;200 experiments is overkill for web performance.&lt;/strong&gt; The agent had essentially exhausted the search space by experiment 50. The remaining 150 experiments were increasingly marginal CSS containment hints and configuration toggles. An ML model has millions of parameters to explore; a Hugo site has maybe 30 meaningful performance levers.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Noise is the limiting factor.&lt;/strong&gt; By experiment 30, the remaining opportunities were smaller than the measurement noise. A proper setup would use multiple Lighthouse runs per experiment and compare medians. The agent tried this occasionally but not systematically.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The best optimization was a deletion.&lt;/strong&gt; Removing the font preload, removing JSON output format, removing unused srcset breakpoints &amp;ndash; the three cleanest wins were all about doing less. The browser&amp;rsquo;s defaults are heavily optimized. Fight them at your peril.&lt;/p&gt;
&lt;h3 id="the-numbers"&gt;The numbers&lt;/h3&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Metric&lt;/th&gt;
 &lt;th&gt;Before&lt;/th&gt;
 &lt;th&gt;After&lt;/th&gt;
 &lt;th&gt;Change&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;Worst LCP (mobile 4G)&lt;/td&gt;
 &lt;td&gt;2,638ms&lt;/td&gt;
 &lt;td&gt;2,109ms&lt;/td&gt;
 &lt;td&gt;-20%&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Lighthouse perf score&lt;/td&gt;
 &lt;td&gt;-&lt;/td&gt;
 &lt;td&gt;99&lt;/td&gt;
 &lt;td&gt;-&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Homepage FCP&lt;/td&gt;
 &lt;td&gt;-&lt;/td&gt;
 &lt;td&gt;904ms&lt;/td&gt;
 &lt;td&gt;-&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Experiments run&lt;/td&gt;
 &lt;td&gt;-&lt;/td&gt;
 &lt;td&gt;200&lt;/td&gt;
 &lt;td&gt;-&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Experiments kept&lt;/td&gt;
 &lt;td&gt;-&lt;/td&gt;
 &lt;td&gt;147&lt;/td&gt;
 &lt;td&gt;74%&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Experiments discarded&lt;/td&gt;
 &lt;td&gt;-&lt;/td&gt;
 &lt;td&gt;47&lt;/td&gt;
 &lt;td&gt;23%&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Build crashes&lt;/td&gt;
 &lt;td&gt;-&lt;/td&gt;
 &lt;td&gt;4&lt;/td&gt;
 &lt;td&gt;2%&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Structure hash violations&lt;/td&gt;
 &lt;td&gt;-&lt;/td&gt;
 &lt;td&gt;6&lt;/td&gt;
 &lt;td&gt;3%&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The full experiment log, evaluation harness, and agent instructions are &lt;a href="https://github.com/MrMatt-io/mrmatt.io"&gt;on GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;script src="https://mrmatt.io/js/chart.umd.min.js"&gt;&lt;/script&gt;
&lt;script&gt;
(function() {
 /* ── Raw experiment data ── */
 var experiments = [
 {n:1,lcp:2638,size:715984.2,status:'baseline',desc:'initial baseline'},
 {n:2,lcp:2563,size:715985.9,status:'keep',desc:'fetchpriority=high on avatar'},
 {n:3,lcp:2341,size:716018.1,status:'keep',desc:'content-visibility auto on gallery'},
 {n:4,lcp:2334,size:716050.4,status:'keep',desc:'contain layout style paint on gallery'},
 {n:5,lcp:2335,size:716082.6,status:'keep',desc:'Hugo minify config'},
 {n:6,lcp:2340,size:716082.6,status:'discard',desc:'rootMargin 400-&gt;800px'},
 {n:7,lcp:2709,size:716082.6,status:'discard',desc:'batch 16-&gt;32'},
 {n:8,lcp:2332,size:716082.6,status:'discard',desc:'batch 16-&gt;8 noisy'},
 {n:9,lcp:2480,size:716082.8,status:'keep',desc:'preload first gallery thumbnail'},
 {n:10,lcp:2856,size:716082.8,status:'discard',desc:'eager load first 4 thumbs'},
 {n:11,lcp:2333,size:716072.3,status:'keep',desc:'LQIP 20x20-&gt;10x10'},
 {n:12,lcp:2406,size:716071.7,status:'discard',desc:'LQIP on homepage'},
 {n:13,lcp:2332,size:716071.7,status:'keep',desc:'defer lightbox to rIC'},
 {n:14,lcp:2260,size:721838.9,status:'keep',desc:'thumbnail q80-&gt;q70'},
 {n:15,lcp:2258,size:726939.3,status:'keep',desc:'thumbnail q70-&gt;q60'},
 {n:16,lcp:2258,size:726939.3,status:'keep',desc:'homepage thumbs q80-&gt;q60'},
 {n:17,lcp:2182,size:726939.3,status:'keep',desc:'preload quality matched to q60'},
 {n:18,lcp:2257,size:726937.7,status:'discard',desc:'removed decoding=async'},
 {n:19,lcp:2186,size:726971.6,status:'discard',desc:'will-change transform'},
 {n:20,lcp:2255,size:727003.9,status:'discard',desc:'content-visibility on homepage sections'},
 {n:21,lcp:2256,size:727036.2,status:'discard',desc:'contain-intrinsic-size'},
 {n:22,lcp:2184,size:727036.4,status:'discard',desc:'responsive preload with imagesrcset'},
 {n:23,lcp:2331,size:727039.6,status:'discard',desc:'fetchpriority=high on font preload'},
 {n:24,lcp:2257,size:727039.4,status:'discard',desc:'fetchpriority=low on font preload'},
 {n:25,lcp:2036,size:727020.3,status:'keep',desc:'remove font preload'},
 {n:26,lcp:2563,size:727020.2,status:'discard',desc:'removed gallery preload'},
 {n:27,lcp:2108,size:727020.5,status:'discard',desc:'preload 3 thumbs'},
 {n:28,lcp:2033,size:727020.3,status:'discard',desc:'preload 200w vs 300w'},
 {n:29,lcp:2033,size:727010.9,status:'discard',desc:'keepEndTags=false'},
 {n:30,lcp:2033,size:727020.3,status:'keep',desc:'keepDefaultAttrVals=false'},
 {n:31,lcp:2723,size:727014.7,status:'discard',desc:'keepDocumentTags=false'},
 {n:32,lcp:2034,size:727039.3,status:'discard',desc:'keepWhitespace=true'},
 {n:34,lcp:2034,size:728935.5,status:'discard',desc:'photography script removal'},
 {n:35,lcp:2111,size:728942.2,status:'discard',desc:'fetchpriority=high on gallery preload'},
 {n:38,lcp:2033,size:945864.4,status:'keep',desc:'dns-prefetch for external domains'},
 {n:39,lcp:1960,size:945863.7,status:'keep',desc:'disable enableGitInfo'},
 {n:40,lcp:2032,size:945863.7,status:'keep',desc:'remove LLMS outputs'},
 {n:41,lcp:1959,size:945864.1,status:'keep',desc:'inline critical CSS for photography'},
 {n:42,lcp:2035,size:945864.3,status:'keep',desc:'inline critical CSS for homepage'},
 {n:43,lcp:2034,size:945893.7,status:'keep',desc:'avatar q85-&gt;q75'},
 {n:44,lcp:2033,size:945925.9,status:'keep',desc:'contain on .main'},
 {n:45,lcp:1961,size:945926.0,status:'keep',desc:'fetchpriority=low on photo strip'},
 {n:47,lcp:2027,size:970623.3,status:'discard',desc:'contain:strict FCP regression'},
 {n:48,lcp:2033,size:970655.6,status:'keep',desc:'content-visibility on footer'},
 {n:49,lcp:2033,size:970676.0,status:'keep',desc:'disable PaperMod scrollbar CSS'},
 {n:50,lcp:1960,size:970643.3,status:'discard',desc:'scroll-to-top disabled'},
 {n:51,lcp:2037,size:970707.4,status:'discard',desc:'CSS @layer'},
 {n:52,lcp:1957,size:970707.3,status:'keep',desc:'remove JSON output'},
 {n:53,lcp:2033,size:970707.2,status:'discard',desc:'remove section RSS'},
 {n:54,lcp:2035,size:970707.3,status:'discard',desc:'decoding=sync on avatar'},
 {n:55,lcp:1957,size:970707.4,status:'keep',desc:'prerender hint'},
 {n:56,lcp:1958,size:970707.6,status:'keep',desc:'expanded photography critical CSS'},
 {n:57,lcp:2034,size:970707.7,status:'keep',desc:'speculation rules'},
 {n:58,lcp:2035,size:970717.1,status:'keep',desc:'auto DNS prefetch'},
 {n:59,lcp:1958,size:970748.6,status:'keep',desc:'content-visibility on post footer'},
 {n:60,lcp:2033,size:970780.2,status:'keep',desc:'content-visibility on lightbox'},
 {n:61,lcp:2034,size:970780.2,status:'keep',desc:'noJSConfigInAssets'},
 {n:62,lcp:2032,size:970785.0,status:'keep',desc:'defer scroll progress to rIC'},
 {n:63,lcp:2034,size:970793.0,status:'keep',desc:'color-scheme meta tag'},
 {n:64,lcp:2184,size:970793.0,status:'discard',desc:'goldmark block attributes'},
 {n:65,lcp:2335,size:970793.0,status:'discard',desc:'related threshold 80-&gt;90'},
 {n:66,lcp:2033,size:970824.6,status:'keep',desc:'contain on photo-thumb-wrap'},
 {n:67,lcp:2029,size:970856.2,status:'keep',desc:'minimize scroll-sentinel'},
 {n:68,lcp:1960,size:970887.9,status:'keep',desc:'containment on blur pseudo'},
 {n:70,lcp:2551,size:970888.0,status:'discard',desc:'prefetch from about page'},
 {n:71,lcp:2033,size:970919.6,status:'keep',desc:'aspect-ratio on lightbox'},
 {n:72,lcp:2034,size:970919.6,status:'keep',desc:'remove categories taxonomy'},
 {n:73,lcp:1959,size:970919.5,status:'keep',desc:'disable Hugo generator meta'},
 {n:75,lcp:2034,size:970951.2,status:'keep',desc:'contain on header'},
 {n:76,lcp:1959,size:970983.1,status:'keep',desc:'containment on post elements'},
 {n:77,lcp:2033,size:970983.1,status:'keep',desc:'explicit loading=eager on avatar'},
 {n:79,lcp:2033,size:971015.0,status:'keep',desc:'contain on photo-strip grid'},
 {n:80,lcp:2034,size:971013.5,status:'keep',desc:'simplified gallery sizes'},
 {n:81,lcp:2035,size:971004.9,status:'keep',desc:'remove 200w srcset'},
 {n:82,lcp:2034,size:971003.6,status:'keep',desc:'remove srcset from photo strip'},
 {n:83,lcp:2034,size:971003.6,status:'keep',desc:'gallery sizes 160px-&gt;175px'},
 {n:84,lcp:2107,size:971003.7,status:'discard',desc:'LQIP blur in critical CSS'},
 {n:86,lcp:2711,size:971003.6,status:'discard',desc:'sizes on avatar'},
 {n:87,lcp:2109,size:971013.6,status:'keep',desc:'referrer policy meta tag'},
 {n:88,lcp:2108,size:971045.5,status:'keep',desc:'containment on TOC'},
 {n:89,lcp:2264,size:971081.0,status:'discard',desc:'disableSVG'},
 {n:90,lcp:2787,size:971045.5,status:'discard',desc:'reorder preloads before CSS'},
 {n:91,lcp:2183,size:971054.5,status:'keep',desc:'format-detection meta tag'},
 {n:93,lcp:2034,size:971086.4,status:'keep',desc:'containment on home-photography'},
 {n:94,lcp:2035,size:971118.3,status:'keep',desc:'containment on home-journal'},
 {n:95,lcp:2033,size:971150.4,status:'keep',desc:'content-visibility on sections'},
 {n:96,lcp:2033,size:971182.5,status:'keep',desc:'containment on post-entry cards'},
 {n:97,lcp:2108,size:971184.4,status:'discard',desc:'fetchpriority=low on all gallery thumbs'},
 {n:98,lcp:3230,size:971214.6,status:'discard',desc:'contain inline-size on grid'},
 {n:99,lcp:2108,size:971246.7,status:'discard',desc:'rAF LQIP application'},
 {n:100,lcp:2108,size:971246.7,status:'discard',desc:'removing grid contain'},
 {n:101,lcp:2486,size:971262.7,status:'discard',desc:'font prefetch'},
 {n:102,lcp:2107,size:971278.8,status:'keep',desc:'containment on wrapper elements'},
 {n:103,lcp:2033,size:971311.0,status:'keep',desc:'containment on photo-thumb-link'},
 {n:106,lcp:2108,size:971334.9,status:'keep',desc:'full eval check'},
 {n:107,lcp:2033,size:971334.9,status:'discard',desc:'batch 16-&gt;12'},
 {n:109,lcp:2108,size:971367.0,status:'keep',desc:'overscroll-behavior eval check'},
 {n:110,lcp:2034,size:971369.0,status:'discard',desc:'JSON metadata block'},
 {n:114,lcp:2033,size:971399.3,status:'keep',desc:'full eval check stable'},
 {n:115,lcp:2109,size:971399.4,status:'discard',desc:'responsive preload'},
 {n:116,lcp:2035,size:971399.3,status:'keep',desc:'first thumbnail eager'},
 {n:117,lcp:2109,size:971399.3,status:'keep',desc:'first thumb eager no fetchpriority'},
 {n:121,lcp:2034,size:971400.9,status:'keep',desc:'full eval stable'},
 {n:123,lcp:2108,size:971400.9,status:'keep',desc:'merged photography LQIP'},
 {n:127,lcp:2109,size:971433.2,status:'keep',desc:'cumulative containment eval'},
 {n:133,lcp:2107,size:971474.4,status:'keep',desc:'containment additions eval'},
 {n:135,lcp:2108,size:971506.9,status:'keep',desc:'rAF batch reveal'},
 {n:136,lcp:2032,size:971514.4,status:'keep',desc:'moderate speculation rules'},
 {n:141,lcp:2034,size:971557.1,status:'keep',desc:'full eval stable perf=100'},
 {n:149,lcp:2109,size:971589.8,status:'keep',desc:'touch-action and compositor hints'},
 {n:154,lcp:2109,size:971634.7,status:'keep',desc:'speculation rules all pages'},
 {n:157,lcp:2036,size:971634.9,status:'keep',desc:'content-visibility in critical CSS'},
 {n:162,lcp:2864,size:971681.2,status:'discard',desc:'photography LCP regression'},
 {n:163,lcp:2108,size:971655.4,status:'keep',desc:'reverted to stable state'},
 {n:164,lcp:2259,size:971655.4,status:'discard',desc:'critical CSS regression'},
 {n:165,lcp:2031,size:705228.1,status:'keep',desc:'clean build'},
 {n:166,lcp:2938,size:705241.6,status:'discard',desc:'CSP upgrade-insecure-requests'},
 {n:167,lcp:2108,size:705228.1,status:'keep',desc:'color-scheme dark in critical CSS'},
 {n:168,lcp:2108,size:705228.2,status:'keep',desc:'content-visibility in critical CSS'},
 {n:173,lcp:2109,size:705260.9,status:'keep',desc:'full eval clean build stable'},
 {n:176,lcp:2107,size:705293.6,status:'keep',desc:'containment on body'},
 {n:181,lcp:2110,size:705326.5,status:'keep',desc:'full eval after re-adding props'},
 {n:188,lcp:2111,size:705359.4,status:'keep',desc:'full eval with recent additions'},
 {n:199,lcp:2110,size:705401.5,status:'keep',desc:'full eval near 200'},
 {n:200,lcp:2109,size:705419.5,status:'keep',desc:'mobile web app meta tags'}
 ];

 /* ── Computed datasets (hardcoded from full results.tsv, not just Lighthouse subset) ── */
 var keptCount = 147, discardCount = 47, crashCount = 4, revertCount = 1;

 /* Best LCP progression (kept experiments only) */
 var bestLcp = 9999;
 var bestProgression = [];
 experiments.forEach(function(e) {
 if (e.status === 'keep' || e.status === 'baseline') {
 if (e.lcp &lt; bestLcp) {
 bestLcp = e.lcp;
 bestProgression.push({n: e.n, lcp: e.lcp, desc: e.desc});
 }
 }
 });

 /* Category analysis */
 var categories = {
 'Resource hints': {tried: 0, kept: 0},
 'Image optimization': {tried: 0, kept: 0},
 'CSS containment': {tried: 0, kept: 0},
 'Hugo config': {tried: 0, kept: 0},
 'Script loading': {tried: 0, kept: 0},
 'HTML minification': {tried: 0, kept: 0},
 'Meta tags': {tried: 0, kept: 0},
 'Other': {tried: 0, kept: 0}
 };

 function categorize(desc) {
 desc = desc.toLowerCase();
 if (desc.match(/preload|prefetch|prerender|preconnect|dns-prefetch|fetchpriority|speculation/)) return 'Resource hints';
 if (desc.match(/thumbnail|q[0-9]|srcset|lqip|image|avatar|photo|gallery sizes|img/)) return 'Image optimization';
 if (desc.match(/contain|content-visibility|isolation|will-change|backface|touch-action|overscroll|pointer-events/)) return 'CSS containment';
 if (desc.match(/hugo|minify|git|build|config|output|taxonomy|cjk|emoji|shortcode|paginate|inline shortcodes|summaryLength/)) return 'Hugo config';
 if (desc.match(/defer|async|script|idle|ric|requestidlecallback|scroll progress/)) return 'Script loading';
 if (desc.match(/keepend|keepdefault|keepdoc|keepwhite/)) return 'HTML minification';
 if (desc.match(/meta|referrer|format-detection|color-scheme|nosniff|csp|permissions|robots/)) return 'Meta tags';
 return 'Other';
 }

 experiments.forEach(function(e) {
 if (e.status === 'baseline') return;
 var cat = categorize(e.desc);
 categories[cat].tried++;
 if (e.status === 'keep') categories[cat].kept++;
 });

 /* Noise demonstration: back-to-back evaluations on same/similar state */
 var noisePairs = [
 {label: 'Exp 39\n(gitInfo)', a: 1960, b: 2033},
 {label: 'Exp 41\n(crit CSS)', a: 1959, b: 2033},
 {label: 'Exp 45\n(fetchpri)', a: 1961, b: 2333},
 {label: 'Exp 68\n(blur)', a: 1960, b: 2033},
 {label: 'Exp 8\n(batch)', a: 2332, b: 2707}
 ];

 /* Preload impact data (delta from best LCP at time of experiment) */
 var preloadData = [
 {label: 'Remove font\npreload', delta: -146},
 {label: 'Preload 1st\nthumbnail', delta: +146},
 {label: 'Remove gallery\npreload', delta: +527},
 {label: 'Preload 3\nthumbnails', delta: +72},
 {label: 'fetchpriority=high\non font', delta: +149},
 {label: 'fetchpriority=high\non gallery', delta: +75},
 {label: 'Font prefetch', delta: +453},
 {label: 'Reorder preloads\nbefore CSS', delta: +751}
 ];

 /* Top wins (biggest LCP improvements from previous best, verified against results.tsv) */
 var topWins = [
 {label: 'content-visibility\non gallery', delta: -222},
 {label: 'Remove font\npreload', delta: -146},
 {label: 'Preload quality\nmatched q60', delta: -76},
 {label: 'fetchpriority=high\non avatar', delta: -75},
 {label: 'Disable\ngitInfo', delta: -73},
 {label: 'Thumbnail\nq80→q70', delta: -72},
 {label: 'contain layout\nstyle paint', delta: -7},
 {label: 'Thumbnail\nq70→q60', delta: -2}
 ].sort(function(a,b) { return a.delta - b.delta; });

 /* ── Render ── */
 function renderCharts() {
 var isDark = document.documentElement.getAttribute('data-theme') === 'dark';
 var textColor = isDark ? '#e0e0e0' : '#090909';
 var gridColor = isDark ? 'rgba(255,255,255,0.08)' : 'rgba(0,0,0,0.06)';
 var borderColor = isDark ? 'rgba(255,255,255,0.15)' : 'rgba(0,0,0,0.12)';
 var barColor = isDark ? '#888' : '#555';
 var accentColor = '#4c81b2';
 var discardColor = isDark ? 'rgba(192,57,43,0.5)' : 'rgba(192,57,43,0.4)';
 var keepColor = isDark ? 'rgba(76,129,178,0.7)' : 'rgba(76,129,178,0.6)';

 var defaultFont = { family: "'Roboto Slab', serif", size: 12, weight: '300' };
 var scaleDefs = {
 x: { grid: { color: gridColor }, ticks: { color: textColor, font: defaultFont } },
 y: { grid: { color: gridColor }, ticks: { color: textColor, font: defaultFont } }
 };
 var pluginDefs = {
 legend: { display: false },
 tooltip: {
 backgroundColor: isDark ? '#1d1e20' : '#fff',
 titleColor: textColor, bodyColor: textColor,
 borderColor: borderColor, borderWidth: 1, padding: 10,
 bodyFont: { family: "'Roboto Slab', serif", size: 13 },
 titleFont: { family: "'Roboto Slab', serif", size: 13, weight: '300' }
 }
 };

 /* ---- 1. LCP Timeline ---- */
 var lcpLabels = experiments.map(function(e) { return e.n; });
 var lcpData = experiments.map(function(e) { return e.lcp; });
 var lcpColors = experiments.map(function(e) {
 if (e.status === 'baseline') return accentColor;
 return e.status === 'discard' ? discardColor : keepColor;
 });
 var lcpBorderColors = experiments.map(function(e) {
 if (e.status === 'baseline') return accentColor;
 return e.status === 'discard' ? 'rgba(192,57,43,0.8)' : accentColor;
 });

 new Chart(document.getElementById('lcp-timeline'), {
 type: 'scatter',
 data: {
 datasets: [{
 data: experiments.map(function(e) { return {x: e.n, y: e.lcp}; }),
 backgroundColor: lcpColors,
 borderColor: lcpBorderColors,
 pointRadius: 4,
 pointHoverRadius: 6
 }, {
 type: 'line',
 data: (function() {
 var best = 9999;
 var pts = [];
 experiments.forEach(function(e) {
 if ((e.status === 'keep' || e.status === 'baseline') &amp;&amp; e.lcp &lt; best) {
 best = e.lcp;
 }
 if (e.status === 'keep' || e.status === 'baseline') {
 pts.push({x: e.n, y: best});
 }
 });
 return pts;
 })(),
 borderColor: isDark ? '#e0e0e0' : '#333',
 borderWidth: 1.5,
 pointRadius: 0,
 borderDash: [4, 3],
 fill: false,
 showLine: true
 }]
 },
 options: {
 responsive: true, maintainAspectRatio: false, animation: false,
 scales: {
 x: Object.assign({}, scaleDefs.x, {
 type: 'linear',
 title: { display: true, text: 'Experiment #', color: textColor, font: defaultFont }
 }),
 y: Object.assign({}, scaleDefs.y, {
 title: { display: true, text: 'Worst LCP (ms)', color: textColor, font: defaultFont },
 min: 1800
 })
 },
 plugins: Object.assign({}, pluginDefs, {
 tooltip: Object.assign({}, pluginDefs.tooltip, {
 callbacks: {
 title: function(items) {
 var idx = items[0].dataIndex;
 if (items[0].datasetIndex === 0) {
 return 'Experiment ' + experiments[idx].n;
 }
 return '';
 },
 label: function(ctx) {
 if (ctx.datasetIndex === 0) {
 var e = experiments[ctx.dataIndex];
 return e.lcp + 'ms — ' + e.desc + ' (' + e.status + ')';
 }
 return 'Best: ' + ctx.parsed.y + 'ms';
 }
 }
 })
 })
 }
 });

 /* ---- 2. Outcomes doughnut ---- */
 new Chart(document.getElementById('outcomes-chart'), {
 type: 'doughnut',
 data: {
 labels: ['Kept', 'Discarded', 'Crashed', 'Reverted'],
 datasets: [{
 data: [keptCount, discardCount, crashCount, revertCount],
 backgroundColor: [keepColor, discardColor, isDark ? '#e74c3c' : '#c0392b', isDark ? '#f39c12' : '#d4a017'],
 borderColor: isDark ? '#1d1e20' : '#fff',
 borderWidth: 2
 }]
 },
 options: {
 responsive: true, maintainAspectRatio: false, animation: false,
 plugins: {
 legend: {
 display: true,
 position: 'bottom',
 labels: { color: textColor, font: defaultFont, padding: 15 }
 },
 title: {
 display: true, text: 'Experiment Outcomes',
 color: textColor, font: Object.assign({}, defaultFont, { size: 13 })
 },
 tooltip: pluginDefs.tooltip
 }
 }
 });

 /* ---- 3. Category chart ---- */
 var catLabels = Object.keys(categories).filter(function(k) { return categories[k].tried &gt; 0; });
 var catTried = catLabels.map(function(k) { return categories[k].tried; });
 var catKept = catLabels.map(function(k) { return categories[k].kept; });

 new Chart(document.getElementById('category-chart'), {
 type: 'bar',
 data: {
 labels: catLabels,
 datasets: [
 { label: 'Tried', data: catTried, backgroundColor: isDark ? 'rgba(255,255,255,0.15)' : 'rgba(0,0,0,0.1)', borderRadius: 2 },
 { label: 'Kept', data: catKept, backgroundColor: keepColor, borderRadius: 2 }
 ]
 },
 options: {
 responsive: true, maintainAspectRatio: false, animation: false,
 indexAxis: 'y',
 scales: {
 x: Object.assign({ beginAtZero: true }, scaleDefs.x),
 y: Object.assign({}, scaleDefs.y, { ticks: Object.assign({}, scaleDefs.y.ticks, { font: Object.assign({}, defaultFont, { size: 11 }) }) })
 },
 plugins: {
 legend: {
 display: true, position: 'bottom',
 labels: { color: textColor, font: defaultFont, padding: 15 }
 },
 title: {
 display: true, text: 'Experiments by Category',
 color: textColor, font: Object.assign({}, defaultFont, { size: 13 })
 },
 tooltip: pluginDefs.tooltip
 }
 }
 });

 /* ---- 4. Top wins horizontal bar ---- */
 new Chart(document.getElementById('top-wins'), {
 type: 'bar',
 data: {
 labels: topWins.map(function(w) { return w.label; }),
 datasets: [{
 data: topWins.map(function(w) { return Math.abs(w.delta); }),
 backgroundColor: accentColor,
 borderRadius: 2
 }]
 },
 options: {
 responsive: true, maintainAspectRatio: false, animation: false,
 indexAxis: 'y',
 scales: {
 x: Object.assign({ beginAtZero: true, title: { display: true, text: 'LCP improvement (ms)', color: textColor, font: defaultFont } }, scaleDefs.x),
 y: scaleDefs.y
 },
 plugins: Object.assign({}, pluginDefs, {
 title: {
 display: true, text: 'Biggest LCP Improvements',
 color: textColor, font: Object.assign({}, defaultFont, { size: 13 })
 },
 tooltip: Object.assign({}, pluginDefs.tooltip, {
 callbacks: { label: function(ctx) { return '-' + ctx.parsed.x + 'ms'; } }
 })
 })
 }
 });

 /* ---- 5. Preload impact ---- */
 new Chart(document.getElementById('preload-chart'), {
 type: 'bar',
 data: {
 labels: preloadData.map(function(p) { return p.label; }),
 datasets: [{
 data: preloadData.map(function(p) { return p.delta; }),
 backgroundColor: preloadData.map(function(p) {
 return p.delta &lt; 0 ? accentColor : discardColor;
 }),
 borderRadius: 2
 }]
 },
 options: {
 responsive: true, maintainAspectRatio: false, animation: false,
 scales: {
 x: Object.assign({}, scaleDefs.x, {
 ticks: Object.assign({}, scaleDefs.x.ticks, { font: Object.assign({}, defaultFont, { size: 10 }) })
 }),
 y: Object.assign({}, scaleDefs.y, {
 title: { display: true, text: 'LCP change (ms)', color: textColor, font: defaultFont }
 })
 },
 plugins: Object.assign({}, pluginDefs, {
 title: {
 display: true, text: 'Impact of Resource Preloading Experiments',
 color: textColor, font: Object.assign({}, defaultFont, { size: 13 })
 },
 tooltip: Object.assign({}, pluginDefs.tooltip, {
 callbacks: {
 label: function(ctx) {
 var v = ctx.parsed.y;
 return (v &gt; 0 ? '+' : '') + v + 'ms (worse = higher)';
 }
 }
 })
 })
 }
 });

 /* ---- 6. Build size timeline ---- */
 var sizeExps = experiments.filter(function(e) { return e.size &gt; 0; });
 new Chart(document.getElementById('size-chart'), {
 type: 'line',
 data: {
 labels: sizeExps.map(function(e) { return e.n; }),
 datasets: [{
 data: sizeExps.map(function(e) { return Math.round(e.size / 1024); }),
 borderColor: barColor,
 backgroundColor: isDark ? 'rgba(255,255,255,0.05)' : 'rgba(0,0,0,0.03)',
 fill: true,
 borderWidth: 1.5, pointRadius: 0, tension: 0.1
 }]
 },
 options: {
 responsive: true, maintainAspectRatio: false, animation: false,
 scales: {
 x: Object.assign({}, scaleDefs.x, {
 title: { display: true, text: 'Experiment #', color: textColor, font: defaultFont }
 }),
 y: Object.assign({}, scaleDefs.y, {
 title: { display: true, text: 'Total build size (MB)', color: textColor, font: defaultFont },
 ticks: Object.assign({}, scaleDefs.y.ticks, {
 callback: function(v) { return v + ' MB'; }
 })
 })
 },
 plugins: Object.assign({}, pluginDefs, {
 tooltip: Object.assign({}, pluginDefs.tooltip, {
 callbacks: { label: function(ctx) { return ctx.parsed.y + ' MB'; } }
 })
 })
 }
 });

 /* ---- 7. Noise demonstration ---- */
 new Chart(document.getElementById('noise-chart'), {
 type: 'bar',
 data: {
 labels: noisePairs.map(function(p) { return p.label; }),
 datasets: [
 { label: 'Run 1', data: noisePairs.map(function(p) { return p.a; }), backgroundColor: accentColor, borderRadius: 2 },
 { label: 'Run 2', data: noisePairs.map(function(p) { return p.b; }), backgroundColor: barColor, borderRadius: 2 }
 ]
 },
 options: {
 responsive: true, maintainAspectRatio: false, animation: false,
 scales: {
 x: scaleDefs.x,
 y: Object.assign({}, scaleDefs.y, {
 min: 1800,
 title: { display: true, text: 'LCP (ms)', color: textColor, font: defaultFont }
 })
 },
 plugins: {
 legend: {
 display: true, position: 'bottom',
 labels: { color: textColor, font: defaultFont, padding: 15 }
 },
 title: {
 display: true, text: 'Same Experiment, Different LCP Readings',
 color: textColor, font: Object.assign({}, defaultFont, { size: 13 })
 },
 tooltip: Object.assign({}, pluginDefs.tooltip, {
 callbacks: { label: function(ctx) { return ctx.dataset.label + ': ' + ctx.parsed.y + 'ms'; } }
 })
 }
 }
 });
 }

 renderCharts();
 new MutationObserver(function() { location.reload(); })
 .observe(document.documentElement, { attributes: true, attributeFilter: ['data-theme'] });
})();
&lt;/script&gt;</content:encoded><media:content url="https://mrmatt.io/images/MattWalker-avatar.png" medium="image"/></item><item><title>Binghamton Spiedies Grilling</title><link>https://mrmatt.io/photography/2026-03-15-binghamton-spiedies-grilling/</link><pubDate>Sun, 15 Mar 2026 00:00:00 -0400</pubDate><guid>https://mrmatt.io/photography/2026-03-15-binghamton-spiedies-grilling/</guid><description>Spiedies, a local Binghamton specialty, cook over glowing coals in this classic outdoor grilling scene. The marinated meat cubes are charred to perfection, embodying the region's beloved street food tradition.</description><content:encoded/><media:content url="https://mrmatt.io/photography/2026-03-15-binghamton-spiedies-grilling/photo_hu_1cefc6fc6484f8e0.webp" medium="image"/></item><item><title>The Membrane</title><link>https://mrmatt.io/posts/the-membrane/</link><pubDate>Wed, 11 Mar 2026 00:00:00 -0400</pubDate><guid>https://mrmatt.io/posts/the-membrane/</guid><description>The AI capability boundary is expanding — and so is the surface area where humans matter most. A mental model for navigating the human-AI interface.</description><content:encoded>&lt;p&gt;Something fundamental is shifting in how humans and AI work together, and it&amp;rsquo;s moving fast enough to be genuinely disorienting. We&amp;rsquo;re at an inflection point. The transformation is real, it&amp;rsquo;s accelerating, and most people can feel it even if they can&amp;rsquo;t name it. I&amp;rsquo;ve been trying to find the right frame for it. The closest model I&amp;rsquo;ve found is the membrane.&lt;/p&gt;
&lt;p&gt;Engineering has a long history of borrowing from biology and finding that the patterns hold. Neural networks. Genetic algorithms. Swarm intelligence. Hub-and-spoke topologies that mirror how cells cluster and communicate. We reach for nature&amp;rsquo;s blueprints because evolution has had a few billion years to optimize for complex systems. When something in nature rhymes with something in technology, it&amp;rsquo;s worth paying attention.&lt;/p&gt;
&lt;p&gt;The membrane is one of those rhymes.&lt;/p&gt;
&lt;figure&gt;
 &lt;img loading="lazy" src="https://mrmatt.io/images/ai-capability-membrane-model.png"
 alt="Illustration of an AI cell pushing against a membrane boundary while a human observes from outside, representing the shifting boundary between AI capability and human judgment"/&gt; &lt;figcaption&gt;
 &lt;p&gt;Conceptual model of the human–AI capability boundary.&lt;/p&gt;
 &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h3 id="the-cell"&gt;The Cell&lt;/h3&gt;
&lt;p&gt;Picture a cell. Inside is everything AI and agents can handle autonomously: not just simple tasks, but work that requires intent, context, and multi-step reasoning. The boundary is further out than most people&amp;rsquo;s mental models have caught up to. Outside is everything that still requires a human: choosing which problems matter, navigating ambiguity where the data won&amp;rsquo;t give you a clean answer, and building the kind of trust that only comes from another person in the room.&lt;/p&gt;
&lt;p&gt;The cell is growing. Fast.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://metr.org/blog/2025-03-19-measuring-ai-ability-to-complete-long-tasks/"&gt;METR&amp;rsquo;s research&lt;/a&gt; shows the length of tasks agents can complete autonomously has been doubling roughly every seven months, accelerating to every four months recently. Their benchmarks measure isolated tasks, so real work is messier. But the &lt;em&gt;direction&lt;/em&gt; is what matters. The cell is expanding, and it&amp;rsquo;s not slowing down.&lt;/p&gt;
&lt;h3 id="stay-on-the-wall"&gt;Stay on the Wall&lt;/h3&gt;
&lt;p&gt;If you&amp;rsquo;re doing work that&amp;rsquo;s inside the cell, you should be asking why you&amp;rsquo;re not delegating it. If you&amp;rsquo;re doing work that&amp;rsquo;s outside, you&amp;rsquo;re applying skills that agents can&amp;rsquo;t replicate — yet. But that boundary is shifting every few months.&lt;/p&gt;
&lt;p&gt;The position of leverage is the membrane itself. The edge between what AI can do and what it can&amp;rsquo;t. That&amp;rsquo;s where you&amp;rsquo;re orchestrating agents, evaluating outputs, deciding what to delegate and what to own, and recalibrating as the boundary moves.&lt;/p&gt;
&lt;p&gt;A concrete example: any time you find yourself copying and pasting information into or out of an agent, you&amp;rsquo;re doing work that&amp;rsquo;s inside the cell. If you can access the information, more than likely so can the agent with the right permissions (use caution here). The membrane work is wiring the agent to the source directly and deciding what to do with what comes back.&lt;/p&gt;
&lt;p&gt;The skill isn&amp;rsquo;t any single task. The skill is reading the membrane: where is it thin, where is it about to stretch, what just crossed over, what&amp;rsquo;s stubbornly staying outside and why. That requires actually using the tools (you can&amp;rsquo;t read the membrane from a distance), staying current with what agents can do (which changes faster than most people realize), and being honest about which of your tasks have quietly moved inside.&lt;/p&gt;
&lt;p&gt;The membrane doesn&amp;rsquo;t move once and settle. It moves continuously. You learn a skill today. Maybe it&amp;rsquo;s already inside the cell and you don&amp;rsquo;t know it yet. Maybe it&amp;rsquo;s outside today and inside in six months. The durable skill is navigating that evolution, not mastering either side of it.&lt;/p&gt;
&lt;h3 id="the-surface-area-argument"&gt;The Surface Area Argument&lt;/h3&gt;
&lt;p&gt;Here&amp;rsquo;s the part I think most people miss.&lt;/p&gt;
&lt;p&gt;When a cell grows, the volume increases. But so does the surface area. The membrane gets bigger.&lt;/p&gt;
&lt;p&gt;The membrane is the human-AI interface. Every point where people and agents interact, hand off, evaluate, and direct. A larger cell means a &lt;em&gt;larger membrane&lt;/em&gt;. More surface area. More points of interaction, not fewer.&lt;/p&gt;
&lt;p&gt;As the cell grows, the tasks inside it change hands. But the boundary where humans engage with AI grows too. The work shifts from &amp;ldquo;human does the task&amp;rdquo; to &amp;ldquo;human guides, evaluates, and orchestrates.&amp;rdquo; The contact surface keeps expanding. There will be &lt;em&gt;more&lt;/em&gt; human-AI interaction in the future, not less. It&amp;rsquo;ll just look different, and it&amp;rsquo;ll keep changing.&lt;/p&gt;
&lt;h3 id="getting-practical"&gt;Getting Practical&lt;/h3&gt;
&lt;p&gt;We&amp;rsquo;re all trying to read a membrane that&amp;rsquo;s moving faster than we can track. Here are a few things that have helped me stay closer to the edge:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Use the tools daily.&lt;/strong&gt; You cannot develop intuition for where the membrane sits by reading about AI. You have to use it. Every week I throw agents at something I assume they&amp;rsquo;ll fail at. Half the time, the boundary has moved since I last checked. That&amp;rsquo;s the calibration loop — you have to run it constantly or your model of the membrane drifts.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Audit your own work.&lt;/strong&gt; Look at your last week honestly. How much of it was inside the cell? I started keeping a rough log: tasks I did myself versus tasks I delegated. The ratio shifts every few months, and noticing that shift is how I know where the membrane is stretching.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Watch for the stretch.&lt;/strong&gt; When a new model drops or a new capability emerges, the membrane doesn&amp;rsquo;t move uniformly. It bulges in specific places. Pay attention to where the new pressure is concentrated. Those are the spots where your skills at the boundary matter most.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Get comfortable not knowing.&lt;/strong&gt; The membrane will keep moving. The map you build today will be wrong in six months. That&amp;rsquo;s fine. The skill is the mapping, not the map.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;Stay on the wall.&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;</content:encoded><media:content url="https://mrmatt.io/images/MattWalker-avatar.png" medium="image"/></item><item><title>94,000 Photos by the Numbers</title><link>https://mrmatt.io/posts/photography-by-the-numbers/</link><pubDate>Tue, 10 Mar 2026 00:00:00 -0400</pubDate><guid>https://mrmatt.io/posts/photography-by-the-numbers/</guid><description>What 22 years of EXIF data reveals about when I shoot, what I shoot with, and how photography changed from film-era habits to phone-first instinct.</description><content:encoded>&lt;p&gt;In &lt;a href="https://mrmatt.io/posts/camera-gear-timeline/"&gt;Every Camera I&amp;rsquo;ve Ever Owned&lt;/a&gt;, I traced 22 years of gear. This post is about the photos themselves &amp;ndash; 94,000 of them &amp;ndash; and what their metadata reveals.&lt;/p&gt;
&lt;p&gt;I scanned the EXIF headers of every JPEG across 15 Google Takeout zip files. Here&amp;rsquo;s what the data says.&lt;/p&gt;
&lt;h3 id="volume-over-time"&gt;Volume over time&lt;/h3&gt;
&lt;div style="position:relative; width:100%; aspect-ratio:3/2; margin:2rem 0;"&gt;
&lt;canvas id="yearly-chart"&gt;&lt;/canvas&gt;
&lt;/div&gt;
&lt;p&gt;The yearly photo count forms a bell curve peaking at 2015 with 13,816 photos. The ramp tracks predictable life events &amp;ndash; kids born, vacations, new cameras arriving every year or two. At the peak, I had five cameras active in a single year: phones, compacts, and a DSLR all competing for pocket space. The decline after 2015 is equally clear: one phone replaced everything, and the volume settled to a sustainable rhythm. The 2020 dip is exactly what you&amp;rsquo;d expect from a year spent mostly indoors.&lt;/p&gt;
&lt;h3 id="when-i-shoot"&gt;When I shoot&lt;/h3&gt;
&lt;div style="display:grid; grid-template-columns:1fr 1fr; gap:1.5rem; margin:2rem 0;"&gt;
&lt;div style="position:relative; width:100%; aspect-ratio:1/1;"&gt;
&lt;canvas id="hourly-chart"&gt;&lt;/canvas&gt;
&lt;/div&gt;
&lt;div style="position:relative; width:100%; aspect-ratio:1/1;"&gt;
&lt;canvas id="dow-chart"&gt;&lt;/canvas&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Saturday at 10am is my golden hour. Weekends get twice the shooting volume of midweek. The hourly distribution peaks sharply at 10am with roughly 8,000 photos, then holds a plateau through the afternoon before dropping off after sunset. Weekdays are flat and low &amp;ndash; photography is clearly a leisure activity, not a workday habit.&lt;/p&gt;
&lt;h3 id="the-heatmap"&gt;The heatmap&lt;/h3&gt;
&lt;div style="position:relative; width:100%; aspect-ratio:3/2; margin:2rem 0;"&gt;
&lt;canvas id="heatmap-chart"&gt;&lt;/canvas&gt;
&lt;/div&gt;
&lt;p&gt;The pattern is clear: weekend mornings and afternoons are when I reach for the camera. The weekday rows are dim and uniform. Saturday and Sunday light up from mid-morning through late afternoon, with the strongest signal clustered around 9am to 3pm.&lt;/p&gt;
&lt;h3 id="people-in-photos"&gt;People in photos&lt;/h3&gt;
&lt;div style="position:relative; width:100%; aspect-ratio:3/2; margin:2rem 0;"&gt;
&lt;canvas id="people-chart"&gt;&lt;/canvas&gt;
&lt;/div&gt;
&lt;p&gt;The percentage of photos containing people dropped from 70% in 2016 to under 2% after 2020. This could reflect Google&amp;rsquo;s face detection metadata changing over time, or a genuine shift toward landscape and nature photography. Probably both. The early years are noisy because the sample sizes are small &amp;ndash; a few hundred photos per year makes each face detection hit or miss count for a lot.&lt;/p&gt;
&lt;h3 id="where-in-the-world"&gt;Where in the world&lt;/h3&gt;
&lt;div style="position:relative; width:100%; aspect-ratio:3/2; margin:2rem 0;"&gt;
&lt;canvas id="gps-chart"&gt;&lt;/canvas&gt;
&lt;/div&gt;
&lt;p&gt;GPS data went from 0% in the pre-smartphone era to 91% in 2017, then drifted back down. The rise tracks smartphone adoption perfectly &amp;ndash; phones embed GPS coordinates by default, dedicated cameras don&amp;rsquo;t. The decline after 2017 likely reflects tightening privacy settings and location permissions being turned off, plus occasional use of cameras without GPS radios.&lt;/p&gt;
&lt;h3 id="the-lens-settings"&gt;The lens settings&lt;/h3&gt;
&lt;div style="display:grid; grid-template-columns:1fr 1fr 1fr; gap:1.5rem; margin:2rem 0;"&gt;
&lt;div style="position:relative; width:100%; aspect-ratio:1/1;"&gt;
&lt;canvas id="aperture-chart"&gt;&lt;/canvas&gt;
&lt;/div&gt;
&lt;div style="position:relative; width:100%; aspect-ratio:1/1;"&gt;
&lt;canvas id="focal-chart"&gt;&lt;/canvas&gt;
&lt;/div&gt;
&lt;div style="position:relative; width:100%; aspect-ratio:1/1;"&gt;
&lt;canvas id="iso-time-chart"&gt;&lt;/canvas&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;f/2.0 at 27mm. That&amp;rsquo;s the phone sweet spot &amp;ndash; a moderately wide angle with a fast-ish aperture. The DSLR&amp;rsquo;s f/1.8 at 50mm barely registers anymore. Average ISO climbs steadily year over year as phones push deeper into low-light computational photography, trading sensor noise for Night Sight algorithms.&lt;/p&gt;
&lt;h3 id="resolution-and-exposure"&gt;Resolution and exposure&lt;/h3&gt;
&lt;div style="display:grid; grid-template-columns:1fr 1fr; gap:1.5rem; margin:2rem 0;"&gt;
&lt;div style="position:relative; width:100%; aspect-ratio:1/1;"&gt;
&lt;canvas id="resolution-chart"&gt;&lt;/canvas&gt;
&lt;/div&gt;
&lt;div style="position:relative; width:100%; aspect-ratio:1/1;"&gt;
&lt;canvas id="exposure-chart"&gt;&lt;/canvas&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Resolution plateaued after the megapixel race ended. The jump from 4MP in 2003 to 12MP in 2012 was dramatic; the crawl from 12MP to 22MP over the next decade was barely noticeable in practice. Most photos are shot between 1/10s and 1/250s &amp;ndash; everyday handheld speeds, the kind of shutter range where image stabilization matters more than raw sensor capability.&lt;/p&gt;
&lt;h3 id="the-full-heartbeat"&gt;The full heartbeat&lt;/h3&gt;
&lt;div style="position:relative; width:100%; aspect-ratio:3/2; margin:2rem 0;"&gt;
&lt;canvas id="monthly-chart"&gt;&lt;/canvas&gt;
&lt;/div&gt;
&lt;p&gt;Zoom out from yearly averages and the real rhythm appears. Every spike is a trip, a holiday, a new baby, a Saturday morning at the park. The three busiest months &amp;ndash; May 2015, April 2018, April 2015 &amp;ndash; each cracked 1,700 photos. That&amp;rsquo;s 55 to 65 photos a day, every day, for a month straight. The quiet valleys are just as telling: the gaps where life got busy, or the camera stayed in the drawer, or a global pandemic kept everyone indoors.&lt;/p&gt;
&lt;p&gt;This is 94,000 shutter presses compressed into a single line. Twenty-two years of reaching for a camera &amp;ndash; first deliberately, then instinctively.&lt;/p&gt;
&lt;p&gt;See the full gear timeline in &lt;a href="https://mrmatt.io/posts/camera-gear-timeline/"&gt;Every Camera I&amp;rsquo;ve Ever Owned&lt;/a&gt;.&lt;/p&gt;
&lt;script src="https://mrmatt.io/js/chart.umd.min.js"&gt;&lt;/script&gt;
&lt;script&gt;
(function() {
 fetch('/data/photo-stats.json')
 .then(function(r) { return r.json(); })
 .then(function(data) { renderCharts(data); });

 function renderCharts(data) {
 var isDark = document.documentElement.getAttribute('data-theme') === 'dark';
 var textColor = isDark ? '#e0e0e0' : '#090909';
 var gridColor = isDark ? 'rgba(255,255,255,0.08)' : 'rgba(0,0,0,0.06)';
 var borderColor = isDark ? 'rgba(255,255,255,0.15)' : 'rgba(0,0,0,0.12)';
 var barColor = isDark ? '#888' : '#555';
 var accentColor = '#4c81b2';

 var defaultFont = { family: "'Roboto Slab', serif", size: 12, weight: '300' };
 var scaleDefs = {
 x: { grid: { color: gridColor }, ticks: { color: textColor, font: defaultFont } },
 y: { grid: { color: gridColor }, ticks: { color: textColor, font: defaultFont } }
 };
 var pluginDefs = {
 legend: { display: false },
 tooltip: {
 backgroundColor: isDark ? '#1d1e20' : '#fff',
 titleColor: textColor, bodyColor: textColor,
 borderColor: borderColor, borderWidth: 1, padding: 10,
 bodyFont: { family: "'Roboto Slab', serif", size: 13 },
 titleFont: { family: "'Roboto Slab', serif", size: 13, weight: '300' }
 }
 };

 /* ---- Yearly totals bar chart ---- */
 var years = Object.keys(data.yearly_totals).sort();
 new Chart(document.getElementById('yearly-chart'), {
 type: 'bar',
 data: {
 labels: years,
 datasets: [{
 data: years.map(function(y) { return data.yearly_totals[y]; }),
 backgroundColor: barColor,
 borderRadius: 2
 }]
 },
 options: {
 responsive: true, maintainAspectRatio: false, animation: false,
 scales: {
 x: scaleDefs.x,
 y: Object.assign({ beginAtZero: true }, scaleDefs.y)
 },
 plugins: pluginDefs
 }
 });

 /* ---- Hour of day bar chart ---- */
 var hours = [];
 var hourlyCounts = [];
 for (var h = 0; h &lt; 24; h++) {
 hours.push(h + ':00');
 hourlyCounts.push(data.hourly[h] || data.hourly[String(h)] || 0);
 }
 new Chart(document.getElementById('hourly-chart'), {
 type: 'bar',
 data: {
 labels: hours,
 datasets: [{
 data: hourlyCounts,
 backgroundColor: barColor,
 borderRadius: 2
 }]
 },
 options: {
 responsive: true, maintainAspectRatio: false, animation: false,
 scales: {
 x: Object.assign({}, scaleDefs.x, { ticks: Object.assign({}, scaleDefs.x.ticks, { maxRotation: 45 }) }),
 y: Object.assign({ beginAtZero: true }, scaleDefs.y)
 },
 plugins: Object.assign({}, pluginDefs, {
 title: { display: true, text: 'Hour of Day', color: textColor, font: Object.assign({}, defaultFont, { size: 13 }) }
 })
 }
 });

 /* ---- Day of week bar chart ---- */
 var dayOrder = ['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday'];
 var dayLabels = ['Sun','Mon','Tue','Wed','Thu','Fri','Sat'];
 var dayCounts = dayOrder.map(function(d) { return data.day_of_week[d] || 0; });
 new Chart(document.getElementById('dow-chart'), {
 type: 'bar',
 data: {
 labels: dayLabels,
 datasets: [{
 data: dayCounts,
 backgroundColor: barColor,
 borderRadius: 2
 }]
 },
 options: {
 responsive: true, maintainAspectRatio: false, animation: false,
 scales: {
 x: scaleDefs.x,
 y: Object.assign({ beginAtZero: true }, scaleDefs.y)
 },
 plugins: Object.assign({}, pluginDefs, {
 title: { display: true, text: 'Day of Week', color: textColor, font: Object.assign({}, defaultFont, { size: 13 }) }
 })
 }
 });

 /* ---- Heatmap (canvas, not Chart.js) ---- */
 var heatCanvas = document.getElementById('heatmap-chart');
 var ctx = heatCanvas.getContext('2d');
 var rect = heatCanvas.parentElement.getBoundingClientRect();
 heatCanvas.width = rect.width;
 heatCanvas.height = rect.height;

 var heatDays = ['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday'];
 var heatDayLabels = ['Sun','Mon','Tue','Wed','Thu','Fri','Sat'];
 var maxHeat = 0;
 heatDays.forEach(function(day) {
 if (!data.heatmap[day]) return;
 for (var hh = 0; hh &lt; 24; hh++) {
 var val = data.heatmap[day][hh] || data.heatmap[day][String(hh)] || 0;
 if (val &gt; maxHeat) maxHeat = val;
 }
 });

 var leftPad = 40;
 var topPad = 20;
 var bottomPad = 30;
 var rightPad = 10;
 var cellW = (heatCanvas.width - leftPad - rightPad) / 24;
 var cellH = (heatCanvas.height - topPad - bottomPad) / 7;

 ctx.font = '11px "Roboto Slab", serif';
 ctx.textAlign = 'right';
 ctx.textBaseline = 'middle';
 ctx.fillStyle = textColor;
 heatDayLabels.forEach(function(label, di) {
 ctx.fillText(label, leftPad - 6, topPad + di * cellH + cellH / 2);
 });

 ctx.textAlign = 'center';
 ctx.textBaseline = 'top';
 for (var hh = 0; hh &lt; 24; hh++) {
 if (hh % 3 === 0) {
 ctx.fillStyle = textColor;
 ctx.fillText(hh + ':00', leftPad + hh * cellW + cellW / 2, topPad + 7 * cellH + 6);
 }
 }

 heatDays.forEach(function(day, di) {
 for (var hh = 0; hh &lt; 24; hh++) {
 var val = 0;
 if (data.heatmap[day]) {
 val = data.heatmap[day][hh] || data.heatmap[day][String(hh)] || 0;
 }
 var intensity = maxHeat &gt; 0 ? val / maxHeat : 0;
 var x = leftPad + hh * cellW;
 var y = topPad + di * cellH;

 if (isDark) {
 var g = Math.round(40 + intensity * 215);
 ctx.fillStyle = 'rgb(' + g + ',' + g + ',' + g + ')';
 } else {
 var g = Math.round(255 - intensity * 230);
 ctx.fillStyle = 'rgb(' + g + ',' + g + ',' + g + ')';
 }
 ctx.fillRect(x + 1, y + 1, cellW - 2, cellH - 2);
 }
 });

 /* ---- People percentage line chart ---- */
 var peopleYears = Object.keys(data.people_by_year).sort();
 var peoplePct = peopleYears.map(function(y) {
 var d = data.people_by_year[y];
 return d.total &gt; 0 ? Math.round(d.with_people / d.total * 1000) / 10 : 0;
 });
 new Chart(document.getElementById('people-chart'), {
 type: 'line',
 data: {
 labels: peopleYears,
 datasets: [{
 data: peoplePct,
 borderColor: accentColor,
 backgroundColor: accentColor + '22',
 fill: true,
 borderWidth: 2, pointRadius: 3, tension: 0.3
 }]
 },
 options: {
 responsive: true, maintainAspectRatio: false, animation: false,
 scales: {
 x: scaleDefs.x,
 y: Object.assign({ beginAtZero: true, max: 100, ticks: Object.assign({}, scaleDefs.y.ticks, { callback: function(v) { return v + '%'; } }) }, scaleDefs.y)
 },
 plugins: Object.assign({}, pluginDefs, {
 tooltip: Object.assign({}, pluginDefs.tooltip, {
 callbacks: { label: function(ctx) { return ctx.parsed.y + '% with people'; } }
 })
 })
 }
 });

 /* ---- GPS percentage line chart ---- */
 var gpsYears = Object.keys(data.gps_by_year).sort();
 var gpsPct = gpsYears.map(function(y) {
 var d = data.gps_by_year[y];
 return d.total &gt; 0 ? Math.round(d.with_gps / d.total * 1000) / 10 : 0;
 });
 new Chart(document.getElementById('gps-chart'), {
 type: 'line',
 data: {
 labels: gpsYears,
 datasets: [{
 data: gpsPct,
 borderColor: accentColor,
 backgroundColor: accentColor + '22',
 fill: true,
 borderWidth: 2, pointRadius: 3, tension: 0.3
 }]
 },
 options: {
 responsive: true, maintainAspectRatio: false, animation: false,
 scales: {
 x: scaleDefs.x,
 y: Object.assign({ beginAtZero: true, max: 100, ticks: Object.assign({}, scaleDefs.y.ticks, { callback: function(v) { return v + '%'; } }) }, scaleDefs.y)
 },
 plugins: Object.assign({}, pluginDefs, {
 tooltip: Object.assign({}, pluginDefs.tooltip, {
 callbacks: { label: function(ctx) { return ctx.parsed.y + '% with GPS'; } }
 })
 })
 }
 });

 /* ---- Aperture horizontal bar chart (top 10) ---- */
 var apertureEntries = Object.keys(data.aperture_dist).map(function(k) {
 return { label: 'f/' + k, count: data.aperture_dist[k], val: parseFloat(k) };
 }).sort(function(a, b) { return b.count - a.count; }).slice(0, 10)
 .sort(function(a, b) { return a.val - b.val; });
 new Chart(document.getElementById('aperture-chart'), {
 type: 'bar',
 data: {
 labels: apertureEntries.map(function(e) { return e.label; }),
 datasets: [{
 data: apertureEntries.map(function(e) { return e.count; }),
 backgroundColor: barColor,
 borderRadius: 2
 }]
 },
 options: {
 responsive: true, maintainAspectRatio: false, animation: false,
 indexAxis: 'y',
 scales: {
 x: Object.assign({ beginAtZero: true }, scaleDefs.x),
 y: scaleDefs.y
 },
 plugins: Object.assign({}, pluginDefs, {
 title: { display: true, text: 'Aperture', color: textColor, font: Object.assign({}, defaultFont, { size: 13 }) }
 })
 }
 });

 /* ---- Focal length horizontal bar chart (top 10) ---- */
 var focalEntries = Object.keys(data.focal_dist).map(function(k) {
 return { label: k + 'mm', count: data.focal_dist[k], val: parseInt(k) };
 }).sort(function(a, b) { return b.count - a.count; }).slice(0, 10)
 .sort(function(a, b) { return a.val - b.val; });
 new Chart(document.getElementById('focal-chart'), {
 type: 'bar',
 data: {
 labels: focalEntries.map(function(e) { return e.label; }),
 datasets: [{
 data: focalEntries.map(function(e) { return e.count; }),
 backgroundColor: barColor,
 borderRadius: 2
 }]
 },
 options: {
 responsive: true, maintainAspectRatio: false, animation: false,
 indexAxis: 'y',
 scales: {
 x: Object.assign({ beginAtZero: true }, scaleDefs.x),
 y: scaleDefs.y
 },
 plugins: Object.assign({}, pluginDefs, {
 title: { display: true, text: 'Focal Length', color: textColor, font: Object.assign({}, defaultFont, { size: 13 }) }
 })
 }
 });

 /* ---- ISO over time line chart ---- */
 var isoYears = Object.keys(data.iso_by_year).sort();
 new Chart(document.getElementById('iso-time-chart'), {
 type: 'line',
 data: {
 labels: isoYears,
 datasets: [{
 data: isoYears.map(function(y) { return data.iso_by_year[y]; }),
 borderColor: accentColor,
 borderWidth: 2, pointRadius: 3, tension: 0.3,
 fill: false
 }]
 },
 options: {
 responsive: true, maintainAspectRatio: false, animation: false,
 scales: {
 x: Object.assign({}, scaleDefs.x, { ticks: Object.assign({}, scaleDefs.x.ticks, { maxRotation: 45 }) }),
 y: Object.assign({ beginAtZero: true }, scaleDefs.y)
 },
 plugins: Object.assign({}, pluginDefs, {
 title: { display: true, text: 'Avg ISO by Year', color: textColor, font: Object.assign({}, defaultFont, { size: 13 }) }
 })
 }
 });

 /* ---- Resolution line chart ---- */
 var resYears = Object.keys(data.resolution_by_year).sort();
 new Chart(document.getElementById('resolution-chart'), {
 type: 'line',
 data: {
 labels: resYears,
 datasets: [{
 data: resYears.map(function(y) { return data.resolution_by_year[y]; }),
 borderColor: accentColor,
 borderWidth: 2, pointRadius: 3, tension: 0.3,
 fill: false
 }]
 },
 options: {
 responsive: true, maintainAspectRatio: false, animation: false,
 scales: {
 x: scaleDefs.x,
 y: Object.assign({ beginAtZero: true, ticks: Object.assign({}, scaleDefs.y.ticks, { callback: function(v) { return v + ' MP'; } }) }, scaleDefs.y)
 },
 plugins: Object.assign({}, pluginDefs, {
 title: { display: true, text: 'Avg Resolution', color: textColor, font: Object.assign({}, defaultFont, { size: 13 }) },
 tooltip: Object.assign({}, pluginDefs.tooltip, {
 callbacks: { label: function(ctx) { return ctx.parsed.y + ' MP'; } }
 })
 })
 }
 });

 /* ---- Exposure distribution horizontal bar chart ---- */
 var exposureKeys = Object.keys(data.exposure_dist);
 var exposureCounts = exposureKeys.map(function(k) { return data.exposure_dist[k]; });
 new Chart(document.getElementById('exposure-chart'), {
 type: 'bar',
 data: {
 labels: exposureKeys,
 datasets: [{
 data: exposureCounts,
 backgroundColor: barColor,
 borderRadius: 2
 }]
 },
 options: {
 responsive: true, maintainAspectRatio: false, animation: false,
 indexAxis: 'y',
 scales: {
 x: Object.assign({ beginAtZero: true }, scaleDefs.x),
 y: scaleDefs.y
 },
 plugins: Object.assign({}, pluginDefs, {
 title: { display: true, text: 'Shutter Speed', color: textColor, font: Object.assign({}, defaultFont, { size: 13 }) }
 })
 }
 });

 /* ---- Monthly volume area chart (the heartbeat) ---- */
 var months = Object.keys(data.monthly_volume).sort();
 var monthlyCounts = months.map(function(m) { return data.monthly_volume[m]; });
 /* Show every 12th label (January each year) */
 var monthLabels = months.map(function(m) {
 return m.substring(5) === '01' ? m.substring(0, 4) : '';
 });
 new Chart(document.getElementById('monthly-chart'), {
 type: 'line',
 data: {
 labels: monthLabels,
 datasets: [{
 data: monthlyCounts,
 borderColor: isDark ? '#e0e0e0' : '#333',
 backgroundColor: isDark ? 'rgba(255,255,255,0.08)' : 'rgba(0,0,0,0.06)',
 fill: true,
 borderWidth: 1.5, pointRadius: 0, tension: 0.2
 }]
 },
 options: {
 responsive: true, maintainAspectRatio: false, animation: false,
 scales: {
 x: Object.assign({}, scaleDefs.x, {
 ticks: Object.assign({}, scaleDefs.x.ticks, {
 maxRotation: 0,
 autoSkip: false,
 callback: function(val, i) { return monthLabels[i] || null; }
 })
 }),
 y: Object.assign({ beginAtZero: true }, scaleDefs.y)
 },
 plugins: pluginDefs
 }
 });
 }

 new MutationObserver(function() { location.reload(); })
 .observe(document.documentElement, { attributes: true, attributeFilter: ['data-theme'] });
})();
&lt;/script&gt;</content:encoded><media:content url="https://mrmatt.io/images/MattWalker-avatar.png" medium="image"/></item><item><title>Every Camera I've Ever Owned</title><link>https://mrmatt.io/posts/camera-gear-timeline/</link><pubDate>Tue, 10 Mar 2026 00:00:00 -0400</pubDate><guid>https://mrmatt.io/posts/camera-gear-timeline/</guid><description>35 cameras, 22 phones, and a Canon Rebel — 22 years of photography gear traced through 94,000 photos of EXIF data.</description><content:encoded>&lt;p&gt;These are all the cameras I&amp;rsquo;ve ever owned &amp;ndash; starting with a Canon Rebel 35mm SLR in high school and ending with the latest Pixel. The film rolls live in shoeboxes, not in metadata, but every digital photo carries its camera&amp;rsquo;s name quietly in the EXIF header. I scanned the EXIF headers of 94,000 Google Photos and found 75 different cameras spanning 22 years. Some were mine. Some were friends&amp;rsquo; phones showing up in shared albums. After filtering down to just the cameras I actually owned, the timeline tells a clear story.&lt;/p&gt;
&lt;p&gt;One caveat: this is JPEG only. The pipeline skips raw files &amp;ndash; CR2s from the Canons, NEFs from the Nikons &amp;ndash; because Google Takeout bundles the JPEGs but not always the raws. So the real photo counts for the DSLR era are higher than what&amp;rsquo;s shown here. The raws live on hard drives, not in the cloud.&lt;/p&gt;
&lt;p&gt;After &lt;a href="https://mrmatt.io/posts/google-takeout-gallery-curation/"&gt;scanning 723GB of Google Takeout data&lt;/a&gt; to build this site&amp;rsquo;s photography gallery, I had the pipeline and the raw material. So I pointed a Python script at all 15 zip files, read the first 64KB of every JPEG to extract the EXIF header, normalized the camera names, and loaded it all into a SQLite database.&lt;/p&gt;
&lt;h3 id="the-timeline"&gt;The timeline&lt;/h3&gt;
&lt;div style="position:relative;width:100%;aspect-ratio:1/1;margin:2rem 0;"&gt;
&lt;canvas id="timeline-chart"&gt;&lt;/canvas&gt;
&lt;/div&gt;
&lt;p&gt;The pattern is clear: dedicated cameras dominated until around 2013, then phones took over completely. The transition wasn&amp;rsquo;t gradual &amp;ndash; it was a cliff. Once the Samsung Galaxy Note II arrived, the Canon PowerShots and Nikon DSLRs collected dust.&lt;/p&gt;
&lt;h3 id="the-eras"&gt;The eras&lt;/h3&gt;
&lt;p&gt;The earliest camera in the data is the Sony Cyber-shot DSC-V1, which became the real workhorse of the compact era: 3,845 photos over seven years. Canon PowerShots, Pentax compacts, more Kodak EasyShares &amp;ndash; a new camera every year or two, each one a modest upgrade. This was the age of carrying a dedicated device in a belt pouch and hoping the batteries held.&lt;/p&gt;
&lt;p&gt;The DSLR era was really just one camera: the Nikon D3100. It was the real commitment &amp;ndash; 5,729 photos over six years. It lived in the camera bag for every family trip and holiday. But carrying a body, two lenses, and a charger loses its appeal when the thing in your pocket takes a photo that&amp;rsquo;s good enough.&lt;/p&gt;
&lt;p&gt;The phone revolution hit around 2012. The Samsung Galaxy Note II changed everything &amp;ndash; 11,039 photos on a single device. Then the Note 4 pushed even further: 12,704 photos, making it the single most-used camera I&amp;rsquo;ve ever owned. The compact cameras and DSLRs were done.&lt;/p&gt;
&lt;p&gt;The Pixel years run from 2016 to present. Google Pixel XL, 2 XL, 3 XL, 4 XL, 5, 6 Pro, 8 Pro, 9 Pro XL, 10 Pro XL. Nine Pixels in ten years. The best camera is the one you have with you, and the phone won that argument decisively.&lt;/p&gt;
&lt;h3 id="phone-vs-dedicated-camera"&gt;Phone vs. dedicated camera&lt;/h3&gt;
&lt;div style="position:relative;width:100%;aspect-ratio:5/6;margin:2rem 0;"&gt;
&lt;canvas id="category-chart"&gt;&lt;/canvas&gt;
&lt;/div&gt;
&lt;p&gt;The crossover happened around 2013. By 2018, dedicated cameras were effectively zero. Computational photography didn&amp;rsquo;t just match optical quality &amp;ndash; it made the camera you always have with you the best camera.&lt;/p&gt;
&lt;h3 id="the-cameras"&gt;The cameras&lt;/h3&gt;
&lt;div id="canon-rebel-card" style="padding:1.25rem 0;border-bottom:1px solid rgba(128,128,128,0.2);"&gt;
&lt;div style="display:flex;gap:1.25rem;align-items:flex-start;"&gt;
&lt;img src="https://mrmatt.io/images/camera-timeline/canon-eos-rebel.webp" alt="Canon EOS Rebel" loading="lazy" class="camera-card-img" onerror="this.style.display='none'"&gt;
&lt;div style="min-width:0;flex:1;"&gt;
&lt;strong style="font-size:1.05rem;"&gt;Canon EOS Rebel&lt;/strong&gt;&lt;br&gt;
&lt;span style="color:var(--secondary);font-size:0.85rem;"&gt;~1998 &amp;middot; 35mm film SLR &amp;middot; No EXIF data&lt;/span&gt;&lt;br&gt;
&lt;span style="font-size:0.85rem;color:var(--secondary);"&gt;The one that started it all. A run-of-the-mill Canon Rebel that I used in high school photography class. The rolls of Kodak Gold and Tri-X are in a shoebox somewhere, but this is where it began.&lt;/span&gt;
&lt;/div&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div id="camera-cards"&gt;&lt;/div&gt;
&lt;h3 id="how-i-built-this"&gt;How I built this&lt;/h3&gt;
&lt;p&gt;Nearly a terabyte of Google Takeout data. Twenty-two years of photos. The pipeline is four Python scripts: &lt;a href="https://github.com/shortnd/MrMatt.io/blob/master/tools/scan_camera_exif.py"&gt;&lt;code&gt;scan_camera_exif.py&lt;/code&gt;&lt;/a&gt; rips through every JPEG across 15 zip files, reads the first 64KB to extract the EXIF header, and loads it into SQLite &amp;ndash; three parallel workers, 30 minutes. &lt;a href="https://github.com/shortnd/MrMatt.io/blob/master/tools/normalize_cameras.py"&gt;&lt;code&gt;normalize_cameras.py&lt;/code&gt;&lt;/a&gt; maps raw EXIF strings to clean names (Samsung&amp;rsquo;s EXIF says &amp;ldquo;SM-G920V&amp;rdquo; not &amp;ldquo;Galaxy S6&amp;rdquo;). &lt;a href="https://github.com/shortnd/MrMatt.io/blob/master/tools/export_camera_timeline.py"&gt;&lt;code&gt;export_camera_timeline.py&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://github.com/shortnd/MrMatt.io/blob/master/tools/export_photo_stats.py"&gt;&lt;code&gt;export_photo_stats.py&lt;/code&gt;&lt;/a&gt; query the database and output the JSON that powers these charts. The result: 94,000 photos with every camera, aperture, ISO, focal length, and GPS coordinate preserved in a queryable database. Charts are self-hosted &lt;a href="https://www.chartjs.org/"&gt;Chart.js&lt;/a&gt; &amp;ndash; no CDN, no npm, just a single JS file.&lt;/p&gt;
&lt;p&gt;For the deep dive into what all that EXIF data reveals about shooting patterns, see &lt;a href="https://mrmatt.io/posts/photography-by-the-numbers/"&gt;94,000 Photos by the Numbers&lt;/a&gt;.&lt;/p&gt;
&lt;script src="https://mrmatt.io/js/chart.umd.min.js"&gt;&lt;/script&gt;
&lt;script&gt;
(function() {
 fetch('/data/camera-timeline.json')
 .then(function(r) { return r.json(); })
 .then(function(data) { renderCharts(data); });

 /* Cameras I actually owned (exclude friends' shared photos) */
 var NOT_OWNED = [
 'Apple iPhone 5s', 'Apple iPhone 5c', 'Apple iPhone 6',
 'Apple iPhone 6s', 'Apple iPhone 6s Plus', 'Apple iPhone 7',
 'Apple iPhone 8', 'Apple iPhone X', 'Apple iPhone XR',
 'Apple iPhone 11', 'Apple iPhone 12 Mini',
 'Apple iPhone 16', 'Apple iPhone 16 Pro',
 'Canon EOS Rebel XTi', 'Canon EOS 5D Mark II',
 'Canon EOS 5D Mark III', 'Canon EOS 5D Mark IV',
 'Sony A6000', 'DJI Phantom 4',
 'Samsung Galaxy S24', 'Samsung Galaxy Tab S2',
 'Pentax Optio S5z', 'Pentax Optio 30',
 'Canon PowerShot SD400', 'Canon PowerShot SD450', 'Canon PowerShot SD1000',
 'Nikon D3000',
 'Google Pixel 6a', 'Google Pixel 8a',
 'Sony Cyber-shot DSC-P100',
 'Kodak EasyShare DX4900', 'Kodak EasyShare C533', 'Kodak EasyShare M893'
 ];

 function isOwned(name) {
 return NOT_OWNED.indexOf(name) === -1;
 }

 function renderCharts(data) {
 var isDark = document.documentElement.getAttribute('data-theme') === 'dark';
 var textColor = isDark ? '#e0e0e0' : '#090909';
 var gridColor = isDark ? 'rgba(255,255,255,0.08)' : 'rgba(0,0,0,0.06)';
 var borderColor = isDark ? 'rgba(255,255,255,0.15)' : 'rgba(0,0,0,0.12)';
 var barColor = isDark ? '#888' : '#555';
 var accentColor = '#4c81b2';

 var PALETTE = isDark
 ? ['#e8e8e8','#d0d0d0','#b8b8b8','#a0a0a0','#888','#707070','#585858',
 '#4c81b2','#5a8fbd','#6b9dc7','#7cabd1','#8db9db',
 '#f0f0f0','#c8c8c8','#a8a8a8','#888','#686868','#484848','#383838']
 : ['#1a1a1a','#2d2d2d','#404040','#535353','#666','#797979','#8c8c8c',
 '#4c81b2','#5a8fbd','#6b9dc7','#7cabd1','#8db9db',
 '#9f9f9f','#b2b2b2','#c5c5c5','#d8d8d8','#ebebeb','#f0f0f0','#333'];

 var defaultFont = { family: "'Roboto Slab', serif", size: 12, weight: '300' };
 var scaleDefs = {
 x: { grid: { color: gridColor }, ticks: { color: textColor, font: defaultFont } },
 y: { grid: { color: gridColor }, ticks: { color: textColor, font: defaultFont } }
 };
 var pluginDefs = {
 legend: { display: false },
 tooltip: {
 backgroundColor: isDark ? '#1d1e20' : '#fff',
 titleColor: textColor, bodyColor: textColor,
 borderColor: borderColor, borderWidth: 1, padding: 10,
 bodyFont: { family: "'Roboto Slab', serif", size: 13 },
 titleFont: { family: "'Roboto Slab', serif", size: 13, weight: '300' }
 }
 };

 /* ---- Chart 1: Timeline stacked area (owned cameras with 50+ photos) ---- */
 var years = Object.keys(data.timeline).sort();
 var cameraNames = data.cameras
 .filter(function(c) { return c.photo_count &gt;= 50 &amp;&amp; isOwned(c.display_name); })
 .sort(function(a, b) { return (a.first_seen || '').localeCompare(b.first_seen || ''); })
 .map(function(c) { return c.display_name; });
 var timelineDS = cameraNames.map(function(name, i) {
 return {
 label: name,
 data: years.map(function(y) { return (data.timeline[y] &amp;&amp; data.timeline[y][name]) || 0; }),
 fill: true,
 backgroundColor: PALETTE[i % PALETTE.length] + '88',
 borderColor: PALETTE[i % PALETTE.length],
 borderWidth: 1, pointRadius: 0, tension: 0.3
 };
 });
 new Chart(document.getElementById('timeline-chart'), {
 type: 'line',
 data: { labels: years, datasets: timelineDS },
 options: {
 responsive: true, maintainAspectRatio: false, animation: false,
 scales: { x: scaleDefs.x, y: Object.assign({ stacked: true, beginAtZero: true }, scaleDefs.y) },
 plugins: Object.assign({}, pluginDefs, {
 tooltip: Object.assign({}, pluginDefs.tooltip, {
 mode: 'index', intersect: false,
 filter: function(item) { return item.raw &gt; 0; }
 })
 })
 }
 });

 /* ---- Chart 2: Category breakdown (stacked bar) ---- */
 var catYears = Object.keys(data.category_by_year).sort();
 var categories = ['phone','compact','dslr','drone','action','tablet'];
 var catColors = isDark
 ? { phone:'#e0e0e0', compact:'#888', dslr:'#bbb', drone:'#4c81b2', action:'#5a8fbd', tablet:'#555' }
 : { phone:'#333', compact:'#999', dslr:'#666', drone:'#4c81b2', action:'#5a8fbd', tablet:'#bbb' };
 var catLabels = { phone:'Phone', compact:'Compact', dslr:'DSLR', drone:'Drone', action:'Action Cam', tablet:'Tablet' };
 var catDS = categories.map(function(cat) {
 return {
 label: catLabels[cat] || cat,
 data: catYears.map(function(y) { return (data.category_by_year[y] &amp;&amp; data.category_by_year[y][cat]) || 0; }),
 backgroundColor: catColors[cat] || '#999', borderRadius: 2
 };
 }).filter(function(ds) { return ds.data.some(function(v) { return v &gt; 0; }); });
 new Chart(document.getElementById('category-chart'), {
 type: 'bar',
 data: { labels: catYears, datasets: catDS },
 options: {
 responsive: true, maintainAspectRatio: false, animation: false,
 scales: { x: Object.assign({ stacked: true }, scaleDefs.x), y: Object.assign({ stacked: true, beginAtZero: true }, scaleDefs.y) },
 plugins: Object.assign({}, pluginDefs, {
 legend: { display: true, labels: { color: textColor, font: defaultFont, boxWidth: 12, padding: 16 } }
 })
 }
 });

 /* ---- Camera cards with sparkline histograms ---- */
 var container = document.getElementById('camera-cards');
 if (!container) return;

 /* Build yearly counts per camera for sparklines */
 var allYears = Object.keys(data.timeline).sort();
 var firstYear = parseInt(allYears[0]);
 var lastYear = parseInt(allYears[allYears.length - 1]);
 var yearSpan = lastYear - firstYear + 1;

 /* Find global max yearly count (across owned cameras) for consistent scaling */
 var globalMax = 0;
 data.cameras.forEach(function(cam) {
 if (!isOwned(cam.display_name) || cam.photo_count &lt; 10) return;
 allYears.forEach(function(y) {
 var cnt = (data.timeline[y] &amp;&amp; data.timeline[y][cam.display_name]) || 0;
 if (cnt &gt; globalMax) globalMax = cnt;
 });
 });

 var html = '';
 var cardIndex = 0;
 var cams = data.cameras
 .filter(function(c) { return c.photo_count &gt;= 10 &amp;&amp; isOwned(c.display_name); })
 .sort(function(a, b) { return (a.first_seen || '').localeCompare(b.first_seen || ''); });

 cams.forEach(function(cam) {
 var fy = cam.first_seen ? cam.first_seen.substring(0, 4) : '?';
 var ly = cam.last_seen ? cam.last_seen.substring(0, 4) : '?';
 var range = fy === ly ? fy : fy + ' \u2013 ' + ly;
 var pct = (cam.photo_count / data.summary.total_photos * 100).toFixed(1);
 var cat = cam.category ? cam.category.charAt(0).toUpperCase() + cam.category.slice(1) : '';
 var stats = [];
 if (cam.median_aperture) stats.push('f/' + cam.median_aperture.toFixed(1));
 if (cam.median_iso) stats.push('ISO ' + cam.median_iso);
 if (cam.median_focal_length_35mm) stats.push(cam.median_focal_length_35mm + 'mm');
 var slug = cam.display_name.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/(^-|-$)/g, '');
 var canvasId = 'spark-' + cardIndex;

 html += '&lt;div style="padding:1.25rem 0;border-bottom:1px solid ' + borderColor + ';"&gt;';
 html += '&lt;div style="display:flex;gap:1.25rem;align-items:flex-start;"&gt;';
 html += '&lt;img src="https://mrmatt.io/images/camera-timeline/' + slug + '.webp" alt="' + cam.display_name + '" loading="lazy" ';
 html += 'class="camera-card-img" ';
 html += 'onerror="this.style.display=\'none\'"&gt;';
 html += '&lt;div style="min-width:0;flex:1;"&gt;';
 html += '&lt;strong style="font-size:1.05rem;"&gt;' + cam.display_name + '&lt;/strong&gt;&lt;br&gt;';
 html += '&lt;span style="color:var(--secondary);font-size:0.85rem;"&gt;' + range + ' &amp;middot; ' + cam.photo_count.toLocaleString() + ' photos (' + pct + '%) &amp;middot; ' + cat + '&lt;/span&gt;';
 if (stats.length) html += '&lt;br&gt;&lt;span style="font-size:0.85rem;color:var(--secondary);"&gt;' + stats.join(' &amp;middot; ') + '&lt;/span&gt;';
 html += '&lt;/div&gt;&lt;/div&gt;';
 /* Sparkline histogram spanning full timeline */
 html += '&lt;div class="camera-sparkline"&gt;';
 html += '&lt;canvas id="' + canvasId + '"&gt;&lt;/canvas&gt;';
 html += '&lt;/div&gt;';
 html += '&lt;/div&gt;';

 cardIndex++;
 });
 container.innerHTML = html;

 /* Shared tooltip element for sparkline hover */
 var tip = document.createElement('div');
 tip.style.cssText = 'position:fixed;padding:3px 7px;font-size:0.75rem;font-family:"Roboto Slab",serif;'
 + 'border-radius:3px;pointer-events:none;z-index:9999;opacity:0;transition:opacity 0.1s;white-space:nowrap;'
 + 'background:' + (isDark ? '#1d1e20' : '#fff') + ';color:' + textColor + ';'
 + 'border:1px solid ' + borderColor + ';';
 document.body.appendChild(tip);

 /* Draw sparkline histograms */
 cardIndex = 0;
 cams.forEach(function(cam) {
 var canvas = document.getElementById('spark-' + cardIndex);
 if (!canvas) { cardIndex++; return; }
 var ctx = canvas.getContext('2d');
 var rect = canvas.getBoundingClientRect();
 var dpr = window.devicePixelRatio || 1;
 canvas.width = rect.width * dpr;
 canvas.height = rect.height * dpr;
 ctx.scale(dpr, dpr);
 var w = rect.width;
 var h = rect.height;

 var barW = Math.max(1, (w - yearSpan + 1) / yearSpan);
 var gap = 1;
 var totalBarW = barW + gap;

 /* Get this camera's yearly counts */
 var counts = allYears.map(function(y) {
 return (data.timeline[y] &amp;&amp; data.timeline[y][cam.display_name]) || 0;
 });
 var localMax = Math.max.apply(null, counts);

 /* Draw year bars */
 for (var i = 0; i &lt; yearSpan; i++) {
 var count = counts[i] || 0;
 var x = i * totalBarW;

 if (count === 0) {
 /* Empty year: faint tick mark */
 ctx.fillStyle = isDark ? 'rgba(255,255,255,0.06)' : 'rgba(0,0,0,0.04)';
 ctx.fillRect(x, h - 2, barW, 2);
 } else {
 /* Active year: height proportional to count, scaled to local max */
 var barH = Math.max(3, (count / localMax) * (h - 2));
 var intensity = count / localMax;
 if (intensity &gt; 0.8) {
 ctx.fillStyle = accentColor;
 } else {
 ctx.fillStyle = isDark
 ? 'rgba(255,255,255,' + (0.2 + intensity * 0.6) + ')'
 : 'rgba(0,0,0,' + (0.15 + intensity * 0.55) + ')';
 }
 ctx.fillRect(x, h - barH, barW, barH);
 }
 }

 /* Hover: show year + count tooltip */
 (function(cvs, cts, tw) {
 cvs.addEventListener('mousemove', function(e) {
 var cr = cvs.getBoundingClientRect();
 var mx = e.clientX - cr.left;
 var idx = Math.floor(mx / tw);
 if (idx &lt; 0 || idx &gt;= yearSpan) { tip.style.opacity = '0'; return; }
 var yr = allYears[idx];
 var cnt = cts[idx] || 0;
 tip.textContent = yr + (cnt &gt; 0 ? ' \u2014 ' + cnt.toLocaleString() + ' photos' : '');
 tip.style.left = (e.clientX + 10) + 'px';
 tip.style.top = (e.clientY - 28) + 'px';
 tip.style.opacity = '1';
 });
 cvs.addEventListener('mouseleave', function() {
 tip.style.opacity = '0';
 });
 })(canvas, counts, totalBarW);

 cardIndex++;
 });
 }

 /* Re-render on theme toggle */
 new MutationObserver(function() { location.reload(); })
 .observe(document.documentElement, { attributes: true, attributeFilter: ['data-theme'] });
})();
&lt;/script&gt;</content:encoded><media:content url="https://mrmatt.io/images/MattWalker-avatar.png" medium="image"/></item><item><title>Building MrMatt.io: Performance &amp; Security</title><link>https://mrmatt.io/posts/building-mrmatt-io-performance/</link><pubDate>Mon, 09 Mar 2026 00:00:00 -0400</pubDate><guid>https://mrmatt.io/posts/building-mrmatt-io-performance/</guid><description>Self-hosted fonts, auto-generated CSP hashes, security headers, accessibility, and the caching strategy for a static site.</description><content:encoded>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Building MrMatt.io&lt;/strong&gt; — a series on rebuilding this site from scratch.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href="https://mrmatt.io/posts/building-mrmatt-io-specs/"&gt;Spec-Driven Development&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://mrmatt.io/posts/building-mrmatt-io-migration/"&gt;The Migration&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://mrmatt.io/posts/building-mrmatt-io-photography/"&gt;The Photography Pipeline&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Performance &amp;amp; Security&lt;/strong&gt; (you are here)&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;p&gt;A static site should be fast and secure by default. In practice, &amp;ldquo;by default&amp;rdquo; gets you most of the way there. The last 20% is where it gets interesting.&lt;/p&gt;
&lt;h3 id="fonts"&gt;Fonts&lt;/h3&gt;
&lt;p&gt;PaperMod pulls its heading font from Google Fonts (Roboto Slab). That means two DNS lookups, a CSS fetch, then the font download— all before the first meaningful paint.&lt;/p&gt;
&lt;p&gt;I self-hosted instead. Downloaded the WOFF2 files (Latin and Latin Extended), dropped them in &lt;code&gt;static/fonts/&lt;/code&gt;, wrote &lt;code&gt;@font-face&lt;/code&gt; with &lt;code&gt;font-display: swap&lt;/code&gt; and variable weight:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-css" data-lang="css"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="k"&gt;font-face&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;font-family&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Roboto Slab&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;font-weight&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nt"&gt;100&lt;/span&gt; &lt;span class="nt"&gt;900&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;font-display&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nt"&gt;swap&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;src&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nt"&gt;url&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;/fonts/roboto-slab-latin.woff2&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nt"&gt;format&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;woff2&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;unicode-range&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nt"&gt;U&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="nt"&gt;0000-00FF&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;U&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="nt"&gt;0131&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;U&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="nt"&gt;0152-0153&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;U&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="nt"&gt;02BB-02BC&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="o"&gt;...;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Two external connections eliminated. The browser shows a system font immediately, swaps to Roboto Slab when it loads from the same origin. Brief flash of unstyled text, but barely noticeable— and far better than invisible text waiting on a third-party server.&lt;/p&gt;
&lt;p&gt;Font files get one-year immutable cache headers. A font update means a new URL, not a cache invalidation.&lt;/p&gt;
&lt;h3 id="images"&gt;Images&lt;/h3&gt;
&lt;p&gt;Every photo in the gallery uses &lt;code&gt;srcset&lt;/code&gt; with three thumbnail sizes in WebP:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;img&lt;/span&gt; &lt;span class="na"&gt;srcset&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;/photo_200.webp 200w, /photo_300.webp 300w, /photo_450.webp 450w&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="na"&gt;sizes&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;(max-width: 500px) 45vw, (max-width: 900px) 22vw, 14vw&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="na"&gt;loading&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;lazy&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="na"&gt;alt&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;...&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The &lt;code&gt;sizes&lt;/code&gt; breakpoints match the CSS grid— 2 columns on mobile (~45vw each), 4 on tablet (~22vw), 6-8 on desktop (~14vw). The browser picks the smallest image that covers the rendered size. A phone never downloads a 450px thumbnail when 200px fills its grid cell.&lt;/p&gt;
&lt;p&gt;Everything uses &lt;code&gt;loading=&amp;quot;lazy&amp;quot;&lt;/code&gt;. Combined with IntersectionObserver progressive loading in the gallery, the initial page load only fetches the first 16 visible thumbnails.&lt;/p&gt;
&lt;p&gt;Hugo generates all of this at build time. Output filenames are content-addressed, so unchanged images reuse cached versions across deployments.&lt;/p&gt;
&lt;h3 id="the-csp-story"&gt;The CSP story&lt;/h3&gt;
&lt;p&gt;This is my favorite part of the whole rebuild.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Phase 1:&lt;/strong&gt; Started with a strict Content Security Policy. &lt;code&gt;default-src 'none'&lt;/code&gt;, explicit allowances for self-hosted scripts, styles, fonts, images. Clean, secure, immediately broken— PaperMod uses inline &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; tags for theme toggle initialization.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Phase 2:&lt;/strong&gt; &lt;code&gt;'unsafe-inline'&lt;/code&gt;. The pragmatic fix. Works, but defeats the purpose of CSP for XSS protection. The site is static so the practical risk is low, but it felt wrong.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Phase 3:&lt;/strong&gt; Manual hashes. CSP supports &lt;code&gt;'sha256-...'&lt;/code&gt; directives that allow specific inline content by hash. I computed the SHA-256 of each inline block, added them to the &lt;code&gt;_headers&lt;/code&gt; file, removed &lt;code&gt;'unsafe-inline'&lt;/code&gt;. Precise. Secure. Satisfying.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Phase 4:&lt;/strong&gt; Production breaks. I changed the lightbox JavaScript— added touch swipe support— and forgot the hashes. They went stale. CSP blocked the updated scripts. Gallery lightbox stopped working in production. The dev server doesn&amp;rsquo;t enforce CSP, so I didn&amp;rsquo;t catch it locally.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Phase 5:&lt;/strong&gt; Auto-generated hashes. A 50-line post-build script that runs after &lt;code&gt;hugo --minify&lt;/code&gt;:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Scans every HTML file in &lt;code&gt;public/&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Finds all &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; tags&lt;/li&gt;
&lt;li&gt;Computes SHA-256 hash of each&lt;/li&gt;
&lt;li&gt;Reads &lt;code&gt;_headers.template&lt;/code&gt; with &lt;code&gt;{{SCRIPT_HASHES}}&lt;/code&gt; and &lt;code&gt;{{STYLE_HASHES}}&lt;/code&gt; placeholders&lt;/li&gt;
&lt;li&gt;Replaces placeholders with collected hashes&lt;/li&gt;
&lt;li&gt;Writes &lt;code&gt;public/_headers&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Now the CSP hashes are always in sync. I can change any inline script and the next build updates the policy automatically. No more stale hashes, no more broken production.&lt;/p&gt;
&lt;p&gt;The pattern keeps repeating— the correct solution isn&amp;rsquo;t always the first one, and the maintenance-free version is worth the extra iteration.&lt;/p&gt;
&lt;h3 id="security-headers"&gt;Security headers&lt;/h3&gt;
&lt;p&gt;The full set, via Cloudflare Pages&amp;rsquo; &lt;code&gt;_headers&lt;/code&gt; file:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;X-Content-Type-Options: nosniff
X-Frame-Options: DENY
Referrer-Policy: strict-origin-when-cross-origin
Permissions-Policy: camera=(), microphone=(), geolocation=()
Strict-Transport-Security: max-age=63072000; includeSubDomains; preload
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;HSTS with preload (two-year max-age). X-Frame-Options DENY to block clickjacking. Permissions-Policy denying camera, mic, and geolocation— a blog has no business requesting those. Referrer-Policy that shares the origin but not the full URL path.&lt;/p&gt;
&lt;p&gt;The CSP rounds it out: &lt;code&gt;default-src 'none'&lt;/code&gt; with explicit allowances for scripts, styles, fonts, images, GitHub API connections, YouTube/Maps frames, service worker, and PWA manifest. Everything else denied.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Mozilla Observatory— A+ score, 125/100, all 10 tests passed" loading="lazy" src="https://mrmatt.io/images/building-mrmatt-io/observatory.png"&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://observatory.mozilla.org/analyze/mrmatt.io"&gt;A+ on Mozilla Observatory&lt;/a&gt;. Plus a &lt;code&gt;/.well-known/security.txt&lt;/code&gt; with contact info for responsible disclosure.&lt;/p&gt;
&lt;h3 id="accessibility"&gt;Accessibility&lt;/h3&gt;
&lt;p&gt;I&amp;rsquo;ve been interested in &lt;a href="https://mrmatt.io/posts/broadening-horizons-in-web-accessibility/"&gt;web accessibility&lt;/a&gt; since the 2016 rebuild. This time I went further.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Skip links.&lt;/strong&gt; A hidden &amp;ldquo;Skip to content&amp;rdquo; link appears on Tab focus before the header. Keyboard users jump straight to content without tabbing through nav.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Focus trapping.&lt;/strong&gt; The photo lightbox is a modal. Tab and Shift+Tab cycle through its interactive elements without escaping to the page behind it. Find all focusable elements, intercept Tab on the last (wrap to first) and Shift+Tab on the first (wrap to last).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;ARIA.&lt;/strong&gt; The lightbox has &lt;code&gt;role=&amp;quot;dialog&amp;quot;&lt;/code&gt;, &lt;code&gt;aria-modal=&amp;quot;true&amp;quot;&lt;/code&gt;, &lt;code&gt;aria-label&lt;/code&gt;. Nav buttons and close button all have descriptive labels. Screen readers can actually navigate it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Reduced motion.&lt;/strong&gt; A global CSS rule kills all transitions when the OS says to:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-css" data-lang="css"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="k"&gt;media&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;prefers-reduced-motion&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nt"&gt;reduce&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;*,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nd"&gt;before&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nd"&gt;after&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;transition&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;none&lt;/span&gt; &lt;span class="cp"&gt;!important&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The lightbox JavaScript checks the same preference— if reduced motion is on, photo transitions happen instantly instead of the 150ms fade.&lt;/p&gt;
&lt;p&gt;&lt;img alt="The 404 page— avatar, &amp;ldquo;Nothing&amp;rsquo;s deployed here,&amp;rdquo; and a monospace &amp;ldquo;cd ~&amp;rdquo; button" loading="lazy" src="https://mrmatt.io/images/building-mrmatt-io/404-page.png"&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Semantic HTML.&lt;/strong&gt; &lt;code&gt;&amp;lt;main&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;nav&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;article&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;header&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;footer&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;section&amp;gt;&lt;/code&gt; used appropriately. Logical heading hierarchy. Alt text on all images— AI-generated for photos, manual for everything else. Descriptive link text.&lt;/p&gt;
&lt;h3 id="caching"&gt;Caching&lt;/h3&gt;
&lt;p&gt;Static sites have an advantage: content doesn&amp;rsquo;t change between deployments. The strategy is aggressive.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Fingerprinted assets&lt;/strong&gt; — Hugo puts a content hash in CSS and JS filenames. &lt;code&gt;Cache-Control: public, max-age=31536000, immutable&lt;/code&gt;. Cached forever— a change produces a new URL.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Fonts&lt;/strong&gt; — One-year immutable. They don&amp;rsquo;t change.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Photos&lt;/strong&gt; — One-year immutable for thumbnails and full-size. Content-addressed by Hugo&amp;rsquo;s pipeline.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;HTML&lt;/strong&gt; — No explicit cache headers. Always fresh, always pointing to the latest fingerprinted assets.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;/fonts/*
 Cache-Control: public, max-age=31536000, immutable

/photography/*/photo*
 Cache-Control: public, max-age=31536000, immutable
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;A repeat visitor downloads almost nothing— just the HTML, which tells the browser everything else is already cached.&lt;/p&gt;
&lt;h3 id="adding-up"&gt;Adding up&lt;/h3&gt;
&lt;p&gt;None of this is individually groundbreaking. Self-hosted fonts, responsive images, CSP, semantic HTML— these are documented best practices. What matters is applying all of them.&lt;/p&gt;
&lt;p&gt;First visit loads fast because images are correctly sized and lazy-loaded. Second visit loads faster because everything is immutably cached. Security works because CSP, HSTS, frame protection, and permissions policy reinforce each other. Accessibility works because skip links, focus trapping, semantic HTML, and motion preferences all contribute.&lt;/p&gt;
&lt;p&gt;A static site built with care is better than most dynamic sites. There are just fewer things to get wrong.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;This is the last post in the &lt;a href="https://mrmatt.io/tags/building-mrmatt-io/"&gt;Building MrMatt.io&lt;/a&gt; series. Start with &lt;a href="https://mrmatt.io/posts/building-mrmatt-io-specs/"&gt;Spec-Driven Development&lt;/a&gt; if you haven&amp;rsquo;t read the others.&lt;/em&gt;&lt;/p&gt;</content:encoded><media:content url="https://mrmatt.io/images/MattWalker-avatar.png" medium="image"/></item><item><title>Building MrMatt.io: The Photography Pipeline</title><link>https://mrmatt.io/posts/building-mrmatt-io-photography/</link><pubDate>Sun, 08 Mar 2026 00:00:00 -0500</pubDate><guid>https://mrmatt.io/posts/building-mrmatt-io-photography/</guid><description>End-to-end from phone camera to published gallery— a PWA upload tool, AI descriptions, and Hugo image processing.</description><content:encoded>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Building MrMatt.io&lt;/strong&gt; — a series on rebuilding this site from scratch.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href="https://mrmatt.io/posts/building-mrmatt-io-specs/"&gt;Spec-Driven Development&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://mrmatt.io/posts/building-mrmatt-io-migration/"&gt;The Migration&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The Photography Pipeline&lt;/strong&gt; (you are here)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://mrmatt.io/posts/building-mrmatt-io-performance/"&gt;Performance &amp;amp; Security&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h3 id="the-problem"&gt;The problem&lt;/h3&gt;
&lt;p&gt;I take photos on my phone. I want them on my website. The gap between those two things is enormous.&lt;/p&gt;
&lt;p&gt;Publishing a photo on a static site means: transfer the image to a computer, resize it, generate thumbnails, write a markdown file with front matter, commit to git, push, wait for CI. That&amp;rsquo;s five to ten minutes of mechanical work per photo— enough friction to guarantee I&amp;rsquo;ll never actually do it.&lt;/p&gt;
&lt;p&gt;I wanted to share a photo from my phone and have it show up on the site. So I built that.&lt;/p&gt;
&lt;h3 id="the-upload-tool"&gt;The upload tool&lt;/h3&gt;
&lt;p&gt;The upload tool is a Progressive Web App at &lt;code&gt;/upload/&lt;/code&gt;. Vanilla JavaScript, no framework, no build step. It handles five things:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Authentication.&lt;/strong&gt; GitHub OAuth with a CSRF state parameter. A Cloudflare Function at &lt;code&gt;/api/oauth-exchange&lt;/code&gt; handles the token exchange server-side so the client secret never touches the browser. Only my account is authorized.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Share target.&lt;/strong&gt; This is the key piece. The PWA manifest registers &lt;code&gt;/upload/&lt;/code&gt; as an Android share target. When I share a photo from my phone&amp;rsquo;s gallery, the service worker intercepts the POST, caches the image, and loads it into the app. It feels native— identical to sharing to any other app on the phone.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;EXIF dates.&lt;/strong&gt; The app parses JPEG EXIF headers in the browser to get the original capture date. It reads the binary APP1 marker, navigates the IFD entries, pulls &lt;code&gt;DateTimeOriginal&lt;/code&gt;. This way the published date matches when the photo was actually taken, not when I uploaded it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Resizing.&lt;/strong&gt; Canvas element resizes to 1600px max before upload. Keeps the upload small and gives Hugo a reasonable source image. The full-resolution original is still available for download.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;AI descriptions.&lt;/strong&gt; This is the part that makes everything practical.&lt;/p&gt;
&lt;h3 id="ai-descriptions"&gt;AI descriptions&lt;/h3&gt;
&lt;p&gt;After selecting a photo, the app sends a base64 version to a Cloudflare Function at &lt;code&gt;/api/describe-photo&lt;/code&gt;. The function calls Claude Haiku&amp;rsquo;s vision API and gets back structured JSON:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;title&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Morning light on the Chesapeake&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;alt&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Sunlight reflecting off calm water with a wooden dock&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;description&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Early morning at the marina, the kind of light that only lasts ten minutes...&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Title becomes the heading. Alt text provides accessibility. Description appears in the lightbox. All three are editable before publishing.&lt;/p&gt;
&lt;p&gt;The feedback loop is what makes this actually useful. The first description is usually 80% right— good structure, decent alt text, plausible description. But it doesn&amp;rsquo;t know location names, can&amp;rsquo;t identify bird species, and sometimes misjudges the mood. I type feedback (&amp;ldquo;this is at Hart-Miller Island, the bird is a great blue heron&amp;rdquo;) and hit regenerate. One round usually gets it to 95%.&lt;/p&gt;
&lt;p&gt;This is dramatically better than writing descriptions from scratch. The AI handles the baseline and I refine the specifics.&lt;/p&gt;
&lt;h3 id="publishing"&gt;Publishing&lt;/h3&gt;
&lt;p&gt;When I hit &amp;ldquo;Upload,&amp;rdquo; the app talks directly to the GitHub API:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Gets the SHA of &lt;code&gt;main&lt;/code&gt;&amp;rsquo;s HEAD&lt;/li&gt;
&lt;li&gt;Creates a branch: &lt;code&gt;photo/{date}-{slug}&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Commits the image to &lt;code&gt;content/photography/{date}-{slug}/photo.jpg&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Commits an &lt;code&gt;index.md&lt;/code&gt; with YAML front matter&lt;/li&gt;
&lt;li&gt;Creates a PR and enables auto-merge&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The PR triggers CI. Hugo builds, CSP headers regenerate, and if it passes, the PR merges and Cloudflare deploys. Share photo, review description, publish— about two minutes. Live on the site within five.&lt;/p&gt;
&lt;p&gt;No git CLI. No local build. No image editing.&lt;/p&gt;
&lt;h3 id="the-gallery"&gt;The gallery&lt;/h3&gt;
&lt;p&gt;The photography section is a responsive CSS grid— two columns on mobile up to eight on wide screens. Each photo is a Hugo page bundle: a directory with &lt;code&gt;photo.jpg&lt;/code&gt; and &lt;code&gt;index.md&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img alt="The photography gallery— responsive CSS grid from phone to desktop" loading="lazy" src="https://mrmatt.io/images/building-mrmatt-io/gallery.png"&gt;&lt;/p&gt;
&lt;p&gt;Hugo&amp;rsquo;s image pipeline generates WebP thumbnails at 200px, 300px, and 450px for &lt;code&gt;srcset&lt;/code&gt;, plus a 1600px version for the lightbox. All images are content-addressed with immutable cache headers. The source JPEG is available as a full-res download, watermarked with the site URL using Hugo&amp;rsquo;s &lt;code&gt;images.Text&lt;/code&gt; filter.&lt;/p&gt;
&lt;p&gt;The gallery doesn&amp;rsquo;t render everything at once. An IntersectionObserver reveals photos in batches of 16 as you scroll, with a 400px look-ahead margin. Fallback shows everything immediately if IntersectionObserver isn&amp;rsquo;t supported.&lt;/p&gt;
&lt;h3 id="the-lightbox"&gt;The lightbox&lt;/h3&gt;
&lt;p&gt;This is the most complex client-side code on the site and it&amp;rsquo;s still under 300 lines.&lt;/p&gt;
&lt;p&gt;&lt;img alt="The lightbox— photo with AI-generated title and description below" loading="lazy" src="https://mrmatt.io/images/building-mrmatt-io/lightbox.png"&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Keyboard nav— arrows to browse, Escape to close&lt;/li&gt;
&lt;li&gt;Touch swipe— 50px+ horizontal swipe triggers navigation&lt;/li&gt;
&lt;li&gt;Focus trapping— Tab cycles through lightbox elements only&lt;/li&gt;
&lt;li&gt;ARIA— &lt;code&gt;role=&amp;quot;dialog&amp;quot;&lt;/code&gt;, &lt;code&gt;aria-modal=&amp;quot;true&amp;quot;&lt;/code&gt;, labeled buttons&lt;/li&gt;
&lt;li&gt;Deep linking— photo slug in the URL hash, shareable links&lt;/li&gt;
&lt;li&gt;Reduced motion— skips the 150ms fade if the OS prefers it&lt;/li&gt;
&lt;li&gt;Preloading— next image loads before display&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The gallery and lightbox both adapt to PaperMod&amp;rsquo;s dark mode toggle.&lt;/p&gt;
&lt;h3 id="lessons"&gt;Lessons&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Share targets work.&lt;/strong&gt; The Web Share Target API is what makes this whole pipeline viable. Without it I&amp;rsquo;d need a native app. With it, sharing a photo to the upload tool is identical to sharing to any other app. A few lines in the manifest and a service worker handler— that&amp;rsquo;s it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;EXIF parsing is tedious.&lt;/strong&gt; Binary format from the 90s. Byte offsets, endianness, IFD chains. Not hard, but the kind of code you write once and never want to touch again. A library would have been fine, but I wanted zero npm dependencies.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;AI feedback &amp;gt; one-shot.&lt;/strong&gt; The feedback loop is what turns AI description from a novelty into something I actually use. First pass gets the structure right. One round of &amp;ldquo;this is at Hammerman Beach, tone should be more contemplative&amp;rdquo; gets it the rest of the way.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Hugo&amp;rsquo;s image API has opinions.&lt;/strong&gt; &lt;code&gt;.Fill&lt;/code&gt; crops to exact dimensions. &lt;code&gt;.Fit&lt;/code&gt; scales to fit within dimensions. &lt;code&gt;.Resize&lt;/code&gt; scales to a width or height. For square grid thumbnails, &lt;code&gt;.Fill&lt;/code&gt;. For the lightbox display, &lt;code&gt;.Fit&lt;/code&gt;. Getting these mixed up produces stretched images or unexpected crops.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Next: &lt;a href="https://mrmatt.io/posts/building-mrmatt-io-performance/"&gt;Performance &amp;amp; Security&lt;/a&gt;— fonts, CSP, headers, accessibility, and caching.&lt;/em&gt;&lt;/p&gt;</content:encoded><media:content url="https://mrmatt.io/images/MattWalker-avatar.png" medium="image"/></item><item><title>Curating 100K Google Photos with Code and AI</title><link>https://mrmatt.io/posts/google-takeout-gallery-curation/</link><pubDate>Sun, 08 Mar 2026 00:00:00 -0500</pubDate><guid>https://mrmatt.io/posts/google-takeout-gallery-curation/</guid><description>How I used Python, OpenCV, and Claude Code to find 51 gallery-worthy photos in 723GB of Google Takeout data.</description><content:encoded>&lt;p&gt;I have 22 years of photos in Google Photos. Somewhere in that pile are the shots I&amp;rsquo;m actually proud of &amp;ndash; landscapes, travel scenes, nature, the odd well-composed hobby photo. The problem is finding them. Google&amp;rsquo;s search is decent for &amp;ldquo;photos of beaches&amp;rdquo; but useless for &amp;ldquo;my best photography that doesn&amp;rsquo;t have people in it.&amp;rdquo; So I built a pipeline to do it.&lt;/p&gt;
&lt;h3 id="the-raw-material"&gt;The raw material&lt;/h3&gt;
&lt;p&gt;Google Takeout delivered 15 zip files totaling 723GB. Roughly 100,000 photos spanning 2003 to 2025. Everything from DSLR RAW files to blurry phone screenshots to photos of receipts. The goal was to extract gallery-worthy landscape and nature photography &amp;ndash; no people as subjects, just the best images from two decades of shooting.&lt;/p&gt;
&lt;p&gt;I used &lt;a href="https://claude.ai/"&gt;Claude Code&lt;/a&gt; as the primary tool for the whole project. Not as a black box that magically picked winners, but as a collaborator writing Python scripts, iterating on scoring logic, and helping me think through the filtering stages. The actual image processing was all local &amp;ndash; Python, OpenCV, and Pillow running on a regular PC.&lt;/p&gt;
&lt;h3 id="the-pipeline"&gt;The pipeline&lt;/h3&gt;
&lt;p&gt;The pipeline ran in stages, each one narrowing the pool. Here&amp;rsquo;s how the numbers shook out:&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Stage&lt;/th&gt;
 &lt;th&gt;What it does&lt;/th&gt;
 &lt;th&gt;Photos in&lt;/th&gt;
 &lt;th&gt;Photos out&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;Pass 0:&lt;/strong&gt; Metadata scan&lt;/td&gt;
 &lt;td&gt;Scan JSON sidecars inside zips without extracting images. Score by file size, RAW format, GPS presence, people tags, album keywords&lt;/td&gt;
 &lt;td&gt;98,312&lt;/td&gt;
 &lt;td&gt;98,312 (scored)&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;Pass 1:&lt;/strong&gt; Extract and score&lt;/td&gt;
 &lt;td&gt;Extract from zips, generate 256px thumbnails, compute image quality scores&lt;/td&gt;
 &lt;td&gt;85,876&lt;/td&gt;
 &lt;td&gt;85,876 (scored)&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;Pass 2:&lt;/strong&gt; Dedup&lt;/td&gt;
 &lt;td&gt;Perceptual hash deduplication (hamming distance 8 or less)&lt;/td&gt;
 &lt;td&gt;67,392 (score 70+)&lt;/td&gt;
 &lt;td&gt;44,404 unique&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;Pass 2b:&lt;/strong&gt; Content filter&lt;/td&gt;
 &lt;td&gt;OpenCV face detection via Haar cascades, screenshot detection&lt;/td&gt;
 &lt;td&gt;44,404&lt;/td&gt;
 &lt;td&gt;32,074&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;Pass 2c:&lt;/strong&gt; Skin filter&lt;/td&gt;
 &lt;td&gt;HSV skin tone detection, outdoor/nature classification, score boost for nature scenes&lt;/td&gt;
 &lt;td&gt;32,074&lt;/td&gt;
 &lt;td&gt;12,812&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;Pass 3:&lt;/strong&gt; Contact sheets&lt;/td&gt;
 &lt;td&gt;Generate 171 contact sheets, manual visual review picking gallery candidates&lt;/td&gt;
 &lt;td&gt;12,812&lt;/td&gt;
 &lt;td&gt;~143 picks&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;Pass 4:&lt;/strong&gt; Final curation&lt;/td&gt;
 &lt;td&gt;Strict category-based review, cutting near-dupes and weaker shots&lt;/td&gt;
 &lt;td&gt;143&lt;/td&gt;
 &lt;td&gt;51 final&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;From 98,312 to 51. A 99.95% reduction.&lt;/p&gt;
&lt;h3 id="the-scoring"&gt;The scoring&lt;/h3&gt;
&lt;p&gt;Pass 1 computed a composite quality score for every image. The idea was simple: good photos tend to be sharp, colorful, well-exposed, and high resolution. Bad photos tend to be blurry, washed out, or tiny. The score combined several signals:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Sharpness&lt;/strong&gt; &amp;ndash; Laplacian variance of the grayscale image. The Laplacian operator highlights edges; images with strong focus have high variance. Blurry phone shots score low, sharp landscapes score high.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Colorfulness&lt;/strong&gt; &amp;ndash; The Hasler and Suesstrunk metric, which works in opponent color space (red-green and yellow-blue channels). Saturated sunsets and tropical water score well. Gray parking lots don&amp;rsquo;t.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Contrast&lt;/strong&gt; &amp;ndash; Standard deviation of luminance. Flat, hazy shots get penalized. Images with a full tonal range get rewarded.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Resolution bonus&lt;/strong&gt; &amp;ndash; Extra points for high megapixel counts. A 24MP DSLR shot started with an advantage over a 2MP flip phone capture. RAW format files got an additional boost on the assumption that I was being more intentional when I shot in RAW.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Metadata signals&lt;/strong&gt; &amp;ndash; GPS coordinates suggested travel photos worth a closer look. Album keywords like &amp;ldquo;vacation&amp;rdquo; or &amp;ldquo;hiking&amp;rdquo; added points. Large file sizes correlated with higher-quality originals.&lt;/p&gt;
&lt;p&gt;The threshold for advancing past Pass 1 was a composite score of 70 or above. That cut roughly 18,000 low-quality images before any heavier processing began.&lt;/p&gt;
&lt;h3 id="the-people-filter"&gt;The people filter&lt;/h3&gt;
&lt;p&gt;This was the most interesting technical challenge. I wanted landscape and nature photography, not portraits or group shots. The pipeline attacked this in layers.&lt;/p&gt;
&lt;p&gt;First, OpenCV&amp;rsquo;s Haar cascade face detector caught the obvious cases &amp;ndash; photos where faces were clearly visible. That was Pass 2b, and it removed about 12,000 images along with detected screenshots.&lt;/p&gt;
&lt;p&gt;But plenty of photos have people in them without showing clear faces. Beach scenes, hiking shots, crowd photos. Pass 2c used HSV color space skin tone detection to estimate how much of the image contained skin-colored pixels. Photos above the threshold got cut. Photos with very low skin content and high green/blue channel ratios got flagged as likely outdoor/nature scenes and received a score boost.&lt;/p&gt;
&lt;p&gt;The skin filter removed 19,262 images. It was surprisingly effective. Not perfect &amp;ndash; it occasionally flagged sandy beaches or terra cotta rooftops &amp;ndash; but the false positive rate was low enough that the contact sheet review caught the few nature photos that got wrongly cut.&lt;/p&gt;
&lt;h3 id="the-visual-review"&gt;The visual review&lt;/h3&gt;
&lt;p&gt;After all the automated filtering, 12,812 photos remained. Still too many to look at one by one. The pipeline generated 171 contact sheets &amp;ndash; grid images with 75 thumbnails each at 192px. Each thumbnail had its filename overlaid so I could trace picks back to the source.&lt;/p&gt;
&lt;p&gt;This was the manual stage. I scrolled through the contact sheets, marking promising photos. At 192px you can&amp;rsquo;t judge fine detail, but you can absolutely judge composition, color, and subject matter. The contact sheet format made it fast &amp;ndash; I could evaluate 75 photos in under a minute.&lt;/p&gt;
&lt;p&gt;That pass yielded about 143 candidates. Then one more round of strict review: organizing by category, cutting near-duplicates where I had three similar shots of the same mountain, and dropping anything that didn&amp;rsquo;t hold up at full resolution. That brought the final count to 51.&lt;/p&gt;
&lt;h3 id="what-worked"&gt;What worked&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Code did 99% of the heavy lifting.&lt;/strong&gt; The entire automated pipeline &amp;ndash; metadata scanning, quality scoring, deduplication, face detection, skin filtering, contact sheet generation &amp;ndash; ran locally with no API calls. Python, OpenCV, and Pillow. No cloud GPU, no expensive vision API for bulk filtering.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Perceptual hashing was reliable.&lt;/strong&gt; Google Photos is full of duplicates &amp;ndash; the same image downloaded at different resolutions, backed up from multiple devices, shared and re-saved. Perceptual hashing with a hamming distance threshold of 8 caught most of them without being so aggressive that it merged genuinely different photos.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Contact sheets were the right abstraction.&lt;/strong&gt; There&amp;rsquo;s a sweet spot between &amp;ldquo;look at every photo individually&amp;rdquo; (too slow) and &amp;ldquo;let the algorithm decide&amp;rdquo; (too opaque). Contact sheets at 192px hit that sweet spot. Fast enough for a human to scan thousands of photos in a sitting, detailed enough to make real judgments about composition.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The pipeline processed 100K photos in a few hours on a regular PC.&lt;/strong&gt; No special hardware. The bottleneck was extracting images from 723GB of zips, not the scoring or filtering. Once images were extracted and thumbnailed, everything else was fast.&lt;/p&gt;
&lt;h3 id="the-result"&gt;The result&lt;/h3&gt;
&lt;p&gt;51 photos spanning 2008 to 2025. Mountain vistas from out west. Lakes in Maine. Caribbean water. Flowers and garden shots. RC planes and coffee. Architecture. Countryside drives. Rivers and streams. A few odd subjects that were just visually interesting.&lt;/p&gt;
&lt;p&gt;Each photo gets published through the site&amp;rsquo;s existing &lt;a href="https://mrmatt.io/posts/building-mrmatt-io-photography/"&gt;photography pipeline&lt;/a&gt; &amp;ndash; AI-generated title, alt text, and description via Claude Haiku, with a round of human editing before it goes live.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s a strange feeling to compress two decades of photos down to 51 images. But that&amp;rsquo;s the point. The gallery isn&amp;rsquo;t an archive &amp;ndash; it&amp;rsquo;s a curated collection. And finding 51 genuine keepers in 100,000 candidates feels about right. The ratio tracks with what any photographer will tell you: most of what you shoot isn&amp;rsquo;t good, and that&amp;rsquo;s fine. The pipeline just made it practical to find the ones that are.&lt;/p&gt;
&lt;h3 id="see-the-gallery"&gt;See the gallery&lt;/h3&gt;
&lt;p&gt;Here&amp;rsquo;s a taste of what made the cut:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://mrmatt.io/photography/"&gt;&lt;img alt="A selection of photos from the gallery" loading="lazy" src="https://mrmatt.io/images/gallery-preview.jpg"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href="https://mrmatt.io/photography/"&gt;Browse the full photography gallery →&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;</content:encoded><media:content url="https://mrmatt.io/images/MattWalker-avatar.png" medium="image"/></item><item><title>Building MrMatt.io: The Migration</title><link>https://mrmatt.io/posts/building-mrmatt-io-migration/</link><pubDate>Sat, 07 Mar 2026 00:00:00 -0500</pubDate><guid>https://mrmatt.io/posts/building-mrmatt-io-migration/</guid><description>Replacing seven services and a deprecated npm ecosystem with Hugo, GitHub Actions, and Cloudflare Pages.</description><content:encoded>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Building MrMatt.io&lt;/strong&gt; — a series on rebuilding this site from scratch.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href="https://mrmatt.io/posts/building-mrmatt-io-specs/"&gt;Spec-Driven Development&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The Migration&lt;/strong&gt; (you are here)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://mrmatt.io/posts/building-mrmatt-io-photography/"&gt;The Photography Pipeline&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://mrmatt.io/posts/building-mrmatt-io-performance/"&gt;Performance &amp;amp; Security&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h3 id="the-old-stack"&gt;The old stack&lt;/h3&gt;
&lt;p&gt;When I last &lt;a href="https://mrmatt.io/posts/website-update-2016/"&gt;rebuilt this site in 2016&lt;/a&gt;, the stack made sense. Here&amp;rsquo;s what was running:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Hugo&lt;/strong&gt; — static site generator&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Gulp&lt;/strong&gt; — task runner for the build pipeline&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;SASS&lt;/strong&gt; — CSS preprocessing&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;npm&lt;/strong&gt; — dependency management&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Travis CI&lt;/strong&gt; — continuous integration&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;s3cmd&lt;/strong&gt; — deployment to S3&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Amazon S3&lt;/strong&gt; — file hosting&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;KeyCDN&lt;/strong&gt; — CDN in front of S3&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Let&amp;rsquo;s Encrypt&lt;/strong&gt; — TLS certificates via cron&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;DNS Made Easy&lt;/strong&gt; — DNS&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Staticman&lt;/strong&gt; — comment system processing GitHub PRs&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Eleven moving parts for a blog. Each one justified at the time— Gulp because Hugo didn&amp;rsquo;t have an asset pipeline yet, SASS because raw CSS was painful, KeyCDN because S3 alone was slow, Let&amp;rsquo;s Encrypt because HTTPS wasn&amp;rsquo;t free on CDNs, Staticman because I wanted comments without a database.&lt;/p&gt;
&lt;p&gt;By 2026, half the npm packages were deprecated and the other half had security advisories. Travis CI&amp;rsquo;s free tier was gutted. Staticman hadn&amp;rsquo;t processed a comment in years. I was paying for four services to host what amounts to some HTML and a few photos.&lt;/p&gt;
&lt;h3 id="the-new-stack"&gt;The new stack&lt;/h3&gt;
&lt;p&gt;Hugo stays. Everything else goes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Hugo + PaperMod&lt;/strong&gt; — Hugo&amp;rsquo;s built-in pipeline handles CSS minification and fingerprinting now. No Gulp, no SASS, no npm. PaperMod is actively maintained with dark mode, search, and solid accessibility defaults. One git submodule instead of a custom theme.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;GitHub Actions&lt;/strong&gt; — One workflow file replaces Travis CI and the deployment scripts. Checkout, build, deploy.&lt;/p&gt;
&lt;p&gt;&lt;img alt="GitHub Actions— a stream of green checks, the entire CI/CD pipeline in one workflow" loading="lazy" src="https://mrmatt.io/images/building-mrmatt-io/github-actions.png"&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Cloudflare Pages&lt;/strong&gt; — This is the big one. Free tier, global CDN, automatic TLS, HTTP/3, preview deployments on PRs. Replaces S3, KeyCDN, Let&amp;rsquo;s Encrypt, and DNS Made Easy. Four services collapsed into one.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Cloudflare Functions&lt;/strong&gt; — Three lightweight serverless endpoints for the &lt;a href="https://mrmatt.io/posts/building-mrmatt-io-photography/"&gt;photo upload tool&lt;/a&gt;: OAuth exchange, client ID lookup, AI photo descriptions. Runs on Cloudflare&amp;rsquo;s edge at no cost for my usage.&lt;/p&gt;
&lt;h3 id="content-migration"&gt;Content migration&lt;/h3&gt;
&lt;p&gt;48 posts from 2007 to 2017, all preserved.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Front matter&lt;/strong&gt; — Every post used TOML (&lt;code&gt;+++&lt;/code&gt; delimiters). PaperMod expects YAML (&lt;code&gt;---&lt;/code&gt;). Mechanical conversion, but each post needed review because some custom fields didn&amp;rsquo;t map cleanly.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Shortcodes&lt;/strong&gt; — The old theme had &lt;code&gt;gallery&lt;/code&gt;, &lt;code&gt;galleryimage&lt;/code&gt;, &lt;code&gt;galleryinit&lt;/code&gt;, &lt;code&gt;widecontent&lt;/code&gt;. None of those exist in PaperMod and I didn&amp;rsquo;t want to recreate them. Most got replaced with standard markdown. The gallery shortcodes were removed entirely— photography now has its &lt;a href="https://mrmatt.io/photography/"&gt;own section&lt;/a&gt; with a proper implementation.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Legacy embeds&lt;/strong&gt; — A few posts had raw HTML iframes for YouTube and Google Maps. Still work fine (Goldmark allows unsafe HTML), but they needed CSP &lt;code&gt;frame-src&lt;/code&gt; directives to permit those origins.&lt;/p&gt;
&lt;p&gt;The words are the same. The RC planes, the fishing trips, the &lt;a href="https://mrmatt.io/posts/canoe-camping-penobscot-river-maine/"&gt;camping photos&lt;/a&gt;, the tech reviews— all still here. Cleaner markup is all.&lt;/p&gt;
&lt;h3 id="what-i-left-out"&gt;What I left out&lt;/h3&gt;
&lt;p&gt;Every rebuild is a chance to add things. I was more interested in what I could remove.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;No npm.&lt;/strong&gt; Hugo handles everything natively now— minification, fingerprinting, image resizing, WebP. The only JavaScript in the build is a 50-line post-build script for CSP hash generation, and it has zero npm dependencies.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;No comments.&lt;/strong&gt; Staticman was clever— comments as GitHub PRs, stored in the repo as data files. In practice I got maybe ten real comments over four years, plus a steady stream of spam. If I bring comments back it&amp;rsquo;ll be something like Giscus backed by GitHub Discussions.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;No client-side analytics.&lt;/strong&gt; Cloudflare Web Analytics runs at the edge— no JS tag, no cookie banners. Page views and referrers without adding anything to the site.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;No build complexity.&lt;/strong&gt; The Hugo config is 96 lines. No webpack, no PostCSS, no Tailwind.&lt;/p&gt;
&lt;h3 id="three-services"&gt;Three services&lt;/h3&gt;
&lt;p&gt;The whole site runs on:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;GitHub&lt;/strong&gt; — source, CI/CD, PRs, releases&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cloudflare&lt;/strong&gt; — hosting, CDN, DNS, TLS, analytics, functions&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Anthropic&lt;/strong&gt; — AI photo descriptions via Claude Haiku&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Monthly hosting cost: $0. The only spend is Anthropic API usage for photo descriptions— fractions of a cent per photo.&lt;/p&gt;
&lt;p&gt;The old stack had S3 ($1-2/mo), KeyCDN ($5-10/mo), DNS Made Easy ($3/mo), plus Travis CI&amp;rsquo;s increasingly restricted free tier. Not a lot of money, but a lot of billing dashboards.&lt;/p&gt;
&lt;h3 id="what-i-learned"&gt;What I learned&lt;/h3&gt;
&lt;p&gt;The 2016 stack was best-of-breed assembly. S3 was the best static host. KeyCDN was a solid CDN. Travis CI was the standard for open-source CI. Each choice was defensible in isolation. But the integration surface— the Gulp tasks, the deployment script talking to three APIs, the cron job renewing certificates— that&amp;rsquo;s where the maintenance burden lived.&lt;/p&gt;
&lt;p&gt;The 2026 stack is platform convergence. Cloudflare Pages isn&amp;rsquo;t the best CDN, or the best host, or the best DNS. But it&amp;rsquo;s good enough at all three, and the integration cost is zero. Same with GitHub— not the best CI, but Actions plus source hosting plus PR workflows in one place eliminates a whole category of glue.&lt;/p&gt;
&lt;p&gt;&lt;img alt="The current site— Hugo + PaperMod, clean and minimal" loading="lazy" src="https://mrmatt.io/images/building-mrmatt-io/homepage.png"&gt;&lt;/p&gt;
&lt;p&gt;Faster, more secure, easier to maintain. I didn&amp;rsquo;t have to be clever— I just had to be willing to delete things.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Next: &lt;a href="https://mrmatt.io/posts/building-mrmatt-io-photography/"&gt;The Photography Pipeline&lt;/a&gt;— phone to published gallery, end to end.&lt;/em&gt;&lt;/p&gt;</content:encoded><media:content url="https://mrmatt.io/images/MattWalker-avatar.png" medium="image"/></item><item><title>Building MrMatt.io: Spec-Driven Development</title><link>https://mrmatt.io/posts/building-mrmatt-io-specs/</link><pubDate>Fri, 06 Mar 2026 00:00:00 -0500</pubDate><guid>https://mrmatt.io/posts/building-mrmatt-io-specs/</guid><description>How plan-as-spec methodology and two commands turned a stale personal site into 44 shipped features.</description><content:encoded>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Building MrMatt.io&lt;/strong&gt; — a series on rebuilding this site from scratch.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Spec-Driven Development&lt;/strong&gt; (you are here)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://mrmatt.io/posts/building-mrmatt-io-migration/"&gt;The Migration&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://mrmatt.io/posts/building-mrmatt-io-photography/"&gt;The Photography Pipeline&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://mrmatt.io/posts/building-mrmatt-io-performance/"&gt;Performance &amp;amp; Security&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h3 id="getting-started-again"&gt;Getting started again&lt;/h3&gt;
&lt;p&gt;Nine years without a blog post. It wasn&amp;rsquo;t for lack of things to say— life just got in the way and the site&amp;rsquo;s toolchain aged out from under me. Changing anything meant relearning Gulp, npm, Travis CI, and a bunch of deployment scripts I hadn&amp;rsquo;t touched since 2017.&lt;/p&gt;
&lt;p&gt;What changed was AI-assisted development. Not &amp;ldquo;AI built my website&amp;rdquo;— I&amp;rsquo;ll get to that— but the distance between having an idea and shipping it collapsed. The stuff that used to eat a weekend (configuring build tools, writing boilerplate, wiring up deployments) now happens at conversation speed. The decisions are still mine. The tedious parts just got fast enough that building was fun again.&lt;/p&gt;
&lt;p&gt;So I rebuilt the whole thing. And then I kept going.&lt;/p&gt;
&lt;h3 id="why-not-a-framework"&gt;Why not a framework&lt;/h3&gt;
&lt;p&gt;There are plenty of tools for structured feature development— GitHub&amp;rsquo;s SpecKit, Heavy, various RFC and ADR templates. They all follow the same pattern: write a document in a specific format, get it reviewed, track its status, implement against it.&lt;/p&gt;
&lt;p&gt;For a team, that makes sense. For a solo project on a personal site, it&amp;rsquo;s overhead. I don&amp;rsquo;t need an approval workflow for adding dark mode to my photo gallery. I need to think through what I&amp;rsquo;m building, capture the decisions, and start.&lt;/p&gt;
&lt;h3 id="plans-as-specs"&gt;Plans as specs&lt;/h3&gt;
&lt;p&gt;Claude Code has a plan mode. You describe what you want to build and it produces a structured plan— context, requirements, design decisions, files to modify, test plan. These are detailed enough to implement against and easy to review in a terminal.&lt;/p&gt;
&lt;p&gt;I leaned into it. Every feature gets a plan, the plan becomes the spec, the spec drives the work.&lt;/p&gt;
&lt;p&gt;&lt;img alt="A spec file showing the structured format— summary, requirements, design, files to modify, and test plan" loading="lazy" src="https://mrmatt.io/images/building-mrmatt-io/spec-example.png"&gt;&lt;/p&gt;
&lt;p&gt;The format is simple markdown:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;# Feature Name
Branch: feature/feature-name

## Summary
One paragraph on what and why.

## Requirements
- What the feature must do

## Design
Technical approach, trade-offs considered.

## Files to Modify
- path/to/file.html — what changes

## Test Plan
- [ ] Verification step one
- [ ] Verification step two
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;No YAML metadata, no status fields, no approval gates. Just enough structure to think clearly.&lt;/p&gt;
&lt;h3 id="two-commands"&gt;Two commands&lt;/h3&gt;
&lt;p&gt;The whole workflow is two commands: &lt;code&gt;/feature&lt;/code&gt; and &lt;code&gt;/ship&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;/feature&lt;/code&gt;&lt;/strong&gt; kicks things off. It checks that git is clean, has a conversation about what I want to build, then:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Scans &lt;code&gt;.specs/&lt;/code&gt; for the next number&lt;/li&gt;
&lt;li&gt;Writes the spec as a numbered markdown file&lt;/li&gt;
&lt;li&gt;Creates a git worktree on a fresh branch&lt;/li&gt;
&lt;li&gt;Implements the feature in the worktree&lt;/li&gt;
&lt;li&gt;Starts a Hugo dev server so I can review&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Everything happens in isolation— main stays clean. I review the running site, ask for changes, iterate.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;/ship&lt;/code&gt;&lt;/strong&gt; finishes it. Stages, commits, pushes, creates a PR with the spec&amp;rsquo;s test plan as checkboxes in the body. Enables auto-merge, waits for CI, cleans up the worktree, pulls the merged changes back to main.&lt;/p&gt;
&lt;p&gt;&lt;img alt="A merged pull request showing the spec&amp;rsquo;s summary and test plan as the PR body" loading="lazy" src="https://mrmatt.io/images/building-mrmatt-io/pr-test-plan.png"&gt;&lt;/p&gt;
&lt;p&gt;One command to start, conversation in the middle, one command to ship.&lt;/p&gt;
&lt;h3 id="44-features"&gt;44 features&lt;/h3&gt;
&lt;p&gt;&lt;img alt="44 numbered spec files in the .specs directory" loading="lazy" src="https://mrmatt.io/images/building-mrmatt-io/specs-directory.png"&gt;&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s what came out of this process:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Photography pipeline&lt;/strong&gt; — PWA upload tool with Android share target, EXIF date parsing, Canvas resizing, GitHub OAuth. AI-powered photo descriptions via Claude Haiku. Automated branch/PR creation with auto-merge. Responsive CSS grid gallery with a custom lightbox— keyboard nav, touch swipe, focus trapping, ARIA, deep linking, progressive loading. Hugo image pipeline generating WebP thumbnails at three sizes plus watermarked full-res downloads.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Security&lt;/strong&gt; — Content Security Policy that went from &lt;code&gt;'unsafe-inline'&lt;/code&gt; to manual hashes to auto-generated hashes via a post-build script. A+ Mozilla Observatory score. HSTS, X-Frame-Options, Permissions-Policy, security.txt.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Performance&lt;/strong&gt; — Self-hosted WOFF2 variable-weight fonts replacing Google Fonts. Responsive &lt;code&gt;srcset&lt;/code&gt; images in WebP. Content-addressed caching with immutable headers.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Accessibility&lt;/strong&gt; — Skip links, focus trapping, ARIA roles, &lt;code&gt;prefers-reduced-motion&lt;/code&gt; support, semantic HTML.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Infrastructure&lt;/strong&gt; — GitHub Actions replacing Travis CI. Cloudflare Pages replacing S3 + KeyCDN + Let&amp;rsquo;s Encrypt + DNS Made Easy. Cloudflare Functions for OAuth and AI. Automated releases.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Content&lt;/strong&gt; — 48 posts migrated from TOML to YAML front matter. Profile updates, homepage photo showcase, custom 404 page, RSS enhancements, related posts.&lt;/p&gt;
&lt;p&gt;Each one was a spec → implement → ship cycle. Most took a single session. Some took two— like the CSP auto-hash generation, where the first approach broke production. That story is in the &lt;a href="https://mrmatt.io/posts/building-mrmatt-io-performance/"&gt;Performance &amp;amp; Security&lt;/a&gt; post.&lt;/p&gt;
&lt;h3 id="the-real-artifact"&gt;The real artifact&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;.specs/&lt;/code&gt; directory is what makes this work. Forty-four documents that capture every decision— what I considered, what I chose, why. When the lightbox JavaScript changed and the manually maintained CSP hashes went stale, I could look at the original spec, understand the intent, and fix it properly instead of hacking around the symptom.&lt;/p&gt;
&lt;p&gt;This isn&amp;rsquo;t &amp;ldquo;AI generated my website.&amp;rdquo; I make every architectural call. I review every line. I decide what gets built and how. What changed is that the mechanical parts— file creation, boilerplate, rote refactoring— happen at conversation speed. The ratio of thinking to typing shifted, and that&amp;rsquo;s the ratio that matters.&lt;/p&gt;
&lt;h3 id="the-rest-of-this-series"&gt;The rest of this series&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://mrmatt.io/posts/building-mrmatt-io-migration/"&gt;&lt;strong&gt;The Migration&lt;/strong&gt;&lt;/a&gt; — the old stack, the new stack, what I cut&lt;/li&gt;
&lt;li&gt;&lt;a href="https://mrmatt.io/posts/building-mrmatt-io-photography/"&gt;&lt;strong&gt;The Photography Pipeline&lt;/strong&gt;&lt;/a&gt; — phone to published page, end to end&lt;/li&gt;
&lt;li&gt;&lt;a href="https://mrmatt.io/posts/building-mrmatt-io-performance/"&gt;&lt;strong&gt;Performance &amp;amp; Security&lt;/strong&gt;&lt;/a&gt; — fonts, CSP, headers, accessibility, caching&lt;/li&gt;
&lt;/ul&gt;</content:encoded><media:content url="https://mrmatt.io/images/MattWalker-avatar.png" medium="image"/></item><item><title>Cast Iron Breakfast</title><link>https://mrmatt.io/photography/2026-02-23-cast-iron-breakfast/</link><pubDate>Mon, 23 Feb 2026 00:00:00 -0500</pubDate><guid>https://mrmatt.io/photography/2026-02-23-cast-iron-breakfast/</guid><description>Nothing says "I take my breakfast seriously" like a proper cast iron hash with perfectly crispy edges and runny yolks. Those snowy trees visible through the windows suggest this winter morning fuel was well-timed.</description><content:encoded/><media:content url="https://mrmatt.io/photography/2026-02-23-cast-iron-breakfast/photo_hu_23fd0b9b66663ee1.webp" medium="image"/></item><item><title>Snow Clearing Duty</title><link>https://mrmatt.io/photography/2026-02-23-snow-clearing-duty/</link><pubDate>Mon, 23 Feb 2026 00:00:00 -0500</pubDate><guid>https://mrmatt.io/photography/2026-02-23-snow-clearing-duty/</guid><description>Nothing says "snow sports season" quite like the business end of a snow blower—at least when you're the one responsible for clearing the driveway. The satisfying geometry of fresh-cut snow tracks and that cheerful red engine are the silver lining to winter's demands in the Baltimore/DC area.</description><content:encoded/><media:content url="https://mrmatt.io/photography/2026-02-23-snow-clearing-duty/photo_hu_40a9d7c5f5a9e223.webp" medium="image"/></item><item><title>Snow Slope Lights</title><link>https://mrmatt.io/photography/2026-02-23-snow-slope-lights/</link><pubDate>Mon, 23 Feb 2026 00:00:00 -0500</pubDate><guid>https://mrmatt.io/photography/2026-02-23-snow-slope-lights/</guid><description>Nothing says winter sports quite like an urban snow park lit up like a neon dream. The parallel lanes and dramatic blue lighting give serious winter vibes—this is what peak snow season looks like when you're chasing that adrenaline rush without having to trek far from civilization.</description><content:encoded/><media:content url="https://mrmatt.io/photography/2026-02-23-snow-slope-lights/photo_hu_abbd806d5054da46.webp" medium="image"/></item><item><title>Valley Sunset Panorama</title><link>https://mrmatt.io/photography/2026-02-23-valley-sunset-panorama/</link><pubDate>Mon, 23 Feb 2026 00:00:00 -0500</pubDate><guid>https://mrmatt.io/photography/2026-02-23-valley-sunset-panorama/</guid><description>That golden hour magic where the sky puts on a show and you're just grateful you hiked up to catch it. The Appalachian valleys really do have their own particular beauty—all those rolling ridges and agricultural patches stretching out to the hazy horizon, reminding you why this region is worth exploring on foot.</description><content:encoded/><media:content url="https://mrmatt.io/photography/2026-02-23-valley-sunset-panorama/photo_hu_95fa6ea71fe91917.webp" medium="image"/></item><item><title>About Me</title><link>https://mrmatt.io/about/</link><pubDate>Thu, 19 Feb 2026 00:00:00 -0500</pubDate><guid>https://mrmatt.io/about/</guid><description>Matt Walker — software engineer, photographer, and serial hobbyist in the Baltimore/DC area. Building data platforms, AI infrastructure, and too many side projects.</description><content:encoded>&lt;p&gt;I&amp;rsquo;m Matt Walker — an engineer, builder, and serial hobbyist based in the Baltimore/DC Metro area. I spend my professional life working at the intersection of data and AI, and the rest of it picking up hobbies faster than I can put them down.&lt;/p&gt;
&lt;h3 id="work"&gt;Work&lt;/h3&gt;
&lt;p&gt;I help organizations build enterprise data platforms and AI-capable infrastructure — semantic data models, agentic automation, and the tooling that ties it all together. The goal is always the same: help businesses get the most value out of their technology, from analytics and machine learning to AI agents and beyond.&lt;/p&gt;
&lt;h3 id="hobbies"&gt;Hobbies&lt;/h3&gt;
&lt;p&gt;I&amp;rsquo;ve never been able to stick to just one thing. Over the years I&amp;rsquo;ve built and flown RC planes, brewed all-grain beer, restored tractors, modified cars for the drag strip, built a retro arcade cabinet, turned wooden bowls on a lathe, tumbled rocks, and dialed in a lawn care program that the neighbors quietly resent. These days it&amp;rsquo;s a lot of camping, traveling, and dragging the family to national parks. I row on a Concept2 for health and sanity, and I&amp;rsquo;ve been on the water enough to justify keeping a boat around.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Fishing Boat" loading="lazy" src="https://mrmatt.io/img/boat.jpg"&gt;&lt;/p&gt;
&lt;p&gt;Coffee found me later in life and I went all in. I roast single-origin beans at home, obsess over roast profiles, and brew every way I can — espresso on a Gagiuino-modded Gaggia Classic, pourovers on a V60, batch brews on a Moccamaster, and the trusty AeroPress for travel. Full coffee nerd, no apologies.&lt;/p&gt;
&lt;h2 id="contact"&gt;Contact&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/MrMatt57"&gt;GitHub&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.linkedin.com/in/mrmatt"&gt;LinkedIn&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded><media:content url="https://mrmatt.io/images/MattWalker-avatar.png" medium="image"/></item><item><title>Gear</title><link>https://mrmatt.io/gear/</link><pubDate>Thu, 19 Feb 2026 00:00:00 -0500</pubDate><guid>https://mrmatt.io/gear/</guid><description>The tools, gadgets, and equipment Matt Walker uses for work, photography, coffee, and everyday life.</description><content:encoded>&lt;p&gt;This page is due for a refresh. Check back soon.&lt;/p&gt;</content:encoded><media:content url="https://mrmatt.io/images/MattWalker-avatar.png" medium="image"/></item><item><title>Now</title><link>https://mrmatt.io/now/</link><pubDate>Thu, 19 Feb 2026 00:00:00 -0500</pubDate><guid>https://mrmatt.io/now/</guid><description>What Matt Walker is focused on right now — current work, side projects, and interests. Inspired by nownownow.com.</description><content:encoded>&lt;p&gt;This page is a snapshot of what I&amp;rsquo;m focused on right now. Inspired by &lt;a href="https://nownownow.com"&gt;nownownow.com&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="work"&gt;Work&lt;/h2&gt;
&lt;p&gt;Building things with cloud infrastructure and AI.&lt;/p&gt;
&lt;h2 id="side-projects"&gt;Side Projects&lt;/h2&gt;
&lt;p&gt;Rebuilding this site from the ground up with Hugo and Cloudflare Pages.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/MrMatt57/pitclaw"&gt;PitClaw&lt;/a&gt; — a spec-driven, agentic engineering project where the software, hardware design, and 3D-printed enclosure are all AI-generated from specs — no code written by hand. ESP32-based smoker controller with a touchscreen web UI, predictive temperature modeling, and a passion for barbecue holding it all together.&lt;/p&gt;
&lt;h2 id="interests"&gt;Interests&lt;/h2&gt;
&lt;p&gt;Rowing, coffee, cooking, and the occasional homelab tinkering.&lt;/p&gt;</content:encoded><media:content url="https://mrmatt.io/images/MattWalker-avatar.png" medium="image"/></item><item><title>Stack</title><link>https://mrmatt.io/stack/</link><pubDate>Thu, 19 Feb 2026 00:00:00 -0500</pubDate><guid>https://mrmatt.io/stack/</guid><description>The technology stack behind mrmatt.io — Hugo, PaperMod, Cloudflare Pages, GitHub Actions, and AI-powered photo publishing.</description><content:encoded>&lt;p&gt;&lt;em&gt;Source available on GitHub: &lt;a href="https://github.com/MrMatt57/MrMatt.io"&gt;MrMatt57/MrMatt.io&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;h3 id="design-philosophy"&gt;Design Philosophy&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Minimal and fast — no JS frameworks, no bloat, static HTML/CSS&lt;/li&gt;
&lt;li&gt;Content-first — the site publishes writing and serves as a professional presence&lt;/li&gt;
&lt;li&gt;Zero dependencies — vanilla JS, no npm, no external libraries anywhere&lt;/li&gt;
&lt;li&gt;Own your stack — static files you control, no platform lock-in&lt;/li&gt;
&lt;li&gt;Low maintenance — no comment systems, no dynamic backends&lt;/li&gt;
&lt;li&gt;AI as a collaborator — Claude generates, the human reviews and refines&lt;/li&gt;
&lt;li&gt;Spec-driven development — plan before you build, every feature documented&lt;/li&gt;
&lt;li&gt;Automation where it matters — AI and APIs handle the tedious parts so publishing stays frictionless&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="static-site"&gt;Static Site&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://gohugo.io/"&gt;Hugo&lt;/a&gt; (extended) with &lt;a href="https://github.com/adityatelange/hugo-PaperMod"&gt;PaperMod&lt;/a&gt; theme (git submodule)&lt;/li&gt;
&lt;li&gt;Custom CSS with &lt;a href="https://fonts.google.com/specimen/Roboto+Slab"&gt;Roboto Slab&lt;/a&gt; typography&lt;/li&gt;
&lt;li&gt;No npm, no build tools — Hugo&amp;rsquo;s built-in asset pipeline handles everything&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="development"&gt;Development&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Spec-driven workflow — every feature starts with a numbered spec in &lt;code&gt;.specs/&lt;/code&gt; before any code is written&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.anthropic.com/en/docs/claude-code"&gt;Claude Code&lt;/a&gt; for interactive development — &lt;code&gt;/feature&lt;/code&gt; creates a spec + git worktree, &lt;code&gt;/ship&lt;/code&gt; commits, pushes, opens a PR with auto-merge, and cleans up&lt;/li&gt;
&lt;li&gt;Git worktree isolation keeps the main checkout clean while features are in progress&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="serverless-backend"&gt;Serverless Backend&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://developers.cloudflare.com/pages/functions/"&gt;Cloudflare Pages Functions&lt;/a&gt; (three endpoints)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/api/describe-photo&lt;/code&gt; — Claude Vision proxy with CORS locked to mrmatt.io&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/api/oauth-exchange&lt;/code&gt; — GitHub OAuth token exchange&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/api/oauth-client-id&lt;/code&gt; — serves the OAuth client ID to the frontend&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="cicd"&gt;CI/CD&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/features/actions"&gt;GitHub Actions&lt;/a&gt; — builds Hugo with submodules, compiles Cloudflare Functions, and deploys on every push to main&lt;/li&gt;
&lt;li&gt;Pull requests run build checks before merge, including Pages Functions validation&lt;/li&gt;
&lt;li&gt;Concurrency controls cancel in-progress deploys when a new push arrives&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="hosting--dns"&gt;Hosting &amp;amp; DNS&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://pages.cloudflare.com/"&gt;Cloudflare Pages&lt;/a&gt; (free tier)&lt;/li&gt;
&lt;li&gt;Cloudflare DNS&lt;/li&gt;
&lt;li&gt;Cloudflare Web Analytics — server-side, no JavaScript tag&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="security"&gt;Security&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP"&gt;Content Security Policy&lt;/a&gt; with deny-by-default baseline (&lt;code&gt;default-src 'none'&lt;/code&gt;) and SHA-256 script hashes — no &lt;code&gt;'unsafe-inline'&lt;/code&gt; in script-src&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security"&gt;HSTS&lt;/a&gt; with preload, X-Frame-Options, X-Content-Type-Options, Referrer-Policy, and Permissions-Policy&lt;/li&gt;
&lt;li&gt;CORS locked to &lt;code&gt;mrmatt.io&lt;/code&gt; on all API endpoints — API keys stay server-side in Cloudflare encrypted environment variables&lt;/li&gt;
&lt;li&gt;OAuth CSRF protection via cryptographic state parameter, single-user whitelist authorization&lt;/li&gt;
&lt;li&gt;&lt;a href="https://codeql.github.com/"&gt;CodeQL&lt;/a&gt; static analysis on every PR via GitHub Actions&lt;/li&gt;
&lt;li&gt;&lt;a href="https://mrmatt.io/.well-known/security.txt"&gt;security.txt&lt;/a&gt; (&lt;a href="https://www.rfc-editor.org/rfc/rfc9116"&gt;RFC 9116&lt;/a&gt;) for vulnerability disclosure&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="automated-publishing"&gt;Automated Publishing&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;GitHub OAuth authentication scoped to the repo owner&lt;/li&gt;
&lt;li&gt;Upload creates a feature branch (&lt;code&gt;photo/YYYY-MM-DD-slug&lt;/code&gt;), commits the image and Hugo content file, opens a PR, and enables auto-merge — all from the phone&lt;/li&gt;
&lt;li&gt;GitHub REST API for branches, blobs, trees, and commits; GraphQL API for the auto-merge mutation&lt;/li&gt;
&lt;li&gt;Photo goes from camera roll to live on the site with no terminal, no laptop&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="photo-upload"&gt;Photo Upload&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Vanilla JS progressive web app at &lt;a href="https://mrmatt.io/upload"&gt;&lt;code&gt;/upload&lt;/code&gt;&lt;/a&gt; — no frameworks, no dependencies&lt;/li&gt;
&lt;li&gt;Android share target — share a photo directly from the camera roll to the site&lt;/li&gt;
&lt;li&gt;Service Worker with network-first caching and cached share-target handoff between the browser and upload page&lt;/li&gt;
&lt;li&gt;Native EXIF parsing from raw JPEG bytes to extract photo dates — no libraries&lt;/li&gt;
&lt;li&gt;Client-side image conversion and resizing via Canvas API before sending to AI&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="ai-powered-descriptions"&gt;AI-Powered Descriptions&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.anthropic.com/"&gt;Anthropic Claude&lt;/a&gt; Haiku 4.5 vision model generates photo titles, alt text, and descriptions from uploaded images&lt;/li&gt;
&lt;li&gt;Cloudflare Pages Function proxies requests to the Anthropic API, keeping the API key server-side&lt;/li&gt;
&lt;li&gt;Feedback loop — review the AI output, provide guidance, and regenerate until it&amp;rsquo;s right&lt;/li&gt;
&lt;li&gt;Structured JSON output parsed directly into Hugo front matter fields&lt;/li&gt;
&lt;/ul&gt;</content:encoded><media:content url="https://mrmatt.io/images/MattWalker-avatar.png" medium="image"/></item><item><title>Version 2026 (Hugo &gt; Hugo, but better)</title><link>https://mrmatt.io/posts/website-update-2026/</link><pubDate>Thu, 19 Feb 2026 00:00:00 -0500</pubDate><guid>https://mrmatt.io/posts/website-update-2026/</guid><description>Nine years later, mrmatt.io gets a proper rebuild. Same Hugo engine, but Cloudflare Pages, GitHub Actions, PaperMod theme, and zero npm dependencies.</description><content:encoded>&lt;p&gt;It has been nine years since my last &lt;a href="https://mrmatt.io/posts/website-update-2016/"&gt;website update&lt;/a&gt;. Nine. Years. In that time my kids have grown up, I&amp;rsquo;ve picked up new hobbies, and somehow never got around to writing about any of it. The site has been sitting here since early 2017, faithfully serving its blog posts and fishing photos to the handful of people who stumble across it.&lt;/p&gt;
&lt;p&gt;The irony is not lost on me that this is now my third post about rebuilding the site.&lt;/p&gt;
&lt;h3 id="the-pattern"&gt;The pattern&lt;/h3&gt;
&lt;p&gt;If you&amp;rsquo;ve been here before, you know the drill:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;2014&lt;/strong&gt; — &lt;a href="https://mrmatt.io/posts/website-update-2014/"&gt;WordPress to Ghost&lt;/a&gt;. Chasing the full-stack JavaScript dream.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;2016&lt;/strong&gt; — &lt;a href="https://mrmatt.io/posts/website-update-2016/"&gt;Ghost to Hugo&lt;/a&gt;. Static sites, Gulp, Travis CI, S3, KeyCDN. A proper devops toolchain.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;2026&lt;/strong&gt; — Hugo to&amp;hellip; Hugo. But everything around it is different.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Hugo was the right call in 2016 and it&amp;rsquo;s still the right call now. It&amp;rsquo;s fast, it&amp;rsquo;s simple, and it gets out of the way. What needed to go was everything I bolted onto it — the Gulp pipeline, the SASS compilation, the Travis CI config, the S3 deployment scripts, the KeyCDN cache purging, the Staticman comment processing. All of it served its purpose, but none of it aged well. Half the npm packages were deprecated and the other half had security advisories.&lt;/p&gt;
&lt;h3 id="whats-new"&gt;What&amp;rsquo;s new&lt;/h3&gt;
&lt;p&gt;The rebuild strips it all back:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Hugo + PaperMod&lt;/strong&gt; — A clean, minimal, actively maintained theme with dark mode, search, and good accessibility out of the box. No custom theme to maintain.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;GitHub Actions&lt;/strong&gt; — One workflow file replaces Travis CI, Gulp tasks, and the deployment script.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cloudflare Pages&lt;/strong&gt; — Replaces S3 + KeyCDN + Let&amp;rsquo;s Encrypt + DNS Made Easy. Free tier, global CDN, automatic TLS, HTTP/3. One service instead of four.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;No build dependencies&lt;/strong&gt; — No npm, no node_modules, no Gulp. Hugo handles asset processing natively now. The repo is just content and config.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;No comments&lt;/strong&gt; — Staticman was clever but I never got meaningful engagement from it. If I bring comments back it&amp;rsquo;ll be something like Giscus backed by GitHub Discussions.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="what-stayed"&gt;What stayed&lt;/h3&gt;
&lt;p&gt;The content. All the posts from 2007 through 2017 are still here. The photos, the RC plane builds, the fishing trips, the tech reviews — it&amp;rsquo;s all migrated over. Some of the old shortcodes needed cleanup but the words are the same.&lt;/p&gt;
&lt;h3 id="whats-actually-different-this-time"&gt;What&amp;rsquo;s actually different this time&lt;/h3&gt;
&lt;p&gt;This rebuild was pair-programmed with &lt;a href="https://claude.ai/"&gt;Claude Code&lt;/a&gt;. The entire migration — deleting legacy files, scaffolding the new Hugo structure, converting front matter, fixing broken shortcodes, writing the CI/CD pipeline — was done conversationally in the terminal. I wrote the spec, Claude executed it phase by phase, and we iterated on the details together. It&amp;rsquo;s a genuinely different way to work on a project like this.&lt;/p&gt;
&lt;p&gt;Going forward, every change to the site is spec-driven. I run &lt;code&gt;/feature&lt;/code&gt;, which creates a numbered spec in &lt;code&gt;.specs/&lt;/code&gt;, spins up a git worktree, and implements the change. When it&amp;rsquo;s ready, &lt;code&gt;/ship&lt;/code&gt; commits, pushes, opens a PR, waits for CI, merges, and cleans up the worktree. Each spec is a self-contained record of what changed and why — the whole history lives in the repo.&lt;/p&gt;
&lt;h3 id="whats-next"&gt;What&amp;rsquo;s next&lt;/h3&gt;
&lt;p&gt;The site exists to publish writing again, and I have things to write about. Azure AI and the work I&amp;rsquo;m doing there. The homelab I&amp;rsquo;ve been building with Proxmox and Tailscale. Coffee roasting. Rowing. Maybe some cooking. The stack page will stay current this time — it&amp;rsquo;s a lot easier to maintain when the stack is three things instead of fifteen.&lt;/p&gt;
&lt;h3 id="homage"&gt;Homage&lt;/h3&gt;
&lt;p&gt;Here&amp;rsquo;s the progression, for tradition&amp;rsquo;s sake:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;WordPress era (2007–2014)&lt;/strong&gt;
&lt;img alt="MrMatt57.org WordPress Screenshot" loading="lazy" src="https://mrmatt.io/images/MrMatt57org-Wordpress.jpg"&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Ghost era (2014–2016)&lt;/strong&gt;
&lt;img alt="MrMatt57.org Ghost Screenshot" loading="lazy" src="https://mrmatt.io/images/MrMatt57org-Ghost.jpg"&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Hugo v1 era (2016–2026)&lt;/strong&gt;
&lt;img alt="mrmatt.io Hugo v1 Screenshot" loading="lazy" src="https://mrmatt.io/images/mrmatt-io-hugo-v1.png"&gt;&lt;/p&gt;</content:encoded><media:content url="https://mrmatt.io/images/mrmatt-io-hugo-v1.png" medium="image"/></item><item><title>Weverton Cliffs Overlook</title><link>https://mrmatt.io/photography/2025-12-24-weverton-cliffs-overlook/</link><pubDate>Wed, 24 Dec 2025 00:00:00 -0500</pubDate><guid>https://mrmatt.io/photography/2025-12-24-weverton-cliffs-overlook/</guid><description>Weverton Cliffs on the Appalachian Trail — the Potomac River below and Maryland mountains stretching into the distance on Christmas Eve.</description><content:encoded/><media:content url="https://mrmatt.io/photography/2025-12-24-weverton-cliffs-overlook/photo_hu_67ed8cf1c96f5d61.webp" medium="image"/></item><item><title>E-Bike Forest Trail</title><link>https://mrmatt.io/photography/2025-08-31-ebike-forest-trail/</link><pubDate>Sun, 31 Aug 2025 00:00:00 -0400</pubDate><guid>https://mrmatt.io/photography/2025-08-31-ebike-forest-trail/</guid><description>Singletrack through the woods — fat tires and leaf litter on a summer trail ride.</description><content:encoded/><media:content url="https://mrmatt.io/photography/2025-08-31-ebike-forest-trail/photo_hu_25f23204c5040c9.webp" medium="image"/></item><item><title>Shenandoah Valley View</title><link>https://mrmatt.io/photography/2025-08-30-shenandoah-valley-view/</link><pubDate>Sat, 30 Aug 2025 00:00:00 -0400</pubDate><guid>https://mrmatt.io/photography/2025-08-30-shenandoah-valley-view/</guid><description>Late summer in the Shenandoah, where every overlook feels like it was specifically designed to make you stop and stare. That pop of red foliage in the foreground is getting a head start on fall.</description><content:encoded/><media:content url="https://mrmatt.io/photography/2025-08-30-shenandoah-valley-view/photo_hu_d18028c90be2ce02.webp" medium="image"/></item><item><title>Maryland Steamed Crabs</title><link>https://mrmatt.io/photography/2025-08-23-maryland-steamed-crabs/</link><pubDate>Sat, 23 Aug 2025 00:00:00 -0400</pubDate><guid>https://mrmatt.io/photography/2025-08-23-maryland-steamed-crabs/</guid><description>Maryland blue crabs piled high and covered in J.O. — a summer tradition.</description><content:encoded/><media:content url="https://mrmatt.io/photography/2025-08-23-maryland-steamed-crabs/photo_hu_ee6bc7d9ae4de3ea.webp" medium="image"/></item><item><title>Superhot Peppers Ripening</title><link>https://mrmatt.io/photography/2025-08-15-photo-mm30excl/</link><pubDate>Fri, 15 Aug 2025 00:00:00 -0400</pubDate><guid>https://mrmatt.io/photography/2025-08-15-photo-mm30excl/</guid><description>Superhot peppers turning red on the vine in the summer garden — wrinkled skins and vivid color signaling they're almost ready to pick.</description><content:encoded/><media:content url="https://mrmatt.io/photography/2025-08-15-photo-mm30excl/photo_hu_f9ee3aa8f59e956f.webp" medium="image"/></item><item><title>Espresso Station</title><link>https://mrmatt.io/photography/2025-08-10-espresso-station/</link><pubDate>Sun, 10 Aug 2025 00:00:00 -0400</pubDate><guid>https://mrmatt.io/photography/2025-08-10-espresso-station/</guid><description>The home espresso setup — Gaggiuino Gaggia Classic pulling a profiled shot alongside the grinder and a fresh iced coffee.</description><content:encoded/><media:content url="https://mrmatt.io/photography/2025-08-10-espresso-station/photo_hu_a372d708ba1b216.webp" medium="image"/></item><item><title>Superhot Pepper Harvest</title><link>https://mrmatt.io/photography/2025-08-03-superhot-pepper-harvest/</link><pubDate>Sun, 03 Aug 2025 00:00:00 -0400</pubDate><guid>https://mrmatt.io/photography/2025-08-03-superhot-pepper-harvest/</guid><description>Homegrown superhot peppers fresh from the garden — sliced open to reveal the heat inside.</description><content:encoded/><media:content url="https://mrmatt.io/photography/2025-08-03-superhot-pepper-harvest/photo_hu_518841cedad3d341.webp" medium="image"/></item><item><title>Harpers Ferry Confluence</title><link>https://mrmatt.io/photography/2025-07-11-harpers-ferry-confluence/</link><pubDate>Fri, 11 Jul 2025 00:00:00 -0400</pubDate><guid>https://mrmatt.io/photography/2025-07-11-harpers-ferry-confluence/</guid><description>Where the Potomac carves its way through the mountains at Harpers Ferry. Two ridgelines, one river, and the kind of scene that made Thomas Jefferson call it "worth a voyage across the Atlantic."</description><content:encoded/><media:content url="https://mrmatt.io/photography/2025-07-11-harpers-ferry-confluence/photo_hu_806339ca0aecb580.webp" medium="image"/></item><item><title>Striped Lawn</title><link>https://mrmatt.io/photography/2025-06-19-striped-lawn/</link><pubDate>Thu, 19 Jun 2025 00:00:00 -0400</pubDate><guid>https://mrmatt.io/photography/2025-06-19-striped-lawn/</guid><description>A perfectly manicured lawn displays the striking striped pattern created by directional mowing. The alternating light and dark green rows stretch toward the roadside, demonstrating the satisfaction of meticulous yard maintenance.</description><content:encoded/><media:content url="https://mrmatt.io/photography/2025-06-19-striped-lawn/photo_hu_75427ee907a0d216.webp" medium="image"/></item><item><title>Mulch Mushroom Bloom</title><link>https://mrmatt.io/photography/2025-06-18-photo-mm30fzuo/</link><pubDate>Wed, 18 Jun 2025 00:00:00 -0400</pubDate><guid>https://mrmatt.io/photography/2025-06-18-photo-mm30fzuo/</guid><description>A carpet of delicate mushrooms emerges from the mulch after summer rain, their thin stems and small caps dotting the ground beneath the trees.</description><content:encoded/><media:content url="https://mrmatt.io/photography/2025-06-18-photo-mm30fzuo/photo_hu_f99febacf5e7e9fb.webp" medium="image"/></item><item><title>Pellet Smoker Night</title><link>https://mrmatt.io/photography/2025-05-16-pellet-smoker-night/</link><pubDate>Fri, 16 May 2025 00:00:00 -0400</pubDate><guid>https://mrmatt.io/photography/2025-05-16-pellet-smoker-night/</guid><description>Late night smoke session on the GMG — the red glow of the controller and wisps of smoke against the dark backyard.</description><content:encoded/><media:content url="https://mrmatt.io/photography/2025-05-16-pellet-smoker-night/photo_hu_7dc1b378c786b433.webp" medium="image"/></item><item><title>Cypress Swamp Forest</title><link>https://mrmatt.io/photography/2025-04-21-cypress-swamp-forest/</link><pubDate>Mon, 21 Apr 2025 00:00:00 -0400</pubDate><guid>https://mrmatt.io/photography/2025-04-21-cypress-swamp-forest/</guid><description>Towering cypress trees emerge from calm, mirror-like water in this peaceful swamp landscape. The interplay of reflected sky and verdant growth creates a tranquil natural sanctuary, where the forest and water exist in quiet harmony.</description><content:encoded/><media:content url="https://mrmatt.io/photography/2025-04-21-cypress-swamp-forest/photo_hu_6d7f685a59d0ed43.webp" medium="image"/></item><item><title>Boardwalk Into Brush</title><link>https://mrmatt.io/photography/2025-04-18-boardwalk-into-brush/</link><pubDate>Fri, 18 Apr 2025 00:00:00 -0400</pubDate><guid>https://mrmatt.io/photography/2025-04-18-boardwalk-into-brush/</guid><description>The boardwalk equivalent of "trust the process" -- you can't see where it ends, but the journey looks pretty good. Those railing shadows are putting on a free geometric art show.</description><content:encoded/><media:content url="https://mrmatt.io/photography/2025-04-18-boardwalk-into-brush/photo_hu_55c82d3cc081ab28.webp" medium="image"/></item><item><title>Chesapeake Bay Sunset</title><link>https://mrmatt.io/photography/2025-04-18-chesapeake-bay-sunset/</link><pubDate>Fri, 18 Apr 2025 00:00:00 -0400</pubDate><guid>https://mrmatt.io/photography/2025-04-18-chesapeake-bay-sunset/</guid><description>Sun setting over the Chesapeake Bay — the bridge stretching across the horizon as the sky turns deep orange and red.</description><content:encoded/><media:content url="https://mrmatt.io/photography/2025-04-18-chesapeake-bay-sunset/photo_hu_22f40f7194efa9f1.webp" medium="image"/></item><item><title>E-Bike Beach Sunset</title><link>https://mrmatt.io/photography/2025-04-18-ebike-beach-sunset/</link><pubDate>Fri, 18 Apr 2025 00:00:00 -0400</pubDate><guid>https://mrmatt.io/photography/2025-04-18-ebike-beach-sunset/</guid><description>Evening ride to the beach — fat tire e-bike resting on the boardwalk as the sun dips below the horizon.</description><content:encoded/><media:content url="https://mrmatt.io/photography/2025-04-18-ebike-beach-sunset/photo_hu_6f0a25bdae133c4f.webp" medium="image"/></item><item><title>Spanish Moss Grove</title><link>https://mrmatt.io/photography/2025-04-18-spanish-moss-grove/</link><pubDate>Fri, 18 Apr 2025 00:00:00 -0400</pubDate><guid>https://mrmatt.io/photography/2025-04-18-spanish-moss-grove/</guid><description>Ancient oaks laden with Spanish moss create a serene and atmospheric corridor through this shaded forest. The path invites exploration deeper into the quiet woods, where dappled sunlight filters through the hanging strands overhead.</description><content:encoded/><media:content url="https://mrmatt.io/photography/2025-04-18-spanish-moss-grove/photo_hu_9a3e1e3a36c973ff.webp" medium="image"/></item><item><title>Hoover Dam</title><link>https://mrmatt.io/photography/2025-04-02-hoover-dam/</link><pubDate>Wed, 02 Apr 2025 00:00:00 -0400</pubDate><guid>https://mrmatt.io/photography/2025-04-02-hoover-dam/</guid><description>This view captures the impressive scale of the Hoover Dam, showing its distinctive curved concrete face against the dramatic rocky landscape of the Black Canyon. The combination of human engineering and desert geology creates a striking contrast between the smooth dam surface and the jagged natural rock formations surrounding it.</description><content:encoded/><media:content url="https://mrmatt.io/photography/2025-04-02-hoover-dam/photo_hu_bede8cc93df32f1c.webp" medium="image"/></item><item><title>Hoover Dam Power Plant</title><link>https://mrmatt.io/photography/2025-04-02-hoover-dam-power-plant/</link><pubDate>Wed, 02 Apr 2025 00:00:00 -0400</pubDate><guid>https://mrmatt.io/photography/2025-04-02-hoover-dam-power-plant/</guid><description>The heart of Hoover Dam's power generation facility stretches into the distance, with massive turbine generators aligned in precision down the center of this cavernous underground chamber. An American flag hangs prominently overhead, marking the scale and significance of this engineering landmark that has powered the Southwest since 1936.</description><content:encoded/><media:content url="https://mrmatt.io/photography/2025-04-02-hoover-dam-power-plant/photo_hu_ac19f68a1e874fb1.webp" medium="image"/></item><item><title>Red Rock Canyon Desert</title><link>https://mrmatt.io/photography/2025-04-02-red-rock-canyon-desert/</link><pubDate>Wed, 02 Apr 2025 00:00:00 -0400</pubDate><guid>https://mrmatt.io/photography/2025-04-02-red-rock-canyon-desert/</guid><description>Desert flora at Red Rock Canyon outside Las Vegas — cholla, yucca, and sagebrush against the red sandstone cliffs.</description><content:encoded/><media:content url="https://mrmatt.io/photography/2025-04-02-red-rock-canyon-desert/photo_hu_4a7b0348d0280b0e.webp" medium="image"/></item><item><title>Sphere Sunset Concert</title><link>https://mrmatt.io/photography/2025-03-30-sphere-sunset-concert/</link><pubDate>Sun, 30 Mar 2025 00:00:00 -0400</pubDate><guid>https://mrmatt.io/photography/2025-03-30-sphere-sunset-concert/</guid><description>The Sphere in Las Vegas — an immersive concert experience with a wraparound LED screen painting a sunset over mountains.</description><content:encoded/><media:content url="https://mrmatt.io/photography/2025-03-30-sphere-sunset-concert/photo_hu_d87b9e3f6c187928.webp" medium="image"/></item><item><title>Sphere At Dusk</title><link>https://mrmatt.io/photography/2025-03-29-sphere-at-dusk/</link><pubDate>Sat, 29 Mar 2025 00:00:00 -0400</pubDate><guid>https://mrmatt.io/photography/2025-03-29-sphere-at-dusk/</guid><description>The Sphere doing what it does best -- being the world's most expensive billboard in the middle of the desert. Dead &amp; Company at the Sphere is the kind of show that makes you fly to Vegas without thinking twice.</description><content:encoded/><media:content url="https://mrmatt.io/photography/2025-03-29-sphere-at-dusk/photo_hu_4561baa21137aa46.webp" medium="image"/></item><item><title>Mike Tyson Then and Now</title><link>https://mrmatt.io/photography/2024-11-15-mike-tyson-then-and-now/</link><pubDate>Fri, 15 Nov 2024 00:00:00 -0500</pubDate><guid>https://mrmatt.io/photography/2024-11-15-mike-tyson-then-and-now/</guid><description>A nostalgic scene capturing retro gaming alongside modern streaming entertainment. The classic arcade cabinet and contemporary Netflix content tell the story of how entertainment has evolved over decades.</description><content:encoded/><media:content url="https://mrmatt.io/photography/2024-11-15-mike-tyson-then-and-now/photo_hu_8bedd55ae8e9439e.webp" medium="image"/></item><item><title>Tacos al Pastor</title><link>https://mrmatt.io/photography/2024-10-19-tacos-al-pastor/</link><pubDate>Sat, 19 Oct 2024 00:00:00 -0400</pubDate><guid>https://mrmatt.io/photography/2024-10-19-tacos-al-pastor/</guid><description>Meat cooks on vertical rotating skewers, building up layers of caramelized, charred exterior while staying tender inside. The technique creates that signature smoky crust and juicy texture that makes this Mexican street food classic so distinctive.</description><content:encoded/><media:content url="https://mrmatt.io/photography/2024-10-19-tacos-al-pastor/photo_hu_c15d449499be362.webp" medium="image"/></item><item><title>Outdoor Steak Dinner</title><link>https://mrmatt.io/photography/2024-10-18-outdoor-steak-dinner/</link><pubDate>Fri, 18 Oct 2024 00:00:00 -0400</pubDate><guid>https://mrmatt.io/photography/2024-10-18-outdoor-steak-dinner/</guid><description>A hearty camping meal spread across weathered wood, featuring a perfectly grilled steak and classic sides with cold drinks nearby. The simple setup captures the satisfaction of cooking and eating well in the great outdoors.</description><content:encoded/><media:content url="https://mrmatt.io/photography/2024-10-18-outdoor-steak-dinner/photo_hu_5625cc113f621646.webp" medium="image"/></item><item><title>E-Bike Marsh Boardwalk</title><link>https://mrmatt.io/photography/2024-07-17-ebike-marsh-boardwalk/</link><pubDate>Wed, 17 Jul 2024 00:00:00 -0400</pubDate><guid>https://mrmatt.io/photography/2024-07-17-ebike-marsh-boardwalk/</guid><description>Morning stop on the marsh boardwalk — miles of green wetland stretching to the treeline under a summer sky.</description><content:encoded/><media:content url="https://mrmatt.io/photography/2024-07-17-ebike-marsh-boardwalk/photo_hu_13c6e1bb3e238d5a.webp" medium="image"/></item><item><title>E-Bike Marsh Sunset</title><link>https://mrmatt.io/photography/2024-07-16-ebike-marsh-sunset/</link><pubDate>Tue, 16 Jul 2024 00:00:00 -0400</pubDate><guid>https://mrmatt.io/photography/2024-07-16-ebike-marsh-sunset/</guid><description>Golden hour ride through the coastal marshlands — the e-bike resting trailside as the sun drops low over the wetlands.</description><content:encoded/><media:content url="https://mrmatt.io/photography/2024-07-16-ebike-marsh-sunset/photo_hu_f8ab7f84c29ceed8.webp" medium="image"/></item><item><title>Salt Marsh Twilight</title><link>https://mrmatt.io/photography/2024-07-16-salt-marsh-twilight/</link><pubDate>Tue, 16 Jul 2024 00:00:00 -0400</pubDate><guid>https://mrmatt.io/photography/2024-07-16-salt-marsh-twilight/</guid><description>That sliver of moon hanging over the marsh is doing all the heavy lifting in this scene. Golden hour at the coast is always good, but golden hour over a salt marsh with a moonrise? That's the whole package.</description><content:encoded/><media:content url="https://mrmatt.io/photography/2024-07-16-salt-marsh-twilight/photo_hu_aee08f0a6bd6a4a7.webp" medium="image"/></item><item><title>Abandoned Cabin Interior</title><link>https://mrmatt.io/photography/2024-06-20-abandoned-cabin-interior/</link><pubDate>Thu, 20 Jun 2024 00:00:00 -0400</pubDate><guid>https://mrmatt.io/photography/2024-06-20-abandoned-cabin-interior/</guid><description>This sparse cabin interior captures the quiet emptiness of a long-abandoned structure, its patina-worn wooden walls and beams telling stories of decades past. Sunlight streams through open doorways, illuminating the dusty floorboards and casting deep shadows across the austere room.</description><content:encoded/><media:content url="https://mrmatt.io/photography/2024-06-20-abandoned-cabin-interior/photo_hu_d780b9cb8e38295f.webp" medium="image"/></item><item><title>Historic Log Cabin</title><link>https://mrmatt.io/photography/2024-06-20-historic-log-cabin/</link><pubDate>Thu, 20 Jun 2024 00:00:00 -0400</pubDate><guid>https://mrmatt.io/photography/2024-06-20-historic-log-cabin/</guid><description>This well-preserved log cabin stands nestled among towering trees, its handcrafted construction and stone chimney speaking to an earlier era of American settlement. The weathered wooden fence in the foreground and the dense forest backdrop create a sense of peaceful isolation, capturing a moment frozen in time.</description><content:encoded/><media:content url="https://mrmatt.io/photography/2024-06-20-historic-log-cabin/photo_hu_b284c1e614dc3ce8.webp" medium="image"/></item><item><title>Mountain Valley Vista</title><link>https://mrmatt.io/photography/2024-06-20-mountain-valley-vista/</link><pubDate>Thu, 20 Jun 2024 00:00:00 -0400</pubDate><guid>https://mrmatt.io/photography/2024-06-20-mountain-valley-vista/</guid><description>An old wooden fence marks the edge of a peaceful valley, its weathered rails leading the eye across open grassland toward distant mountains. The afternoon light and sweeping cirrus clouds create a serene landscape that captures the quiet grandeur of the countryside.</description><content:encoded/><media:content url="https://mrmatt.io/photography/2024-06-20-mountain-valley-vista/photo_hu_a633a29896d5c053.webp" medium="image"/></item><item><title>Chimney Tops Peak</title><link>https://mrmatt.io/photography/2024-06-18-chimney-tops-peak/</link><pubDate>Tue, 18 Jun 2024 00:00:00 -0400</pubDate><guid>https://mrmatt.io/photography/2024-06-18-chimney-tops-peak/</guid><description>The Smokies showing off their signature move: impossibly green peaks punching through the tree canopy. That rocky summit looks like it's daring you to climb it.</description><content:encoded/><media:content url="https://mrmatt.io/photography/2024-06-18-chimney-tops-peak/photo_hu_936c680c4f237bdd.webp" medium="image"/></item><item><title>Newfound Gap Valley</title><link>https://mrmatt.io/photography/2024-06-18-newfound-gap-valley/</link><pubDate>Tue, 18 Jun 2024 00:00:00 -0400</pubDate><guid>https://mrmatt.io/photography/2024-06-18-newfound-gap-valley/</guid><description>Looking down into the heart of the Smokies where the forest just goes and goes. That tiny ribbon of road at the valley floor really puts the scale of these mountains into perspective.</description><content:encoded/><media:content url="https://mrmatt.io/photography/2024-06-18-newfound-gap-valley/photo_hu_61bec549fa2d4a4b.webp" medium="image"/></item><item><title>Smoky Mountain Layers</title><link>https://mrmatt.io/photography/2024-06-18-smoky-mountain-layers/</link><pubDate>Tue, 18 Jun 2024 00:00:00 -0400</pubDate><guid>https://mrmatt.io/photography/2024-06-18-smoky-mountain-layers/</guid><description>The Great Smoky Mountains doing their best watercolor impression. You can just make out a road threading through the valley below, proof that someone decided to pave through all this drama.</description><content:encoded/><media:content url="https://mrmatt.io/photography/2024-06-18-smoky-mountain-layers/photo_hu_c441a96e15a4fdcb.webp" medium="image"/></item><item><title>Smoky Mountain Ridges</title><link>https://mrmatt.io/photography/2024-06-18-smoky-mountain-ridges/</link><pubDate>Tue, 18 Jun 2024 00:00:00 -0400</pubDate><guid>https://mrmatt.io/photography/2024-06-18-smoky-mountain-ridges/</guid><description>They don't call them the Smokies for nothing. Each ridge fades a little more into that signature blue haze until the mountains and sky become the same thing.</description><content:encoded/><media:content url="https://mrmatt.io/photography/2024-06-18-smoky-mountain-ridges/photo_hu_30b92954e114a505.webp" medium="image"/></item><item><title>Beach Shadows</title><link>https://mrmatt.io/photography/2024-04-08-beach-shadows/</link><pubDate>Mon, 08 Apr 2024 00:00:00 -0400</pubDate><guid>https://mrmatt.io/photography/2024-04-08-beach-shadows/</guid><description>A striking play of light captures the elongated shadows of four figures stretching across the sand during late afternoon. The warm, textured beach contrasts beautifully with the dark silhouettes, creating a simple but evocative moment by the water.</description><content:encoded/><media:content url="https://mrmatt.io/photography/2024-04-08-beach-shadows/photo_hu_ab4e972b4947470c.webp" medium="image"/></item><item><title>Total Eclipse</title><link>https://mrmatt.io/photography/2024-04-08-total-eclipse/</link><pubDate>Mon, 08 Apr 2024 00:00:00 -0400</pubDate><guid>https://mrmatt.io/photography/2024-04-08-total-eclipse/</guid><description>Captured during the total solar eclipse from Lake Erie, this image shows the moon perfectly aligned between Earth and the sun, revealing the sun's ethereal corona in full glory. The dramatic contrast between the dark lunar silhouette and the brilliant white glow of the corona against the blackened daytime sky captures the awe-inspiring moment of totality.</description><content:encoded/><media:content url="https://mrmatt.io/photography/2024-04-08-total-eclipse/photo_hu_484d328d6eeb0c10.webp" medium="image"/></item><item><title>Lakeside Pier Lighthouse</title><link>https://mrmatt.io/photography/2024-04-07-lakeside-pier-lighthouse/</link><pubDate>Sun, 07 Apr 2024 00:00:00 -0400</pubDate><guid>https://mrmatt.io/photography/2024-04-07-lakeside-pier-lighthouse/</guid><description>Scale check: one human, one lighthouse, one very blue Great Lake. The peeling paint and rust stains give this beacon a distinguished "I've survived a few winters" kind of charm.</description><content:encoded/><media:content url="https://mrmatt.io/photography/2024-04-07-lakeside-pier-lighthouse/photo_hu_4b2e71a0d51fbf24.webp" medium="image"/></item><item><title>Autumn Forest Vista</title><link>https://mrmatt.io/photography/2023-10-21-autumn-forest-vista/</link><pubDate>Sat, 21 Oct 2023 00:00:00 -0400</pubDate><guid>https://mrmatt.io/photography/2023-10-21-autumn-forest-vista/</guid><description>A scenic overlook through mature forest reveals rolling hills in the distance, their patchwork of fields and woodlands visible between the trees. The forest floor is carpeted with fallen leaves in shades of amber and rust, while overhead the canopy displays the full spectrum of autumn color.</description><content:encoded/><media:content url="https://mrmatt.io/photography/2023-10-21-autumn-forest-vista/photo_hu_9340959003490c4a.webp" medium="image"/></item><item><title>Mayan Pyramid Ruins</title><link>https://mrmatt.io/photography/2023-09-01-mayan-pyramid-ruins/</link><pubDate>Fri, 01 Sep 2023 00:00:00 -0400</pubDate><guid>https://mrmatt.io/photography/2023-09-01-mayan-pyramid-ruins/</guid><description>They really don't build them like they used to -- and I mean that literally, because this thing has been standing for over a thousand years. The little flowering bush at the base is a nice touch of color against all that ancient stone.</description><content:encoded/><media:content url="https://mrmatt.io/photography/2023-09-01-mayan-pyramid-ruins/photo_hu_a6aea36e683516f9.webp" medium="image"/></item><item><title>Ancient Stone Ruins</title><link>https://mrmatt.io/photography/2023-08-31-ancient-stone-ruins/</link><pubDate>Thu, 31 Aug 2023 00:00:00 -0400</pubDate><guid>https://mrmatt.io/photography/2023-08-31-ancient-stone-ruins/</guid><description>A thousand-year-old Mayan structure just casually hanging out on the Cozumel coastline like it's no big deal. The jungle is slowly reclaiming it, which honestly only makes it cooler.</description><content:encoded/><media:content url="https://mrmatt.io/photography/2023-08-31-ancient-stone-ruins/photo_hu_b4683047b94ba2d2.webp" medium="image"/></item><item><title>Crocodile Lagoon</title><link>https://mrmatt.io/photography/2023-08-31-crocodile-lagoon/</link><pubDate>Thu, 31 Aug 2023 00:00:00 -0400</pubDate><guid>https://mrmatt.io/photography/2023-08-31-crocodile-lagoon/</guid><description>Just a crocodile going about its day in a Cozumel lagoon, completely unbothered by the tourists gawking from the shore. That tail cutting through the still water is equal parts majestic and terrifying.</description><content:encoded/><media:content url="https://mrmatt.io/photography/2023-08-31-crocodile-lagoon/photo_hu_537daa027f52ea13.webp" medium="image"/></item><item><title>Island Coastline Aerial</title><link>https://mrmatt.io/photography/2023-08-31-island-coastline-aerial/</link><pubDate>Thu, 31 Aug 2023 00:00:00 -0400</pubDate><guid>https://mrmatt.io/photography/2023-08-31-island-coastline-aerial/</guid><description>Nothing says "tourist excursion" quite like a fleet of brightly colored Jeeps lined up on an island road. But honestly, when the coastline looks like this, you can't blame anyone for wanting the full tour.</description><content:encoded/><media:content url="https://mrmatt.io/photography/2023-08-31-island-coastline-aerial/photo_hu_e6e1ec6b102e527c.webp" medium="image"/></item><item><title>Rocky Shore Overlook</title><link>https://mrmatt.io/photography/2023-08-31-rocky-shore-overlook/</link><pubDate>Thu, 31 Aug 2023 00:00:00 -0400</pubDate><guid>https://mrmatt.io/photography/2023-08-31-rocky-shore-overlook/</guid><description>The view from the top of the Cozumel coast where the Caribbean shifts from deep blue to electric turquoise in a single glance. The tiny humans on the rocks below really sell the scale.</description><content:encoded/><media:content url="https://mrmatt.io/photography/2023-08-31-rocky-shore-overlook/photo_hu_e884909a023429ef.webp" medium="image"/></item><item><title>Rocky Summit Overlook</title><link>https://mrmatt.io/photography/2023-05-28-rocky-summit-overlook/</link><pubDate>Sun, 28 May 2023 00:00:00 -0400</pubDate><guid>https://mrmatt.io/photography/2023-05-28-rocky-summit-overlook/</guid><description>Nothing earns a lunch break quite like scrambling up to a rock ledge with this kind of payoff. The entire valley stretching out below is a solid reminder that trees are absolutely winning at world domination.</description><content:encoded/><media:content url="https://mrmatt.io/photography/2023-05-28-rocky-summit-overlook/photo_hu_40ee69dbefecba2.webp" medium="image"/></item><item><title>Blue Ridge Overcast</title><link>https://mrmatt.io/photography/2023-04-29-blue-ridge-overcast/</link><pubDate>Sat, 29 Apr 2023 00:00:00 -0400</pubDate><guid>https://mrmatt.io/photography/2023-04-29-blue-ridge-overcast/</guid><description>The Blue Ridge doing its best brooding poet impression on a spring afternoon. Those dramatic clouds rolling over the ridgeline are either deeply atmospheric or about to ruin a perfectly good hike.</description><content:encoded/><media:content url="https://mrmatt.io/photography/2023-04-29-blue-ridge-overcast/photo_hu_d9291fbb54f39772.webp" medium="image"/></item><item><title>Yacht Club Waterfront</title><link>https://mrmatt.io/photography/2022-08-23-yacht-club-waterfront/</link><pubDate>Tue, 23 Aug 2022 00:00:00 -0400</pubDate><guid>https://mrmatt.io/photography/2022-08-23-yacht-club-waterfront/</guid><description>Disney's Yacht Club looking impossibly picturesque across the lagoon, like a New England postcard that somebody accidentally dropped in central Florida. The reflection on the water is almost too perfect.</description><content:encoded/><media:content url="https://mrmatt.io/photography/2022-08-23-yacht-club-waterfront/photo_hu_89a6c2aa231ecee6.webp" medium="image"/></item><item><title>Cape Henlopen Shipping</title><link>https://mrmatt.io/photography/2022-07-04-cape-henlopen-shipping/</link><pubDate>Mon, 04 Jul 2022 00:00:00 -0400</pubDate><guid>https://mrmatt.io/photography/2022-07-04-cape-henlopen-shipping/</guid><description>A cargo ship doing its thing on the Fourth of July because global commerce doesn't take holidays. The old Fort Miles buildings below look like they've seen a few Independence Days themselves.</description><content:encoded/><media:content url="https://mrmatt.io/photography/2022-07-04-cape-henlopen-shipping/photo_hu_cebd1e07b3a5aaf0.webp" medium="image"/></item><item><title>Coastal Battery Overlook</title><link>https://mrmatt.io/photography/2022-07-04-coastal-battery-overlook/</link><pubDate>Mon, 04 Jul 2022 00:00:00 -0400</pubDate><guid>https://mrmatt.io/photography/2022-07-04-coastal-battery-overlook/</guid><description>Fourth of July spent exploring old coastal fortifications instead of watching fireworks. These WWII-era lookout towers have better views than most beach houses anyway.</description><content:encoded/><media:content url="https://mrmatt.io/photography/2022-07-04-coastal-battery-overlook/photo_hu_dd4d58e2df19e827.webp" medium="image"/></item><item><title>Tower Above Treeline</title><link>https://mrmatt.io/photography/2022-07-04-tower-above-treeline/</link><pubDate>Mon, 04 Jul 2022 00:00:00 -0400</pubDate><guid>https://mrmatt.io/photography/2022-07-04-tower-above-treeline/</guid><description>Somewhere in that endless sea of green is a solitary tower standing guard over absolutely nothing. It's giving main character energy in a forest that clearly didn't get the memo.</description><content:encoded/><media:content url="https://mrmatt.io/photography/2022-07-04-tower-above-treeline/photo_hu_3621280b72fd2258.webp" medium="image"/></item><item><title>Blue Ridge Vista</title><link>https://mrmatt.io/photography/2022-04-17-blue-ridge-vista/</link><pubDate>Sun, 17 Apr 2022 00:00:00 -0400</pubDate><guid>https://mrmatt.io/photography/2022-04-17-blue-ridge-vista/</guid><description>The Blue Ridge doing what it does best -- stacking mountain ridges into the distance until they dissolve into the sky. Early spring means the trees haven't leafed out yet, which somehow makes the view even more dramatic.</description><content:encoded/><media:content url="https://mrmatt.io/photography/2022-04-17-blue-ridge-vista/photo_hu_2a4a431522a6bf16.webp" medium="image"/></item><item><title>Garden Hedge Maze</title><link>https://mrmatt.io/photography/2022-04-16-garden-hedge-maze/</link><pubDate>Sat, 16 Apr 2022 00:00:00 -0400</pubDate><guid>https://mrmatt.io/photography/2022-04-16-garden-hedge-maze/</guid><description>The hedge maze at Luray Caverns, where you can pretend you're in a period drama while actually just trying to find the exit before lunch. Those Shenandoah mountains in the background don't hurt the vibe either.</description><content:encoded/><media:content url="https://mrmatt.io/photography/2022-04-16-garden-hedge-maze/photo_hu_964101541fdf7b0b.webp" medium="image"/></item><item><title>Wildlife Wall Mural</title><link>https://mrmatt.io/photography/2022-04-15-wildlife-wall-mural/</link><pubDate>Fri, 15 Apr 2022 00:00:00 -0400</pubDate><guid>https://mrmatt.io/photography/2022-04-15-wildlife-wall-mural/</guid><description>Somewhere near Luray, Virginia, a building decided it wasn't getting enough attention and commissioned an entire forest ecosystem on its side. The deer looks surprisingly dignified for being painted on corrugated metal.</description><content:encoded/><media:content url="https://mrmatt.io/photography/2022-04-15-wildlife-wall-mural/photo_hu_eec0ef169e989a34.webp" medium="image"/></item><item><title>Autumn Lake Reflections</title><link>https://mrmatt.io/photography/2021-10-15-autumn-lake-reflections/</link><pubDate>Fri, 15 Oct 2021 00:00:00 -0400</pubDate><guid>https://mrmatt.io/photography/2021-10-15-autumn-lake-reflections/</guid><description>Peak leaf-peeping season at its most serene. The water is so still you could convince yourself the reflection is the real thing.</description><content:encoded/><media:content url="https://mrmatt.io/photography/2021-10-15-autumn-lake-reflections/photo_hu_5a270860c6bb50bf.webp" medium="image"/></item><item><title>Cadillac Mountain Vista</title><link>https://mrmatt.io/photography/2021-06-29-cadillac-mountain-vista-2/</link><pubDate>Tue, 29 Jun 2021 00:00:00 -0400</pubDate><guid>https://mrmatt.io/photography/2021-06-29-cadillac-mountain-vista-2/</guid><description>The kind of view that makes you forget about the thigh-burning hike up. From the granite slopes of Cadillac Mountain, the Atlantic coastline stretches out like nature's own progress bar at 100%.</description><content:encoded/><media:content url="https://mrmatt.io/photography/2021-06-29-cadillac-mountain-vista-2/photo_hu_f7b16fd1b10e260f.webp" medium="image"/></item><item><title>Cadillac Mountain Vista</title><link>https://mrmatt.io/photography/2021-06-29-cadillac-mountain-vista/</link><pubDate>Tue, 29 Jun 2021 00:00:00 -0400</pubDate><guid>https://mrmatt.io/photography/2021-06-29-cadillac-mountain-vista/</guid><description>The payoff for every switchback on the way up Cadillac Mountain -- a view that stretches from the granite under your feet to the islands scattered across Frenchman Bay. Worth every bead of sweat.</description><content:encoded/><media:content url="https://mrmatt.io/photography/2021-06-29-cadillac-mountain-vista/photo_hu_b6182b0b4be59a2b.webp" medium="image"/></item><item><title>Rocky Summit Treeline</title><link>https://mrmatt.io/photography/2021-06-28-rocky-summit-treeline/</link><pubDate>Mon, 28 Jun 2021 00:00:00 -0400</pubDate><guid>https://mrmatt.io/photography/2021-06-28-rocky-summit-treeline/</guid><description>The summit playing hard to get, barely visible through the determined treeline. Those evergreens are giving it everything they've got to claim the last few feet before the rock takes over.</description><content:encoded/><media:content url="https://mrmatt.io/photography/2021-06-28-rocky-summit-treeline/photo_hu_843520e7898907e3.webp" medium="image"/></item><item><title>Rural Aerial View</title><link>https://mrmatt.io/photography/2020-09-05-rural-aerial-view/</link><pubDate>Sat, 05 Sep 2020 00:00:00 -0400</pubDate><guid>https://mrmatt.io/photography/2020-09-05-rural-aerial-view/</guid><description>Drone's-eye view of the kind of countryside where the roads curve for no reason other than because that's how the land wanted it. Late summer green as far as the eye can see, dotted with the occasional farmstead.</description><content:encoded/><media:content url="https://mrmatt.io/photography/2020-09-05-rural-aerial-view/photo_hu_5208752bf8b68c2a.webp" medium="image"/></item><item><title>Rolling Green Fields</title><link>https://mrmatt.io/photography/2020-09-04-rolling-green-fields/</link><pubDate>Fri, 04 Sep 2020 00:00:00 -0400</pubDate><guid>https://mrmatt.io/photography/2020-09-04-rolling-green-fields/</guid><description>Virginia farmland from a few hundred feet up, where the patchwork of green fields and tree lines looks like someone laid out the world's most satisfying quilt. Drone photography makes everything look like a painting.</description><content:encoded/><media:content url="https://mrmatt.io/photography/2020-09-04-rolling-green-fields/photo_hu_334c79a74035db9d.webp" medium="image"/></item><item><title>Rainforest Waterfall</title><link>https://mrmatt.io/photography/2019-05-14-rainforest-waterfall/</link><pubDate>Tue, 14 May 2019 00:00:00 -0400</pubDate><guid>https://mrmatt.io/photography/2019-05-14-rainforest-waterfall/</guid><description>There's something humbling about standing at the base of a waterfall in El Yunque and realizing nature's been running this show way longer than you've been hiking to see it. Worth every muddy step on the trail.</description><content:encoded/><media:content url="https://mrmatt.io/photography/2019-05-14-rainforest-waterfall/photo_hu_74df0ce6d871b630.webp" medium="image"/></item><item><title>Bermuda Blue Panorama</title><link>https://mrmatt.io/photography/2019-05-11-bermuda-blue-panorama/</link><pubDate>Sat, 11 May 2019 00:00:00 -0400</pubDate><guid>https://mrmatt.io/photography/2019-05-11-bermuda-blue-panorama/</guid><description>The kind of blue that makes you question whether your phone camera is lying to you. It wasn't -- Bermuda's water really is that absurdly turquoise.</description><content:encoded/><media:content url="https://mrmatt.io/photography/2019-05-11-bermuda-blue-panorama/photo_hu_489ff5ffdc3f9f5e.webp" medium="image"/></item><item><title>Bermuda Cove Overlook</title><link>https://mrmatt.io/photography/2019-05-11-bermuda-cove-overlook/</link><pubDate>Sat, 11 May 2019 00:00:00 -0400</pubDate><guid>https://mrmatt.io/photography/2019-05-11-bermuda-cove-overlook/</guid><description>The kind of view that makes you reconsider every life choice that didn't end with you living on this exact spot. Bermuda delivering impossibly turquoise water, a private little cove, and more shades of blue than should be legal.</description><content:encoded/><media:content url="https://mrmatt.io/photography/2019-05-11-bermuda-cove-overlook/photo_hu_8ea27fba3bc2610.webp" medium="image"/></item><item><title>Forest Floor Moss</title><link>https://mrmatt.io/photography/2018-10-28-forest-floor-moss/</link><pubDate>Sun, 28 Oct 2018 00:00:00 -0400</pubDate><guid>https://mrmatt.io/photography/2018-10-28-forest-floor-moss/</guid><description>Getting down to moss-level and discovering a tiny, impossibly green world. This stuff was growing on what appears to be a fallen log, turning decay into something genuinely beautiful.</description><content:encoded/><media:content url="https://mrmatt.io/photography/2018-10-28-forest-floor-moss/photo_hu_4cd03fd131ff4655.webp" medium="image"/></item><item><title>Grilling Burgers and Steaks</title><link>https://mrmatt.io/photography/2018-05-12-grilling-burgers-and-steaks/</link><pubDate>Sat, 12 May 2018 00:00:00 -0400</pubDate><guid>https://mrmatt.io/photography/2018-05-12-grilling-burgers-and-steaks/</guid><description>A backyard cookout in progress, with burgers and steaks sizzling on the grill. The meat shows nice char marks as it cooks over the hot surface, smoke drifting up in the warm air.</description><content:encoded/><media:content url="https://mrmatt.io/photography/2018-05-12-grilling-burgers-and-steaks/photo_hu_44db5f01e6624664.webp" medium="image"/></item><item><title>Caribbean Island Approach</title><link>https://mrmatt.io/photography/2018-04-22-caribbean-island-approach/</link><pubDate>Sun, 22 Apr 2018 00:00:00 -0400</pubDate><guid>https://mrmatt.io/photography/2018-04-22-caribbean-island-approach/</guid><description>Approaching what looks like a Caribbean island from the water, where green mountains meet deep blue sea and the houses cling to the hillsides like they picked the best seats in the house. Not a bad commute.</description><content:encoded/><media:content url="https://mrmatt.io/photography/2018-04-22-caribbean-island-approach/photo_hu_9d83fda65b0946fd.webp" medium="image"/></item><item><title>Island Mountain Farm</title><link>https://mrmatt.io/photography/2018-04-21-island-mountain-farm/</link><pubDate>Sat, 21 Apr 2018 00:00:00 -0400</pubDate><guid>https://mrmatt.io/photography/2018-04-21-island-mountain-farm/</guid><description>Caribbean farming at its finest -- palm trees, solar panels, and a volcano backdrop that makes your local farmer's market look a little less impressive. St. Kitts really knows how to do pastoral scenery.</description><content:encoded/><media:content url="https://mrmatt.io/photography/2018-04-21-island-mountain-farm/photo_hu_342a41fda16d26f8.webp" medium="image"/></item><item><title>Windswept Palms</title><link>https://mrmatt.io/photography/2018-04-21-windswept-palms/</link><pubDate>Sat, 21 Apr 2018 00:00:00 -0400</pubDate><guid>https://mrmatt.io/photography/2018-04-21-windswept-palms/</guid><description>These palms have clearly been through some things, but they're still standing -- which is more than most of us can say after a Caribbean cruise buffet. Mount Liamuiga looming in the background adds just the right amount of drama.</description><content:encoded/><media:content url="https://mrmatt.io/photography/2018-04-21-windswept-palms/photo_hu_89a08a9e4c76ddb7.webp" medium="image"/></item><item><title>Tropical Beach Day</title><link>https://mrmatt.io/photography/2018-04-18-tropical-beach-day/</link><pubDate>Wed, 18 Apr 2018 00:00:00 -0400</pubDate><guid>https://mrmatt.io/photography/2018-04-18-tropical-beach-day/</guid><description>A perfect beach day captured in brilliant turquoise waters and fine white sand, with visitors scattered throughout the shallow bay. Dark volcanic islands frame the horizon while puffy clouds drift across the blue sky, creating an idyllic tropical setting.</description><content:encoded/><media:content url="https://mrmatt.io/photography/2018-04-18-tropical-beach-day/photo_hu_79e7cc34298d7b73.webp" medium="image"/></item><item><title>Green Mountain Meadow</title><link>https://mrmatt.io/photography/2017-08-01-green-mountain-meadow/</link><pubDate>Tue, 01 Aug 2017 00:00:00 -0400</pubDate><guid>https://mrmatt.io/photography/2017-08-01-green-mountain-meadow/</guid><description>Vermont in summer doing exactly what you'd expect -- rolling green fields, layered mountain ridges fading into blue, and not a single thing to complain about. Postcard material without even trying.</description><content:encoded/><media:content url="https://mrmatt.io/photography/2017-08-01-green-mountain-meadow/photo_hu_6369f49cc2470ac.webp" medium="image"/></item><item><title>Valley Tree Line</title><link>https://mrmatt.io/photography/2017-08-01-valley-tree-line/</link><pubDate>Tue, 01 Aug 2017 00:00:00 -0400</pubDate><guid>https://mrmatt.io/photography/2017-08-01-valley-tree-line/</guid><description>Another angle of the Vermont countryside where every shade of green gets its moment. The mountains just keep stacking up behind each other like they're in line for something.</description><content:encoded/><media:content url="https://mrmatt.io/photography/2017-08-01-valley-tree-line/photo_hu_4563ac9ce3f8532d.webp" medium="image"/></item><item><title>Pantry and Closet Light Automation</title><link>https://mrmatt.io/posts/pantry-closet-light-automation/</link><pubDate>Thu, 02 Feb 2017 00:00:00 -0500</pubDate><guid>https://mrmatt.io/posts/pantry-closet-light-automation/</guid><description>Automating pantry and closet lights with SmartThings — using a multi-sensor's contact switch and accelerometer to trigger lights on door open and movement.</description><content:encoded>&lt;p&gt;One of the most common home automations is lighting. You need it when you need it and you want to conserve when you don&amp;rsquo;t. One of the main scenarios for my home is my pantry and closets. If you need something from them, it is helpful to have light.&lt;/p&gt;
&lt;h3 id="the-problem"&gt;The Problem&lt;/h3&gt;
&lt;p&gt;I started with a basic contact switch which works well if you always close the door. I don&amp;rsquo;t know if your house is like mine, but that doesn&amp;rsquo;t always happen. When the door opens, the light turns on, when it closes, the light turns off. You could also add an allowance to turn the light off after it has been on for a certain period of time. But what happens when you come back and the light is off? Open and close the door? Toggle the switch?&lt;/p&gt;
&lt;h3 id="the-solution"&gt;The Solution&lt;/h3&gt;
&lt;p&gt;Enter SmartThings multi-sensor. They also have an accelerometer. This means if we mount the base on the door, we can use the door&amp;rsquo;s motion to trigger the light too. So in the scenario above, if the light is off and the door is open, a simple or in most cases necessary movement of the door will turn the light back on!&lt;/p&gt;
&lt;p&gt;Ok, so now how to put this all together. We want the light to:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Turn on when the door is open&lt;/li&gt;
&lt;li&gt;Turn on when the door moves&lt;/li&gt;
&lt;li&gt;Turn off when the door is closed&lt;/li&gt;
&lt;li&gt;Turn off after n-minutes of inactivity&lt;/li&gt;
&lt;/ol&gt;
&lt;img src="https://mrmatt.io/img/AutomatePantryClosetFlow.png" alt="Automated Pantry and Closet Flow" class="boxshadow"&gt;
&lt;h3 id="hardware"&gt;Hardware&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href="https://www.amazon.com/gp/product/B010NZV0GE/ref=as_li_tl?ie=UTF8&amp;amp;camp=1789&amp;amp;creative=9325&amp;amp;creativeASIN=B010NZV0GE&amp;amp;linkCode=as2&amp;amp;tag=matwalstecand-20&amp;amp;linkId=f418d2a528302c5933143b9dcf651585"&gt;SmartThings&lt;/a&gt; (obviously)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.amazon.com/gp/product/B0118RQW3W/ref=as_li_tl?ie=UTF8&amp;amp;camp=1789&amp;amp;creative=9325&amp;amp;creativeASIN=B0118RQW3W&amp;amp;linkCode=as2&amp;amp;tag=matwalstecand-20&amp;amp;linkId=f93d0b890c3fe6c55d0206ac8781d58f"&gt;SmartThings MultiSensor&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Switch or Bulb. I am using the &lt;a href="https://www.amazon.com/gp/product/B01701DL7A/ref=as_li_tl?ie=UTF8&amp;amp;camp=1789&amp;amp;creative=9325&amp;amp;creativeASIN=B01701DL7A&amp;amp;linkCode=as2&amp;amp;tag=matwalstecand-20&amp;amp;linkId=9367f338ac2e61c8fd29758c1f02c137"&gt;Cree Connected&lt;/a&gt; bulbs. They work well because this solution doesn&amp;rsquo;t require a physical switch and the bulb can be always powered.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="smartapp"&gt;SmartApp&lt;/h3&gt;
&lt;p&gt;There was no native solution that puts all this together. So I wrote my own SmartApp. The key is around handing the cron tasks for the allowance so they reset on activity.&lt;/p&gt;
&lt;h4 id="smartapp-configuration"&gt;SmartApp configuration&lt;/h4&gt;
&lt;img src="https://mrmatt.io/img/SmartThingsPantry.png" alt="SmartThings Pantry App" class="boxshadow"&gt;
&lt;h4 id="smartapp-code"&gt;SmartApp Code&lt;/h4&gt;
&lt;p&gt;Here is the code, also available on &lt;a href="https://github.com/MrMatt57/SmartThings/blob/master/SmartApps/AutomatedCloset.groovy"&gt;GitHub&lt;/a&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-Groovy" data-lang="Groovy"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cm"&gt;/**
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cm"&gt; * Closet Door
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cm"&gt; *
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cm"&gt; * Copyright 2015 Matthew Walker
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cm"&gt; *
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cm"&gt; * Licensed under the Apache License, Version 2.0 (the &amp;#34;License&amp;#34;); you may not use this file except
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cm"&gt; * in compliance with the License. You may obtain a copy of the License at:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cm"&gt; *
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cm"&gt; * http://www.apache.org/licenses/LICENSE-2.0
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cm"&gt; *
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cm"&gt; * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cm"&gt; * on an &amp;#34;AS IS&amp;#34; BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cm"&gt; * for the specific language governing permissions and limitations under the License.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cm"&gt; *
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cm"&gt; */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;definition&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nl"&gt;name:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Automated Closet&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nl"&gt;namespace:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nl"&gt;author:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Matthew Walker&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nl"&gt;description:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Automated Closet&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nl"&gt;category:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Convenience&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nl"&gt;iconUrl:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience.png&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nl"&gt;iconX2Url:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience@2x.png&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nl"&gt;iconX3Url:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience@2x.png&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;preferences&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;section&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;When these sensors are activated...&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;input&lt;/span&gt; &lt;span class="nl"&gt;name:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;contactSensor&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;type:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;capability.contactSensor&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;title:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Contact Sensor&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;multiple:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;input&lt;/span&gt; &lt;span class="nl"&gt;name:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;knockSensor&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;type:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;capability.accelerationSensor&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;title:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Movement Sensor&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;required:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;multiple:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;section&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Turn this on...&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;input&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;switchDevice&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;capability.switch&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;title:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Switch?&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;required:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;multiple:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;input&lt;/span&gt; &lt;span class="nl"&gt;name:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;turnOff&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;type:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;bool&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;title:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Turn off when Closed?&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;input&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;allowance&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;number&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;title:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Leave on for Minutes? (0 Forever)&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kt"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;installed&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;init&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kt"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;updated&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;unsubscribe&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;init&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kt"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;knockSensor&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;subscribe&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;knockSensor&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;acceleration.active&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;doorKnockHandler&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;	&lt;span class="n"&gt;subscribe&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;contactSensor&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;contact&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sensorHandler&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kt"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;sensorHandler&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;evt&lt;/span&gt;&lt;span class="o"&gt;){&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;debug&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;$evt.value&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;evt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;value&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;open&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;turnOnSwitch&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="nf"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;evt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;value&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;closed&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;turnOff&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;turnOffSwitch&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kt"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;doorKnockHandler&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;evt&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;contactSensor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;latestValue&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;contact&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;closed&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;){&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="c1"&gt;// door is closed
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;turnOnSwitch&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kt"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;turnOnSwitch&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;	&lt;span class="n"&gt;switchDevice&lt;/span&gt;&lt;span class="o"&gt;?.&lt;/span&gt;&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;	&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;allowance&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;allowance&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; 	&lt;span class="n"&gt;unschedule&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="c1"&gt;// reset timers
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;		&lt;span class="kt"&gt;def&lt;/span&gt; &lt;span class="n"&gt;delay&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;allowance&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;		&lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;debug&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Turning off in ${allowance} minutes (${delay}seconds)&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;		&lt;span class="n"&gt;runIn&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;delay&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;turnOffSwitch&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kt"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;turnOffSwitch&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;	&lt;span class="n"&gt;switchDevice&lt;/span&gt;&lt;span class="o"&gt;?.&lt;/span&gt;&lt;span class="na"&gt;off&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;unschedule&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="c1"&gt;// reset timers
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content:encoded><media:content url="https://mrmatt.io/images/MattWalker-avatar.png" medium="image"/></item><item><title>Broadening horizons with web accessibility, testing and automation (Hugo, Sitemap.xml and Pa11y)</title><link>https://mrmatt.io/posts/broadening-horizons-in-web-accessibility/</link><pubDate>Sat, 21 Jan 2017 00:00:00 -0500</pubDate><guid>https://mrmatt.io/posts/broadening-horizons-in-web-accessibility/</guid><description>Automating web accessibility testing with Pa11y and HTML CodeSniffer — integrating WCAG 2.0 AA checks into a Hugo static site's CI build pipeline.</description><content:encoded>&lt;p&gt;When rebuilding my site I wanted to start as simple as possible, put the sledge hammers away and carefully evaluate each decision as things get built up. Not just with the technology stack, but the content too.&lt;/p&gt;
&lt;p&gt;In the past, reading through accessibility standards like &lt;a href="https://www.section508.gov/"&gt;508&lt;/a&gt; and &lt;a href="https://www.w3.org/WAI/intro/wcag.php"&gt;WCAG&lt;/a&gt; were daunting and felt like red tape. I have always aspired to having semantic markup, mostly from a clean code and Search Engine Optimization standpoint. True web accessibility was always on the back-burner.&lt;/p&gt;
&lt;p&gt;In a recent project at work, I was turned onto &lt;a href="https://squizlabs.github.io/HTML_CodeSniffer/"&gt;HTML Sniffer&lt;/a&gt; and the tools that surround it, namely &lt;a href="https://pa11y.org/"&gt;Pa11y&lt;/a&gt;. They have not only helped me begin to charter the world of web accessibility but have given me a greater appreciation for why it is needed. They had to be a fundamental part of my development toolchain.&lt;/p&gt;
&lt;h3 id="html-sniffer-bookmarklet"&gt;HTML Sniffer bookmarklet&lt;/h3&gt;
&lt;p&gt;The &lt;a href="https://squizlabs.github.io/HTML_CodeSniffer/"&gt;bookmarklet&lt;/a&gt; alone is a great tool for manually testing and learning more about accessibility standards. It shows you an interactive view of what needs to be fixed (errors), things to review and possibly resolve (warnings) and helpful guides to get you in the right mindset (notices).&lt;/p&gt;
&lt;p&gt;&lt;img alt="HTML Sniffer Bookmarklet" loading="lazy" src="https://mrmatt.io/img/html-sniffer-bookmarklet.png"&gt;&lt;/p&gt;
&lt;h3 id="automating-testing-with-pa11y-ci"&gt;Automating testing with Pa11y-ci&lt;/h3&gt;
&lt;p&gt;For every post or change to the website it isn&amp;rsquo;t feasible to regression test every page. This is where Pa11y comes in. It provides a command-line interface and some reporting tools on top of HTML Sniffer.&lt;/p&gt;
&lt;p&gt;I evaluated Pa11y dashboard, but in the theme of a static site I didn&amp;rsquo;t want to host it somewhere (runs on Node.js/MongoDb). I was also interested to see what the &lt;a href="https://github.com/pa11y/sidekick"&gt;Pa11y Sidekick&lt;/a&gt; project does. In the meantime, I decided to integrate it into my own build process.&lt;/p&gt;
&lt;p&gt;Here is how it works.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Continuous integration build kicks off&lt;/li&gt;
&lt;li&gt;Hugo generates static content along with sitemap.xml of all the pages in the site.&lt;/li&gt;
&lt;li&gt;Starts up lightweight server with http-server&lt;/li&gt;
&lt;li&gt;Runs pa11y-ci command pointed at locally hosted sitemap.xml, replacing normal host with localhost.&lt;/li&gt;
&lt;li&gt;Pa11y executes a headless PhantomJS browser against all pages in the sitemap and reports any errors.&lt;/li&gt;
&lt;li&gt;If the command exits with an error, the build fails and no harm, no foul is done to the site.&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 id="commands"&gt;Commands&lt;/h4&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Build site and site map to /public directory&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Install node modules&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;npm install -g http-server pa11y pa11y-ci
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Start local server against static site files&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;http-server ./public --silent &lt;span class="p"&gt;&amp;amp;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Run pa11y against sitemap&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;pa11y-ci --sitemap http://localhost:8080/sitemap.xml &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --sitemap-find &lt;span class="s2"&gt;&amp;#34;https://mrmatt.io/&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --sitemap-replace &lt;span class="s2"&gt;&amp;#34;http://localhost:8080/&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;em&gt;The full configuration can be found in the sites &lt;a href="https://github.com/MrMatt57/MrMatt.io/blob/master/.travis.yml"&gt;travis-ci config&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;h4 id="output"&gt;Output&lt;/h4&gt;
&lt;p&gt;Here is an example of what it looks like when an error is thrown.
&lt;img alt="Pa11y-ci error output" loading="lazy" src="https://mrmatt.io/img/pa11y-ci.jpg"&gt;&lt;/p&gt;
&lt;h4 id="configuration"&gt;Configuration&lt;/h4&gt;
&lt;p&gt;There are many configuration options outside of what is available through the pa11y-ci command line. A
&lt;code&gt;.pa11yci&lt;/code&gt; can serve as a proxy to the underlying pa11y configuration. Mine is pretty basic, setting a page timeout limit of 10 seconds and the target standard. I chose WCAG2AA as a strict but good balance for testing rules.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;defaults&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;timeout&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;standard&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;WCAG2AA&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;em&gt;At &lt;a href="https://github.com/MrMatt57/MrMatt.io/commit/597d47e6746dc196aaf5f51b3f648f6d62bbbdb0"&gt;one point&lt;/a&gt; I had to disable a rule becuase Disqus comments were injecting a poorly formatted iframe. I have since remove Discus in favor for static comments.&lt;/em&gt;&lt;/p&gt;
&lt;h3 id="development-testing"&gt;Development Testing&lt;/h3&gt;
&lt;p&gt;To test locally, I created a Node.js &amp;ldquo;test&amp;rdquo; script command to go agains the Hugo hosted site.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;scripts&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;test&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;pa11y-ci --sitemap http://localhost:3000/sitemap.xml&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="conclusion"&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;I know this barely scratchs the surface of accessability testing, but if anything I hope sparks an interest in learning more about it as it has for me. It also helps serve as a gatekeeper for any not-so-good markup making it into the site. I think the &lt;a href="https://github.com/pa11y/sidekick"&gt;Sidekick&lt;/a&gt; project will bring better reporting and analysis, but for now the bookmarklet and build integration keep me at least a little better tuned in.&lt;/p&gt;</content:encoded><media:content url="https://mrmatt.io/images/MattWalker-avatar.png" medium="image"/></item><item><title>Static Site Caching with a CDN (Hugo, S3 and KeyCDN)</title><link>https://mrmatt.io/posts/static-site-content-caching/</link><pubDate>Sat, 14 Jan 2017 00:00:00 -0500</pubDate><guid>https://mrmatt.io/posts/static-site-content-caching/</guid><description>Setting up cache control headers for a static Hugo site on S3 with KeyCDN — caching static assets while keeping HTML fresh on each deploy.</description><content:encoded>&lt;p&gt;I am really enjoying the &lt;a href="https://mrmatt.io/posts/website-update-2016/"&gt;switch&lt;/a&gt; to a static Hugo site. One of the main benefits is not having to run a server to host it. That also means no &lt;code&gt;.htaccess&lt;/code&gt; or &lt;code&gt;web.config&lt;/code&gt; files to set headers or caching policies. With a traditional site &amp;ldquo;static&amp;rdquo; assets can be safely cached and dynamic content will be served fresh each request. Now that the whole site is static, we need to separate how things are cached.&lt;/p&gt;
&lt;p&gt;This site's &lt;a href="https://mrmatt.io/stack/"&gt;toolchain&lt;/a&gt; uses s3cmd sync to deploy files to an Amazon S3 bucket that I use as an origin for KeyCDN. In most CDNs the only way to vary cache control headers is to take the value from the origin. So this means setting the headers in S3. To do this, you can use s3cmd&amp;rsquo;s to vary headers based on file types. In my current implementation I am considering &lt;code&gt;html&lt;/code&gt;, &lt;code&gt;xml&lt;/code&gt;, and &lt;code&gt;json&lt;/code&gt; files that may change and should not be browser cached. Everything else I am caching for 24 hours.&lt;/p&gt;
&lt;h3 id="static-files-to-cache"&gt;Static files to cache&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;s3cmd sync --delete-removed &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --exclude &lt;span class="s1"&gt;&amp;#39;.git/*&amp;#39;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --rexclude &lt;span class="s2"&gt;&amp;#34;^(.*\.((html|xml|json)&lt;/span&gt;$&lt;span class="s2"&gt;))*&lt;/span&gt;$&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --add-header&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Cache-Control: max-age=1440&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --acl-public &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --preserve &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --no-mime-magic &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --guess-mime-type &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --recursive &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; public/ &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; s3://BUCKETNAME/ 
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="no-cache-files"&gt;No-Cache files&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;s3cmd sync --delete-removed &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --exclude &lt;span class="s1"&gt;&amp;#39;*&amp;#39;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --rinclude &lt;span class="s2"&gt;&amp;#34;^(.*\.((html|xml|json)&lt;/span&gt;$&lt;span class="s2"&gt;))*&lt;/span&gt;$&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --add-header&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Cache-Control: no-cache, no-store, must-revalidate&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --add-header&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Pragma: no-cache&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --add-header&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Expires: 0&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --acl-public &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --preserve &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --no-mime-magic &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --guess-mime-type &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --recursive &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; public/ &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; s3://BUCKETNAME/ 
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The full source can be found in the &lt;a href="https://github.com/MrMatt57/MrMatt.io/blob/master/.travis.yml"&gt;travis-ci config&lt;/a&gt; and the &lt;a href="https://github.com/MrMatt57/MrMatt.io/blob/master/deploy.sh"&gt;deploy.sh&lt;/a&gt; script on Github.&lt;/p&gt;
&lt;h3 id="cdn-settings"&gt;CDN settings&lt;/h3&gt;
&lt;p&gt;In your CDN set your Expire setting to honor your origin&amp;rsquo;s server header.&lt;/p&gt;
&lt;p&gt;Now static files; &lt;code&gt;css&lt;/code&gt;, &lt;code&gt;js&lt;/code&gt;, &lt;code&gt;fonts&lt;/code&gt; and &lt;code&gt;images&lt;/code&gt; are browser cached and only the main page is served for each request.
&lt;img alt="Site Cache Profile" loading="lazy" src="https://mrmatt.io/img/CacheProfile.jpg"&gt;&lt;/p&gt;
&lt;h3 id="bonus"&gt;Bonus&lt;/h3&gt;
&lt;p&gt;Purge your KeyCDN cache with each deployment with an API call&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;curl &lt;span class="s2"&gt;&amp;#34;https://api.keycdn.com/zones/purge/{ID}.json&amp;#34;&lt;/span&gt; -u &lt;span class="o"&gt;{&lt;/span&gt;KeyCDNToken&lt;span class="o"&gt;}&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content:encoded><media:content url="https://mrmatt.io/images/MattWalker-avatar.png" medium="image"/></item><item><title>Looking Back on 2016</title><link>https://mrmatt.io/posts/2016-looking-back/</link><pubDate>Sun, 01 Jan 2017 00:00:00 -0500</pubDate><guid>https://mrmatt.io/posts/2016-looking-back/</guid><description>A look back at 2016 — homeschooling milestones, a record largemouth bass at Liberty Reservoir, government open-source work with 18F, and more.</description><content:encoded>&lt;p&gt;Looking back on 2016, it was a good year. Here are some of my highlights.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Family
&lt;ul&gt;
&lt;li&gt;Homeshooling officially started&lt;/li&gt;
&lt;li&gt;Swim lessons and Little Ninja&amp;rsquo;s&lt;/li&gt;
&lt;li&gt;Family BBQ&lt;/li&gt;
&lt;li&gt;Visit at the River House&lt;/li&gt;
&lt;li&gt;Two nephews on the way!&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Work
&lt;ul&gt;
&lt;li&gt;Helped promote modern software development practices within the governement with 18f and the Department of Labor. &lt;a href="https://github.com/18F/dol-whd-14c"&gt;Github repo&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Fishing
&lt;ul&gt;
&lt;li&gt;Went on a Father and Son trip to Dale Hollow with my Dad.&lt;/li&gt;
&lt;li&gt;Ignited the passion in my oldest son with several day trips to Liberty. He&amp;rsquo;s after the big one!&lt;/li&gt;
&lt;li&gt;Caught my personal best Largemouth Bass 7lbs, 23&amp;quot;! (Liberty Reservior, 1/4 oz white, gold/siver willow spinnerbait)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Misc
&lt;ul&gt;
&lt;li&gt;Reserved my place in line for a Tesla Model 3.&lt;/li&gt;
&lt;li&gt;Got my Technician and General class amatuer radio operators licenses.&lt;/li&gt;
&lt;li&gt;Converted from heating oil to naturual gas at home.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img alt="Personal Best Liberty Bass" loading="lazy" src="https://mrmatt.io/img/LibertyBass.jpg"&gt;
&lt;img alt="He&amp;rsquo;s got the Fishing Bug" loading="lazy" src="https://mrmatt.io/img/FishingBug.jpg"&gt;
&lt;img alt="Grilling" loading="lazy" src="https://mrmatt.io/img/Grilling.jpg"&gt;
&lt;img alt="Brisket and Boston Butt" loading="lazy" src="https://mrmatt.io/img/BBQ.jpg"&gt;
&lt;img alt="Spiedies" loading="lazy" src="https://mrmatt.io/img/Spiedies.jpg"&gt;
&lt;img alt="Gas Conversion" loading="lazy" src="https://mrmatt.io/img/GasConversion.jpg"&gt;
&lt;img alt="Gas Line" loading="lazy" src="https://mrmatt.io/img/GasLine.jpg"&gt;
&lt;img alt="Tesla Model 3 Reservation Thank You." loading="lazy" src="https://mrmatt.io/img/Tesla.jpg"&gt;&lt;/p&gt;</content:encoded><media:content url="https://mrmatt.io/img/LibertyBass.jpg" medium="image"/></item><item><title>Another site redo (Ghost &gt; Hugo)</title><link>https://mrmatt.io/posts/website-update-2016/</link><pubDate>Sat, 31 Dec 2016 00:00:00 -0500</pubDate><guid>https://mrmatt.io/posts/website-update-2016/</guid><description>Rebuilding the site from Ghost to Hugo — static files, no jQuery, no Bootstrap, better accessibility standards, and a GitHub + Travis CI devops pipeline.</description><content:encoded>&lt;p&gt;It was two years ago since I made my last &lt;a href="https://mrmatt.io/posts/website-update-2014/"&gt;website update&lt;/a&gt;. I moved the site from a custom hosted Wordpress framework over to Ghost. I was and still am interested in full stack javascript solutions. It was no lack of the platform for not keeping things updated, I just didn&amp;rsquo;t have time to post. I plan to change that this year, with a new an improved &lt;a href="https://mrmatt.io/stack/"&gt;website stack&lt;/a&gt; built on Hugo with a solid &lt;a href="https://github.com/MrMatt57/MrMatt57.org"&gt;open source&lt;/a&gt; based devops toolchain.&lt;/p&gt;
&lt;p&gt;Building on the last website update, my focuses are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Standards Based Markup/Content&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;Better Semantic Markup&lt;/li&gt;
&lt;li&gt;Follow WCAG 2.0 AA Guidlines&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Better DevOps&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;Github&lt;/li&gt;
&lt;li&gt;Travis-ci&lt;/li&gt;
&lt;li&gt;Accessabilty Testing&lt;/li&gt;
&lt;li&gt;Branch based Deployments&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Even Lighter and Faster&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;Static files built with Gulp and Hugo&lt;/li&gt;
&lt;li&gt;No JQuery&lt;/li&gt;
&lt;li&gt;No Bootstrap&lt;/li&gt;
&lt;li&gt;Javascript not required&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These are just a few areas of improvement. Keep an eye on the sites &lt;a href="https://mrmatt.io/stack/"&gt;stack&lt;/a&gt; page for a full listing of what makes the new site run.&lt;/p&gt;
&lt;h3 id="homage"&gt;Homage&lt;/h3&gt;
&lt;p&gt;&lt;img alt="MrMatt57.org Ghost Screenshot" loading="lazy" src="https://mrmatt.io/img/MrMatt57org-Ghost.jpg"&gt;&lt;/p&gt;</content:encoded><media:content url="https://mrmatt.io/img/MrMatt57org-Ghost.jpg" medium="image"/></item><item><title>Backyard Brew Station</title><link>https://mrmatt.io/photography/2015-06-15-backyard-brew-station/</link><pubDate>Mon, 15 Jun 2015 00:00:00 -0400</pubDate><guid>https://mrmatt.io/photography/2015-06-15-backyard-brew-station/</guid><description>A well-organized home brewing setup sits ready on a stone patio, featuring a custom wooden cart laden with coolers and accessories. The lush garden backdrop and brick chimney suggest this is a cherished outdoor entertaining space designed for batches and gatherings.</description><content:encoded/><media:content url="https://mrmatt.io/photography/2015-06-15-backyard-brew-station/photo_hu_89663b0325114098.webp" medium="image"/></item><item><title>Fire Pit In Snow</title><link>https://mrmatt.io/photography/2015-03-07-fire-pit-in-snow/</link><pubDate>Sat, 07 Mar 2015 00:00:00 -0500</pubDate><guid>https://mrmatt.io/photography/2015-03-07-fire-pit-in-snow/</guid><description>The eternal winter debate settled: yes, you absolutely can have a fire pit party when there's snow on the ground. In fact, the contrast of roaring flames against fresh white snow makes it objectively better.</description><content:encoded/><media:content url="https://mrmatt.io/photography/2015-03-07-fire-pit-in-snow/photo_hu_84c2dd30f63476d1.webp" medium="image"/></item><item><title>Website Update (Wordpress &gt; Ghost)</title><link>https://mrmatt.io/posts/website-update-2014/</link><pubDate>Tue, 30 Dec 2014 00:00:00 -0500</pubDate><guid>https://mrmatt.io/posts/website-update-2014/</guid><description>Migrating from WordPress to Ghost — responsive design, modern JavaScript stack, npm and Bower package management, and a content-first approach.</description><content:encoded>&lt;p&gt;&lt;strong&gt;12/31/2016: My site is now on Hugo, check out the &lt;a href="https://mrmatt.io/posts/website-update-2016/"&gt;update&lt;/a&gt;.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;It has been over four years since my last blog post. It is no coincidence that my oldest son is about to turn four. Life has been busy and more rewarding than ever. As time has gone by, my website aged and no longer represented me. The site was long overdue for an update.&lt;/p&gt;
&lt;h3 id="new-site"&gt;New Site:&lt;/h3&gt;
&lt;p&gt;When designing the new site, here were some things I considered.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Responsive Web Design&lt;/strong&gt; - Has to work on all screen sizes (Mobile First)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Modern Development Stack&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;Ghost Blog Platform - Javascript&lt;/li&gt;
&lt;li&gt;Package management - NPM, Bower&lt;/li&gt;
&lt;li&gt;DevOps - Git Repository w/ push Deployments, Grunt task&lt;/li&gt;
&lt;li&gt;Markdown WYSIWYG&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Showcase Content&lt;/strong&gt; - I wanted the content to take the stage and be easy to author and maintan.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="old-site"&gt;Old Site:&lt;/h3&gt;
&lt;p&gt;My old site served me well, but here are some issues that pushed the update.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Wordpress&lt;/strong&gt; - At the time this was &lt;em&gt;the&lt;/em&gt; platform for blogging. I never was a PHP developer, I developed my own theme and a few plugins, but it was never natural.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Dated Design&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;Table Layout (Yuck)&lt;/li&gt;
&lt;li&gt;Narrow and not responsive content and navigation&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Unfinished Concepts&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;Lifeblog - This was a neat concept and worked while I was active on social networks.&lt;/li&gt;
&lt;li&gt;Setup (Stuff I Use) - This turned out to be too much to maintain and as a result, never had anything interesting or relavant.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="homage"&gt;Homage&lt;/h3&gt;
&lt;p&gt;&lt;img alt="MrMatt57.org Ghost Screenshot" loading="lazy" src="https://mrmatt.io/img/MrMatt57org-Wordpress.jpg"&gt;&lt;/p&gt;</content:encoded><media:content url="https://mrmatt.io/img/MrMatt57org-Wordpress.jpg" medium="image"/></item><item><title>River Valley Vista</title><link>https://mrmatt.io/photography/2013-11-03-river-valley-vista/</link><pubDate>Sun, 03 Nov 2013 00:00:00 -0400</pubDate><guid>https://mrmatt.io/photography/2013-11-03-river-valley-vista/</guid><description>A sweeping view of a river cutting through a valley in early autumn, with mountains rising in the distance and a patchwork of forests in various stages of seasonal transition. The clear blue water reflects the sky as it winds through the landscape, creating a serene and expansive scene.</description><content:encoded/><media:content url="https://mrmatt.io/photography/2013-11-03-river-valley-vista/photo_hu_2b2863a660ee4c08.webp" medium="image"/></item><item><title>John Deere Snowblower</title><link>https://mrmatt.io/photography/2011-02-14-john-deere-snowblower/</link><pubDate>Mon, 14 Feb 2011 00:00:00 -0500</pubDate><guid>https://mrmatt.io/photography/2011-02-14-john-deere-snowblower/</guid><description>The trusty John Deere 318 with its snowblower attachment and cab, ready to tackle another winter driveway. Snow-covered pines make for a proper February scene.</description><content:encoded/><media:content url="https://mrmatt.io/photography/2011-02-14-john-deere-snowblower/photo_hu_f368e1a91b74958d.webp" medium="image"/></item><item><title>Fresh Hop Harvest</title><link>https://mrmatt.io/photography/2010-08-07-fresh-hop-harvest/</link><pubDate>Sat, 07 Aug 2010 00:00:00 -0400</pubDate><guid>https://mrmatt.io/photography/2010-08-07-fresh-hop-harvest/</guid><description>The spoils of a backyard hop harvest, tumbling out of the bucket like little green promises of future homebrew. Nothing quite beats the smell of fresh hops on a warm August day.</description><content:encoded/><media:content url="https://mrmatt.io/photography/2010-08-07-fresh-hop-harvest/photo_hu_adeb32fd3b79989e.webp" medium="image"/></item><item><title>Blue Hydrangea Bush</title><link>https://mrmatt.io/photography/2010-06-29-blue-hydrangea-bush/</link><pubDate>Tue, 29 Jun 2010 00:00:00 -0400</pubDate><guid>https://mrmatt.io/photography/2010-06-29-blue-hydrangea-bush/</guid><description>This hydrangea bush clearly read the assignment and went maximum blue. The kind of absurdly prolific bloomer that makes every neighboring plant look like an underachiever.</description><content:encoded/><media:content url="https://mrmatt.io/photography/2010-06-29-blue-hydrangea-bush/photo_hu_686fdabd03c483a7.webp" medium="image"/></item><item><title>Yellow Spotted Lily</title><link>https://mrmatt.io/photography/2010-06-25-yellow-spotted-lily/</link><pubDate>Fri, 25 Jun 2010 00:00:00 -0400</pubDate><guid>https://mrmatt.io/photography/2010-06-25-yellow-spotted-lily/</guid><description>These Asiatic lilies showed up to summer looking like they were hand-painted by someone who really loves polka dots. The kind of flower that makes you stop mowing the lawn and just stare for a minute.</description><content:encoded/><media:content url="https://mrmatt.io/photography/2010-06-25-yellow-spotted-lily/photo_hu_2e9b8d542abd370f.webp" medium="image"/></item><item><title>Hydrangea Awakening</title><link>https://mrmatt.io/photography/2010-05-31-hydrangea-awakening/</link><pubDate>Mon, 31 May 2010 00:00:00 -0400</pubDate><guid>https://mrmatt.io/photography/2010-05-31-hydrangea-awakening/</guid><description>Caught this hydrangea in the middle of its glow-up, still deciding whether it wanted to be blue or green. That transitional moment where the petals look like they're made of tissue paper and watercolor.</description><content:encoded/><media:content url="https://mrmatt.io/photography/2010-05-31-hydrangea-awakening/photo_hu_26fceff77d668e32.webp" medium="image"/></item><item><title>Canon A2000is Time Lapse using CHDK</title><link>https://mrmatt.io/posts/time-lapse-chdk/</link><pubDate>Fri, 21 May 2010 00:00:00 -0400</pubDate><guid>https://mrmatt.io/posts/time-lapse-chdk/</guid><description>How to create time-lapse videos with a Canon A2000IS using the CHDK hack — intervalometer script, AC power setup, and video assembly with QuickTime.</description><content:encoded>&lt;p&gt;I don&amp;rsquo;t know what it is about them, but time lapse videos captivate me. It might be the fact that you can live hours in minutes? Maybe in a strange way it is gratifying to watch life pass by without consequences? Whatever it is, they are a lot of fun to watch and now create!&lt;/p&gt;
&lt;div class="videoWrapper"&gt;
 &lt;iframe width="1280" height="720" src="https://www.youtube.com/embed/mMORdLGUVNI?rel=0&amp;amp;controls=0" frameborder="0" allowfullscreen title="Time Lapse - Owings Mills, MD"&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;div class="videoWrapper"&gt;
 &lt;iframe width="1280" height="720" src="https://www.youtube.com/embed/TEtyBWHY_H4?rel=0&amp;amp;controls=0" frameborder="0" allowfullscreen title="Time Lapse - Owings Mills VFC Carnival "&gt;&lt;/iframe&gt;
&lt;/div&gt;
When I found out I could use my [Canon A2000is](https://www.amazon.com/dp/B001EQ4BZE) point and shoot to create a time lapse, I was floored. I owe it all to the [Canon Hack Development Kit (CHDK)](https://chdk.wikia.com/wiki/CHDK). With this nifty piece of software, your Canon camera can boot a whole new feature set from an SD card. Most importantly, run [Ultra Intervalometer script](https://chdk.wikia.com/wiki/UBASIC/Scripts:_Ultra_Intervalometer).
&lt;p&gt;One gotcha with CHDK is that it will only boot from a FAT16 partition. So if you have a 4gig+ SD card, you will need to jump through some hoops to create a bootable FAT16 (32mb will do) and a larger FAT32 partition for storage. MacOS and Linux read the partitions no problem, if you are using Windows, &lt;a href="http://stereo.jpn.org/eng/sdm/quick.htm"&gt;SDM Installer&lt;/a&gt; will help you create and switch between partitions.&lt;/p&gt;
&lt;p&gt;The next thing you need is power, after all your wimpy lithium-ion or in my case NiMHs won&amp;rsquo;t last all day. This is where an AC Adapter comes in. Mine didn&amp;rsquo;t come with one, so I found a &lt;a href="https://www.amazon.com/s?k=canon+ac+adapter+HQRP"&gt;HQRP AC Replacement Adapter&lt;/a&gt; for my camera. Get the one with the transformer in the middle, it has a longer cord.&lt;/p&gt;
&lt;p&gt;Once you get all your images together, you need to &lt;a href="https://www.youtube.com/watch?v=YcYBMK0aweM"&gt;make the video&lt;/a&gt;. For this I used Quicktime 7 Pro. It was super easy and imported directly to iMovie for transitions, audio and text. When creating the video, I used the default 15fps photo sequence.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s hard to believe that my sub $200 &lt;a href="https://www.amazon.com/dp/B001EQ4BZE"&gt;Canon A2000IS&lt;/a&gt; camera is capable of such amazing things. Many more to follow, stay tuned.&lt;/p&gt;
&lt;p&gt;Just remember - time is relative.&lt;/p&gt;</content:encoded><media:content url="https://mrmatt.io/images/MattWalker-avatar.png" medium="image"/></item><item><title>Equipment Detail 318</title><link>https://mrmatt.io/photography/2010-05-02-equipment-detail-318/</link><pubDate>Sun, 02 May 2010 00:00:00 -0400</pubDate><guid>https://mrmatt.io/photography/2010-05-02-equipment-detail-318/</guid><description>A close-up of weathered green machinery bearing a high-visibility identification label. The equipment shows signs of regular use, with industrial details and a coiled measurement tool visible nearby.</description><content:encoded/><media:content url="https://mrmatt.io/photography/2010-05-02-equipment-detail-318/photo_hu_2ccf1fc9a1f5a620.webp" medium="image"/></item><item><title>Mountain Dew Stash</title><link>https://mrmatt.io/photography/2010-04-02-mountain-dew-stash/</link><pubDate>Fri, 02 Apr 2010 00:00:00 -0400</pubDate><guid>https://mrmatt.io/photography/2010-04-02-mountain-dew-stash/</guid><description>When the sale price is too good to pass up and you end up with a trunk full of Mountain Dew. No regrets.</description><content:encoded/><media:content url="https://mrmatt.io/photography/2010-04-02-mountain-dew-stash/photo_hu_2192198a25b67588.webp" medium="image"/></item><item><title>Cruising the Bahamas - Carnival Pride</title><link>https://mrmatt.io/posts/carnival-pride/</link><pubDate>Sat, 19 Dec 2009 00:00:00 -0500</pubDate><guid>https://mrmatt.io/posts/carnival-pride/</guid><description>Photos and video from our Carnival Pride cruise out of Baltimore — Universal Studios in Orlando, Atlantis in Nassau, and Paradise Cove in Freeport.</description><content:encoded>&lt;p&gt;&lt;img alt="Carnival Pride" loading="lazy" src="https://mrmatt.io/img/Carnival-Pride-Banner.jpg"&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="Carnival Pride Nassau Lighthouse" loading="lazy" src="https://mrmatt.io/img/Carnival-Pride-Nassau-Lighthouse.jpg"&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="Carnival Pride Depart" loading="lazy" src="https://mrmatt.io/img/Carnival-Pride-Depart.jpg"&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="Bahamas" loading="lazy" src="https://mrmatt.io/img/Atlantis-Bahamas.jpg"&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="Carnival Pride Freeport" loading="lazy" src="https://mrmatt.io/img/Carnival-Pride-Freeport.jpg"&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="Carnival Pride Steerage" loading="lazy" src="https://mrmatt.io/img/Carnival-Pride-Steerage.jpg"&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="Carnival Pride Trails" loading="lazy" src="https://mrmatt.io/img/Canrival-Pride-Trails.jpg"&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="Carnival Pride - 11/21/2009" loading="lazy" src="https://mrmatt.io/img/Carnival-Pride-Route.jpg"&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Carnival Pride - 11/21/2009&lt;/strong&gt;
After our first cruise on the Freedom of the Seas earlier this year, we were itching to go on another one. When my brother in-law and girlfriend wanted to go cruising, it wasn-t hard to convince us. We live 20 minutes from the Port of Baltimore, so we thought we would give it a try. It was awesome to not have to worry about flying an luggage.&lt;/p&gt;
&lt;p&gt;We visited Universal Studios in Orlando, the Atlantis Resort in Nassau and Paradise Cove in Freeport. It is hard to choose a highlight of the trip, it was all so much fun!&lt;/p&gt;
&lt;div class="videoWrapper"&gt;
&lt;iframe width="1280" height="720" src="https://www.youtube.com/embed/bHKuOSuwk7U?rel=0&amp;amp;controls=0" frameborder="0" allowfullscreen title="Carnival Pride - Cruise Nov 2009"&gt;&lt;/iframe&gt;
&lt;/div&gt;</content:encoded><media:content url="https://mrmatt.io/img/Carnival-Pride-Nassau-Lighthouse.jpg" medium="image"/></item><item><title>Cruise Ship Departure</title><link>https://mrmatt.io/photography/2009-11-27-cruise-ship-departure/</link><pubDate>Fri, 27 Nov 2009 00:00:00 -0500</pubDate><guid>https://mrmatt.io/photography/2009-11-27-cruise-ship-departure/</guid><description>Pulling away from port with the ship's wake carving a bright line through the deep blue water. Someone on the lower deck is leaning out to watch.</description><content:encoded/><media:content url="https://mrmatt.io/photography/2009-11-27-cruise-ship-departure/photo_hu_aed0336b2339a14a.webp" medium="image"/></item><item><title>Sun Deck at Port</title><link>https://mrmatt.io/photography/2009-11-27-sun-deck-at-port/</link><pubDate>Fri, 27 Nov 2009 00:00:00 -0500</pubDate><guid>https://mrmatt.io/photography/2009-11-27-sun-deck-at-port/</guid><description>The sun deck packed with passengers soaking up the last bit of warmth as the ship pulls past a busy cargo port. Everyone trying to squeeze in one more hour of vacation.</description><content:encoded/><media:content url="https://mrmatt.io/photography/2009-11-27-sun-deck-at-port/photo_hu_26607feaff4cf978.webp" medium="image"/></item><item><title>Atlantis Resort</title><link>https://mrmatt.io/photography/2009-11-26-atlantis-resort/</link><pubDate>Thu, 26 Nov 2009 00:00:00 -0500</pubDate><guid>https://mrmatt.io/photography/2009-11-26-atlantis-resort/</guid><description>The Atlantis resort on Paradise Island, all pink towers and palm trees against the tropical sky. Hard to miss from anywhere on the island.</description><content:encoded/><media:content url="https://mrmatt.io/photography/2009-11-26-atlantis-resort/photo_hu_e670d634fc2aa408.webp" medium="image"/></item><item><title>Tropical Palace Towers</title><link>https://mrmatt.io/photography/2009-11-26-tropical-palace-towers/</link><pubDate>Thu, 26 Nov 2009 00:00:00 -0500</pubDate><guid>https://mrmatt.io/photography/2009-11-26-tropical-palace-towers/</guid><description>Somewhere between a theme park and an actual palace, these towers with their golden lotus domes are giving off serious fantasy-kingdom energy. The palm trees really sell the whole tropical fortress vibe.</description><content:encoded/><media:content url="https://mrmatt.io/photography/2009-11-26-tropical-palace-towers/photo_hu_c9726cd5b0e4bdf7.webp" medium="image"/></item><item><title>Turquoise Shoreline</title><link>https://mrmatt.io/photography/2009-11-26-turquoise-shoreline/</link><pubDate>Thu, 26 Nov 2009 00:00:00 -0500</pubDate><guid>https://mrmatt.io/photography/2009-11-26-turquoise-shoreline/</guid><description>That impossibly clear Caribbean water where you can see every shade of blue and green shift with the sand beneath it. A perfect beach day in the Bahamas.</description><content:encoded/><media:content url="https://mrmatt.io/photography/2009-11-26-turquoise-shoreline/photo_hu_119c7a286f5ab306.webp" medium="image"/></item><item><title>Nassau Lighthouse</title><link>https://mrmatt.io/photography/2009-11-25-nassau-lighthouse/</link><pubDate>Wed, 25 Nov 2009 00:00:00 -0500</pubDate><guid>https://mrmatt.io/photography/2009-11-25-nassau-lighthouse/</guid><description>The lighthouse at the tip of the Nassau harbor entrance, waves crashing against the rocky point. Taken from high up on the cruise ship as we came into port.</description><content:encoded/><media:content url="https://mrmatt.io/photography/2009-11-25-nassau-lighthouse/photo_hu_ec7f67c56762498f.webp" medium="image"/></item><item><title>Canoe Camping - Penobscot River, Maine</title><link>https://mrmatt.io/posts/canoe-camping-penobscot-river-maine/</link><pubDate>Sun, 30 Aug 2009 00:00:00 -0400</pubDate><guid>https://mrmatt.io/posts/canoe-camping-penobscot-river-maine/</guid><description>Multi-day canoe camping trip on the upper west branch of the Penobscot River and Chesuncook Lake in Maine — route, camps, fishing, and video.</description><content:encoded>&lt;p&gt;&lt;strong&gt;Upper West Branch Penobscot River and Chesuncook Lake&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;August 2nd - 5th 2009&lt;/p&gt;
&lt;p&gt;Ever since the epic Maine 2003 trip, I wanted to go back. There is just something about the great north woods of Maine that makes me feel at home.&lt;/p&gt;
&lt;p&gt;We enjoy canoeing and camping and thought why not combine the two. Typically we have backpacked on our multi-day camping trips. Something about being able to not worry about weight as much and spending more time enjoy the camp life was very appealing.&lt;/p&gt;
&lt;p&gt;We targeted the upper west branch of the Penobscot for its remote setting and docile waters. The fine folks at &lt;a href="https://www.katahdinoutfitters.com"&gt;Katahdin Outfitters&lt;/a&gt; helped us plan the perfect trip. We left our car at the ranger station on Chesuncook lake and put-in at Lobster Stream on the river.&lt;/p&gt;
&lt;p&gt;Here is a map of our course and our camps:&lt;/p&gt;
&lt;div class="videoWrapper"&gt;&lt;iframe frameborder="0" height="300" marginheight="0" marginwidth="0" scrolling="no" src="https://maps.google.com/maps/ms?ie=UTF8&amp;hl=en&amp;msa=0&amp;msid=108788543476703129185.0004711c18ab9cbab24a4&amp;ll=45.966425,-69.411621&amp;spn=0.286364,0.700378&amp;t=p&amp;z=10&amp;output=embed" width="720" title="Map"&gt;&lt;/iframe&gt;&lt;/div&gt;
&lt;small&gt;View [Maine 2009](https://maps.google.com/maps/ms?ie=UTF8&amp;hl=en&amp;msa=0&amp;msid=108788543476703129185.0004711c18ab9cbab24a4&amp;ll=45.966425,-69.411621&amp;spn=0.286364,0.700378&amp;t=p&amp;z=10&amp;source=embed) in a larger map&lt;/small&gt;
Fishing was another draw for me. I had a great smallmouth bass day trip on the east branch back in 2003. The west branch is more known for its landlocked salmon and brook trout. With the water being so high and being late in season, the fishing was less than stellar. I caught several creek chubs, perch and one small brook trout but not a single bass. I have to admit, I didn't fish that hard, I was too busy enjoying the wilderness.
&lt;div class="videoWrapper"&gt;
 &lt;iframe width="1280" height="720" src="https://www.youtube.com/embed/xboSdBhuvLo?rel=0&amp;amp;controls=0" frameborder="0" allowfullscreen title="Canoe Camping the Upper WestBranch Penobscot River and Chesuncook Lake, Maine 2009 "&gt;&lt;/iframe&gt;
&lt;/div&gt;
As you can see the camps were well established and for the most part very clean. Our favorite was the first on Big Island North. What a treat to be able spend the night in such a beautiful place. Jaimie was so relieved to see the well ventilated housed latrine. It was -a busy week- and we saw at most a group or two of people each day. The camps are quite far apart so settling in early is advised.
&lt;p&gt;We had a wonderful trip and would highly recommend it!&lt;/p&gt;
&lt;p&gt;Resources:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://maps.google.com/maps/ms?ie=UTF8&amp;amp;hl=en&amp;amp;msa=0&amp;amp;msid=110070353070567409014.000464c4cf0087ffe18e9&amp;amp;z=10"&gt;Google Maps - West Branch Penobscot River&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.katahdinoutfitters.com/penobscot.htm"&gt;Katahdin Outfitters - Penobscot River&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded><media:content url="https://mrmatt.io/images/MattWalker-avatar.png" medium="image"/></item><item><title>Coastal Boulders</title><link>https://mrmatt.io/photography/2009-08-07-coastal-boulders/</link><pubDate>Fri, 07 Aug 2009 00:00:00 -0400</pubDate><guid>https://mrmatt.io/photography/2009-08-07-coastal-boulders/</guid><description>A perfectly rounded boulder wedged between jagged coastal granite. The contrast between the smooth and rough surfaces caught my eye along the shore.</description><content:encoded/><media:content url="https://mrmatt.io/photography/2009-08-07-coastal-boulders/photo_hu_a7c91879fad50c72.webp" medium="image"/></item><item><title>Rockweed at Low Tide</title><link>https://mrmatt.io/photography/2009-08-07-rockweed-at-low-tide/</link><pubDate>Fri, 07 Aug 2009 00:00:00 -0400</pubDate><guid>https://mrmatt.io/photography/2009-08-07-rockweed-at-low-tide/</guid><description>Low tide reveals a thick blanket of rockweed draped over the shore. The layered textures and muted colors have a surprising amount of depth up close.</description><content:encoded/><media:content url="https://mrmatt.io/photography/2009-08-07-rockweed-at-low-tide/photo_hu_b8009247748a9750.webp" medium="image"/></item><item><title>Herring Gull Portrait</title><link>https://mrmatt.io/photography/2009-08-06-herring-gull-portrait/</link><pubDate>Thu, 06 Aug 2009 00:00:00 -0400</pubDate><guid>https://mrmatt.io/photography/2009-08-06-herring-gull-portrait/</guid><description>This gull was completely unbothered, posing on the rocks like it owned the place. Sharp detail on the feathers against that perfect Maine sky.</description><content:encoded/><media:content url="https://mrmatt.io/photography/2009-08-06-herring-gull-portrait/photo_hu_de75e9e0fd2eb5b.webp" medium="image"/></item><item><title>Mountain Ridgeline</title><link>https://mrmatt.io/photography/2009-08-05-mountain-ridgeline/</link><pubDate>Wed, 05 Aug 2009 00:00:00 -0400</pubDate><guid>https://mrmatt.io/photography/2009-08-05-mountain-ridgeline/</guid><description>Layers of green ridges disappearing into the haze on an overcast summer day. The kind of view you get from a pulloff along a Maine mountain road.</description><content:encoded/><media:content url="https://mrmatt.io/photography/2009-08-05-mountain-ridgeline/photo_hu_4bf489bf18b1b67c.webp" medium="image"/></item><item><title>Katahdin Lake View</title><link>https://mrmatt.io/photography/2009-08-04-katahdin-lake-view/</link><pubDate>Tue, 04 Aug 2009 00:00:00 -0400</pubDate><guid>https://mrmatt.io/photography/2009-08-04-katahdin-lake-view/</guid><description>Mount Katahdin doing its best impression of a desktop wallpaper, viewed from across the lake on a perfectly hazy Maine afternoon. The kind of scene that makes you understand why Thoreau wouldn't shut up about this place.</description><content:encoded/><media:content url="https://mrmatt.io/photography/2009-08-04-katahdin-lake-view/photo_hu_d593ebfbf3761543.webp" medium="image"/></item><item><title>Lakeside Campsite</title><link>https://mrmatt.io/photography/2009-08-04-lakeside-campsite/</link><pubDate>Tue, 04 Aug 2009 00:00:00 -0400</pubDate><guid>https://mrmatt.io/photography/2009-08-04-lakeside-campsite/</guid><description>Your own private slice of Maine lakefront, accessible only by canoe and completely worth the paddle. The tiny cabin peeking through the trees confirms someone had the right idea about where to spend their summers.</description><content:encoded/><media:content url="https://mrmatt.io/photography/2009-08-04-lakeside-campsite/photo_hu_527be1721912170d.webp" medium="image"/></item><item><title>Thistle In Bloom</title><link>https://mrmatt.io/photography/2009-08-04-thistle-in-bloom/</link><pubDate>Tue, 04 Aug 2009 00:00:00 -0400</pubDate><guid>https://mrmatt.io/photography/2009-08-04-thistle-in-bloom/</guid><description>Nature's punk rocker -- all spiky attitude and electric purple hair. This thistle was absolutely thriving in the Maine summer, looking like it had zero interest in being called a weed.</description><content:encoded/><media:content url="https://mrmatt.io/photography/2009-08-04-thistle-in-bloom/photo_hu_9c0e72d9eebbf0fc.webp" medium="image"/></item><item><title>Maine Lake Horizon</title><link>https://mrmatt.io/photography/2009-08-03-maine-lake-horizon/</link><pubDate>Mon, 03 Aug 2009 00:00:00 -0400</pubDate><guid>https://mrmatt.io/photography/2009-08-03-maine-lake-horizon/</guid><description>Peak summer in Maine, where the lake is so still it looks like glass and the only sounds are loons and the occasional paddle stroke. The kind of view that makes you seriously reconsider your life choices about living in the suburbs.</description><content:encoded/><media:content url="https://mrmatt.io/photography/2009-08-03-maine-lake-horizon/photo_hu_fca243b7428911fe.webp" medium="image"/></item><item><title>Lichen on Bark</title><link>https://mrmatt.io/photography/2009-08-02-lichen-on-bark/</link><pubDate>Sun, 02 Aug 2009 00:00:00 -0400</pubDate><guid>https://mrmatt.io/photography/2009-08-02-lichen-on-bark/</guid><description>Delicate strands of old man's beard lichen clinging to tree bark in the Maine woods. A tiny ecosystem right at eye level.</description><content:encoded/><media:content url="https://mrmatt.io/photography/2009-08-02-lichen-on-bark/photo_hu_34e8748d975b621e.webp" medium="image"/></item><item><title>Mossy Forest Floor</title><link>https://mrmatt.io/photography/2009-08-02-mossy-forest-floor/</link><pubDate>Sun, 02 Aug 2009 00:00:00 -0400</pubDate><guid>https://mrmatt.io/photography/2009-08-02-mossy-forest-floor/</guid><description>The forest floor in Maine doing its best impression of a fairy tale setting -- thick moss, tiny fir saplings reaching for the light, and the kind of quiet that makes you forget the rest of the world exists.</description><content:encoded/><media:content url="https://mrmatt.io/photography/2009-08-02-mossy-forest-floor/photo_hu_884514e353441968.webp" medium="image"/></item><item><title>Penobscot River Reflection</title><link>https://mrmatt.io/photography/2009-08-02-penobscot-river-reflection/</link><pubDate>Sun, 02 Aug 2009 00:00:00 -0400</pubDate><guid>https://mrmatt.io/photography/2009-08-02-penobscot-river-reflection/</guid><description>The towering spruces and firs of Maine's boreal forest create a mirror image in the glassy surface of the Penobscot River. The stillness of the water captures every detail of the tree line, creating a moment of perfect symmetry between land and reflection.</description><content:encoded/><media:content url="https://mrmatt.io/photography/2009-08-02-penobscot-river-reflection/photo_hu_7ad5651c030cc018.webp" medium="image"/></item><item><title>The Ultimate USB *Key* Drive - Lacie iamaKey 8GB</title><link>https://mrmatt.io/posts/the-ultimate-usb-key-drive-lacie-iamakey-8gb/</link><pubDate>Mon, 01 Jun 2009 00:00:00 -0400</pubDate><guid>https://mrmatt.io/posts/the-ultimate-usb-key-drive-lacie-iamakey-8gb/</guid><description>Review of the LaCie iamaKey 8GB — a keychain-sized USB flash drive that goes everywhere your keys go. Durable, convenient, and always on hand.</description><content:encoded>&lt;p&gt;I have always been a fan of portable storage. However, most -portable- devices just aren&amp;rsquo;t well- portable. You think you will carry them around, but end up leaving them behind. When I first saw the LaCie iamaKey, I knew it would be different. I tried the ultra secure route with an &lt;a href="https://mrmatt.io/posts/james-bond-style-usb-key-ironkey/"&gt;IronKey&lt;/a&gt;, but it is too bulky and so secure- I ended up locking myself out (don&amp;rsquo;t ask).&lt;/p&gt;
&lt;p&gt;&lt;img alt="Lacie Iamakey USB ThumbDrive" loading="lazy" src="https://mrmatt.io/img/iamaKey8GB/Lacie-USB-Key-Macro1.jpg"&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="Lacie Iamakey USB ThumbDrive" loading="lazy" src="https://mrmatt.io/img/iamaKey8GB/Lacie-USB-Key-Macro2.jpg"&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="Lacie Iamakey USB ThumbDrive" loading="lazy" src="https://mrmatt.io/img/iamaKey8GB/Lacie-USB-Key-Macro3.jpg"&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="Lacie Iamakey USB ThumbDrive" loading="lazy" src="https://mrmatt.io/img/iamaKey8GB/Lacie-USB-Key-Macro4.jpg"&gt;
Pros:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Tags along with something I already carry and keep track of, my keys - never leave home without them.&lt;/li&gt;
&lt;li&gt;Plenty of space to include a live CD and all of the diagnostic utilities you could ever need.&lt;/li&gt;
&lt;li&gt;Reasonably Priced for such a unique flash drive.&lt;/li&gt;
&lt;li&gt;So far the construction and materials seems very durable.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Cons:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Not the fastest flash drive at 26 mb/s Read and 16 mb/s Write&lt;/li&gt;
&lt;li&gt;Doesn&amp;rsquo;t have hardware level encryption (wishful thinking), but nothing TrueCrypt can&amp;rsquo;t take care of.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Conclusion:&lt;/p&gt;
&lt;p&gt;I would highly recommend this to anyone that wants to have a flash drive with them all the time. I have been carrying the drive for about two weeks now and it&amp;rsquo;s been great. I have actually found that I am using flash storage more because it is so convenient. Stay tuned for what I loaded it up with.&lt;/p&gt;</content:encoded><media:content url="https://mrmatt.io/img/iamaKey8GB/Lacie-USB-Key-Macro1.jpg" medium="image"/></item><item><title>Hike @ Loch Raven Reservoir w/ iPhone 3G GPS</title><link>https://mrmatt.io/posts/hike-loch-raven-reservoir-w-iphone-3g-gps/</link><pubDate>Sat, 25 Apr 2009 00:00:00 -0400</pubDate><guid>https://mrmatt.io/posts/hike-loch-raven-reservoir-w-iphone-3g-gps/</guid><description>Hiking the Merryman Trail at Loch Raven Reservoir in Maryland, tracked with an iPhone 3G GPS using the Trails app — photos and route details.</description><content:encoded>&lt;p&gt;&lt;a href="https://www.everytrail.com/view_trip.php?trip_id=186126"&gt;&lt;img alt="Loch Raven Reservoir Hike In" loading="lazy" src="https://mrmatt.io/img/Loch-Raven-Reservoir/hike-in.jpg"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;This was the first time I used the GPS on my iPhone for tracking and marking waypoints. I stuffed it in a pocket on the top of my backpack and I must say it worked quite well. I used the iPhone app &lt;a href="http://phobos.apple.com/WebObjects/MZStore.woa/wa/viewSoftware?id=289190494&amp;amp;mt=8"&gt;Trails&lt;/a&gt; to record the hike. The battery life was great, it only used about a third of the battery for the 3 hours it was tracking. One nice feature was the use of the light sensor (near the ear piece). When it was covered, the screen automatically shut off to help save the battery. The accuracy seemed quite good as well, there are even posts saying the phone/app &lt;a href="http://gpsobsessed.com/200-garmin-edge-305-more-acccurate-than-199-trails-iphone-app/"&gt;bests a $200 Garmin&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Here are some pictures from our hike on the Merryman Trail at the Loch Raven Reservoir in Timonium, Maryland. We parked on Warren Rd. and had easy access to the trail. We got side tracked at the feeder stream waterfalls and took the fire road up to a neighborhood and decided to turn back (as you can see from the maps). Overall it was a nice hike and beautiful spring day. You can view more details on our &lt;a href="https://www.everytrail.com/view_trip.php?trip_id=186126"&gt;Hike In&lt;/a&gt; and &lt;a href="https://www.everytrail.com/view_trip.php?trip_id=186127"&gt;Hike Out&lt;/a&gt; @ EveryTrail.com.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://mrmatt.io/img/Loch-Raven-Reservoir/trail.jpg"&gt;&lt;img alt="Trail" loading="lazy" src="https://mrmatt.io/img/Loch-Raven-Reservoir/trail_thumb.jpg" title="Trail"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://mrmatt.io/img/Loch-Raven-Reservoir/light.jpg"&gt;&lt;img alt="Light" loading="lazy" src="https://mrmatt.io/img/Loch-Raven-Reservoir/light_thumb.jpg" title="Light"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://mrmatt.io/img/Loch-Raven-Reservoir/waterfall.jpg"&gt;&lt;img alt="Waterfall" loading="lazy" src="https://mrmatt.io/img/Loch-Raven-Reservoir/waterfall_thumb2.jpg" title="Waterfall"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://mrmatt.io/img/Loch-Raven-Reservoir/moss.jpg"&gt;&lt;img alt="Moss" loading="lazy" src="https://mrmatt.io/img/Loch-Raven-Reservoir/moss_thumb.jpg" title="Moss"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://mrmatt.io/img/Loch-Raven-Reservoir/buds.jpg"&gt;&lt;img alt="Buds" loading="lazy" src="https://mrmatt.io/img/Loch-Raven-Reservoir/buds_thumb.jpg" title="Buds"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://mrmatt.io/img/Loch-Raven-Reservoir/cabbage_thumb.jpg"&gt;&lt;img alt="Cabbage" loading="lazy" src="https://mrmatt.io/img/Loch-Raven-Reservoir/cabbage.jpg" title="Cabbage"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://mrmatt.io/img/Loch-Raven-Reservoir/tree.jpg"&gt;&lt;img alt="Tree" loading="lazy" src="https://mrmatt.io/img/Loch-Raven-Reservoir/tree_thumb.jpg" title="Tree"&gt;&lt;/a&gt;
&lt;a href="https://mrmatt.io/img/Loch-Raven-Reservoir/marker.jpg"&gt;&lt;img alt="Marker" loading="lazy" src="https://mrmatt.io/img/Loch-Raven-Reservoir/marker_thumb.jpg" title="Marker"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;More information about the trail can be found &lt;a href="http://www.trails.com/tcatalog_trail.aspx?trailid=XMR012-025"&gt;here&lt;/a&gt;.&lt;/p&gt;</content:encoded><media:content url="https://mrmatt.io/img/Loch-Raven-Reservoir/waterfall.jpg" medium="image"/></item><item><title>Forest Floor Moss</title><link>https://mrmatt.io/photography/2009-04-05-forest-floor-moss/</link><pubDate>Sun, 05 Apr 2009 00:00:00 -0400</pubDate><guid>https://mrmatt.io/photography/2009-04-05-forest-floor-moss/</guid><description>Getting down low on the forest floor to capture the miniature world of moss and spore stalks in the spring sunlight.</description><content:encoded/><media:content url="https://mrmatt.io/photography/2009-04-05-forest-floor-moss/photo_hu_9145262af062108b.webp" medium="image"/></item><item><title>Moss on Stone</title><link>https://mrmatt.io/photography/2009-04-05-moss-on-stone/</link><pubDate>Sun, 05 Apr 2009 00:00:00 -0400</pubDate><guid>https://mrmatt.io/photography/2009-04-05-moss-on-stone/</guid><description>A single patch of moss catches a beam of light between the rocks. The contrast between the vivid green and the dark stone made it impossible to walk past.</description><content:encoded/><media:content url="https://mrmatt.io/photography/2009-04-05-moss-on-stone/photo_hu_7a1a271ac5aa1bf.webp" medium="image"/></item><item><title>Trail Marker</title><link>https://mrmatt.io/photography/2009-04-05-trail-marker/</link><pubDate>Sun, 05 Apr 2009 00:00:00 -0400</pubDate><guid>https://mrmatt.io/photography/2009-04-05-trail-marker/</guid><description>A bright trail marker pinned to a tree along a hiking path. Simple wayfinding that keeps you on track through the woods.</description><content:encoded/><media:content url="https://mrmatt.io/photography/2009-04-05-trail-marker/photo_hu_d6cfa848b78aa348.webp" medium="image"/></item><item><title>Woodland Waterfall</title><link>https://mrmatt.io/photography/2009-04-05-woodland-waterfall/</link><pubDate>Sun, 05 Apr 2009 00:00:00 -0400</pubDate><guid>https://mrmatt.io/photography/2009-04-05-woodland-waterfall/</guid><description>A gentle woodland waterfall with that classic long-exposure silk effect on the water. The moss-covered rocks and fallen leaves add to the quiet atmosphere.</description><content:encoded/><media:content url="https://mrmatt.io/photography/2009-04-05-woodland-waterfall/photo_hu_21ef6c65cb2d1eae.webp" medium="image"/></item><item><title>Underwater Snorkeling</title><link>https://mrmatt.io/photography/2009-03-30-underwater-snorkeling/</link><pubDate>Mon, 30 Mar 2009 00:00:00 -0400</pubDate><guid>https://mrmatt.io/photography/2009-03-30-underwater-snorkeling/</guid><description>Full gear and a sea scooter for an underwater adventure. The visibility was incredible — that Caribbean blue is hard to forget.</description><content:encoded/><media:content url="https://mrmatt.io/photography/2009-03-30-underwater-snorkeling/photo_hu_f4892648db1686d4.webp" medium="image"/></item><item><title>Cruising the Caribbean - Freedom of the Seas</title><link>https://mrmatt.io/posts/freedom-of-the-seas/</link><pubDate>Thu, 26 Mar 2009 00:00:00 -0400</pubDate><guid>https://mrmatt.io/posts/freedom-of-the-seas/</guid><description>Video highlights from our Caribbean cruise aboard Royal Caribbean's Freedom of the Seas — sun, ports, and ocean views from a week at sea in 2009.</description><content:encoded>&lt;div class="videoWrapper"&gt;
 &lt;iframe width="1280" height="720" src="https://www.youtube.com/embed/QEWHl4QzS98?rel=0&amp;amp;controls=0" frameborder="0" allowfullscreen title="Freedom of the Seas 2009"&gt;&lt;/iframe&gt;
&lt;/div&gt;</content:encoded><media:content url="https://mrmatt.io/images/MattWalker-avatar.png" medium="image"/></item><item><title>Mind Control - Tea in 30 Seconds</title><link>https://mrmatt.io/posts/mind-control/</link><pubDate>Fri, 13 Feb 2009 00:00:00 -0500</pubDate><guid>https://mrmatt.io/posts/mind-control/</guid><description>A fun time-lapse video of making a cup of tea condensed into 30 seconds — from boiling the kettle to the first sip, at high speed.</description><content:encoded>&lt;div class="videoWrapper"&gt;
 &lt;iframe width="1280" height="720" src="https://www.youtube.com/embed/umUc0RXX0GA?rel=0&amp;amp;controls=0" frameborder="0" allowfullscreen title="Mind Control - Tea in 30 Seconds"&gt;&lt;/iframe&gt;
&lt;/div&gt;</content:encoded><media:content url="https://mrmatt.io/images/MattWalker-avatar.png" medium="image"/></item><item><title>Honda Slow Window Syndrome (HSWS)</title><link>https://mrmatt.io/posts/honda-slow-window-syndrome/</link><pubDate>Sat, 31 Jan 2009 00:00:00 -0500</pubDate><guid>https://mrmatt.io/posts/honda-slow-window-syndrome/</guid><description>How to fix slow power windows on older Hondas — replacing the regulator and motor, then lubricating the window tracks with white lithium grease.</description><content:encoded>&lt;p&gt;If you are an early generation Honda automobile owner and your power window needs -help- rolling up, you may be a victim of HSWS (honda slow window syndrome). Don&amp;rsquo;t worry you aren&amp;rsquo;t alone, many people around the world face the same annoying and sometimes embarrassing problem.&lt;/p&gt;
&lt;p&gt;Almost all early generation Hondas have one problem in common- slow power windows. Rolling them down isn&amp;rsquo;t usually the problem, it&amp;rsquo;s closing them up. They screech to a halt (errrr) and often need physical help to seal them up. If you are like me, you probably think that the motor is just weak and needs replacing. If you have waited until your window stops- this probably is the case. However, if they still move- there may be a fix.&lt;/p&gt;
&lt;p&gt;Recently my wife&amp;rsquo;s &amp;lsquo;99 Honda Civic&amp;rsquo;s driver side window just about called it kaput. After a $200+ dealer quote, I decided to take matters in my own hands. It doesn&amp;rsquo;t pay to just replace the motor. The module comes with the regulator, motor and wiring harness. I found a good &lt;a href="http://search.am-autoparts.com/search?af=category:windowmotorsandregulators%20%20vehicle:honda"&gt;aftermarket source&lt;/a&gt; selling them for under $60 shipped. The installation was pretty easy. The only problem I ran into was that the replacement motor mount was looking for nuts instead of bolts. I just hacked off the plastic and threads from the OEM motor mount and used them as nuts. Once I had everything put back together I was excited to give the new motor a try. Bummer - again, rolling it down was not the problem - but rolling it up was still slow. The only culprit left is friction. Duh!&lt;/p&gt;
&lt;p&gt;I took some spray white lithium grease and covered the window tracks and slowly but surely things started to speed up. After about 4 cycles, things were back to what I would consider normal. The new regulator and motor definitely helped, but ultimately the track needed lubrication. It seems like a routine grease application to the window tracks may prevent HSWS. So if your windows are getting slow, start treatment today.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Stephen&amp;rsquo;s desk has both white lithium grease and lotion on it. Interesting." loading="lazy" src="https://mrmatt.io/img/white.lithium.grease.jpg"&gt;
Photo by &lt;a href="https://flickr.com/photos/commondream/2778095180//"&gt;-commondream-&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Banner Photo by &lt;a href="https://flickr.com/photos/ph0rk/2986357207/"&gt;-ph0rk-&lt;/a&gt;&lt;/p&gt;</content:encoded><media:content url="https://mrmatt.io/images/MattWalker-avatar.png" medium="image"/></item><item><title>Amazon Cloudfront - Shopping for a CDN?</title><link>https://mrmatt.io/posts/amazon-cloudfront-c2bb-shopping-for-a-cdn/</link><pubDate>Wed, 03 Dec 2008 00:00:00 -0500</pubDate><guid>https://mrmatt.io/posts/amazon-cloudfront-c2bb-shopping-for-a-cdn/</guid><description>Comparing Amazon CloudFront against Akamai and LimeLight with latency benchmarks from 35 global locations — setup guide and performance data.</description><content:encoded>&lt;p&gt;&lt;img alt="Cloudfront Amazon Data Centers" loading="lazy" src="https://mrmatt.io/img/amazoncloudfront.jpg"&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Location of Amazon Data Centers World-wide. Credit &lt;a href="https://www.allthingsdistributed.com/2008/11/amazon_cloudfront.html"&gt;Werner Vogels&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;This past month Amazon sent me an early Christmas present, their very own content delivery network (CDN). Adding to their already robust line of -cloud- offerings, Amazon Cloudfront brings edge server routing to the mix. I have been using Amazon S3 for static content delivery since my most recent &lt;a href="https://mrmatt.io/posts/hello-world-new-server-theme-content-collaboration/"&gt;hello world&lt;/a&gt;. Cloudfront takes the highly scalable and redundant S3 and puts it closer to the end user, thus distributing throughput and reducing latency.&lt;/p&gt;
&lt;p&gt;When it comes to web development, one of my driving forces is performance. I love seeking out and shaving milliseconds off page loads and network requests. One of the best ways to do this is to put the content as close to the source as possible. Content Delivery Networks do just that. If my viewers are in Asia, I don&amp;rsquo;t want my server in New York and vice versa. With a CDN data is cached at several geographically optimized locations as needed. When a request comes in, it is routed to the nearest location.&lt;/p&gt;
&lt;h3 id="the-quickest-route-to-the-jungle"&gt;The quickest route to the Jungle&lt;/h3&gt;
&lt;p&gt;A good tool to analyze network routing, latency and a bunch of other stuff is &lt;a href="http://www.pingplotter.com/"&gt;pingplotter&lt;/a&gt;. They have a free version and pro version. The free version works great for my purposes. Internet Control Message Protocol (ICMP) Pings are a lightweight and predictable way to provide insight on latency and network conditions. Here is a comparison of my routes between Amazon Cloudfront and my standard web server.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://mrmatt.io/img/pingplotter.jpg"&gt;&lt;img alt="Amazon Cloudfront Pingplotter" loading="lazy" src="https://mrmatt.io/img/pingplotter_thumb.jpg" title="Amazon Cloudfront Pingplotter"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;As you can see, with Cloudfront I scream right to Amazon&amp;rsquo;s Newark, NJ server as opposed to going through McLean, VA, then ATL, and finally to my server in Dallas, TX.&lt;/p&gt;
&lt;h3 id="how-cloudfront-performs-in-the-cdn-storm"&gt;How Cloudfront performs in the CDN storm&lt;/h3&gt;
&lt;p&gt;Pingplotter works great to determine &lt;em&gt;your&lt;/em&gt; route and latency.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://just-ping.com/"&gt;Just-ping.com&lt;/a&gt; provides a great way to test your host/cdn from many geographic locations at once.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Disclaimer: The below analysis was put together to illustrate a general concept. Accuracy and real world conditions will vary.&lt;/em&gt;&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Geographic Location&lt;/th&gt;
 &lt;th&gt;Single Web Server&lt;/th&gt;
 &lt;th&gt;Amazon Cloudfront&lt;/th&gt;
 &lt;th&gt;Akamai&lt;/th&gt;
 &lt;th&gt;LimeLight&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;Florida, U.S.A.&lt;/td&gt;
 &lt;td&gt;34.4&lt;/td&gt;
 &lt;td&gt;&lt;strong&gt;29.4&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;36.5&lt;/td&gt;
 &lt;td&gt;36.9&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Chicago, U.S.A.&lt;/td&gt;
 &lt;td&gt;34.5&lt;/td&gt;
 &lt;td&gt;6.6&lt;/td&gt;
 &lt;td&gt;19&lt;/td&gt;
 &lt;td&gt;&lt;strong&gt;1.3&lt;/strong&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;San Francisco, U.S.A.&lt;/td&gt;
 &lt;td&gt;43.6&lt;/td&gt;
 &lt;td&gt;2.2&lt;/td&gt;
 &lt;td&gt;3.6&lt;/td&gt;
 &lt;td&gt;&lt;strong&gt;2&lt;/strong&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;New York, U.S.A.&lt;/td&gt;
 &lt;td&gt;43.9&lt;/td&gt;
 &lt;td&gt;&lt;strong&gt;5&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;8.9&lt;/td&gt;
 &lt;td&gt;6.6&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Santa Clara, U.S.A.&lt;/td&gt;
 &lt;td&gt;47.1&lt;/td&gt;
 &lt;td&gt;3.9&lt;/td&gt;
 &lt;td&gt;&lt;strong&gt;2.8&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;6.9&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Vancouver, Canada&lt;/td&gt;
 &lt;td&gt;73&lt;/td&gt;
 &lt;td&gt;52&lt;/td&gt;
 &lt;td&gt;&lt;strong&gt;4.5&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;95.6&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Austin1, U.S.A.&lt;/td&gt;
 &lt;td&gt;102.4&lt;/td&gt;
 &lt;td&gt;38.1&lt;/td&gt;
 &lt;td&gt;&lt;strong&gt;5.2&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;76.3&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Austin, U.S.A.&lt;/td&gt;
 &lt;td&gt;102.7&lt;/td&gt;
 &lt;td&gt;38.1&lt;/td&gt;
 &lt;td&gt;&lt;strong&gt;5.3&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;76.4&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;London, United Kingdom&lt;/td&gt;
 &lt;td&gt;108&lt;/td&gt;
 &lt;td&gt;13.2&lt;/td&gt;
 &lt;td&gt;1.8&lt;/td&gt;
 &lt;td&gt;&lt;strong&gt;1.6&lt;/strong&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Amsterdam3, Netherlands&lt;/td&gt;
 &lt;td&gt;114.3&lt;/td&gt;
 &lt;td&gt;19.9&lt;/td&gt;
 &lt;td&gt;&lt;strong&gt;0.7&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;7.4&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Amsterdam2, Netherlands&lt;/td&gt;
 &lt;td&gt;115.8&lt;/td&gt;
 &lt;td&gt;20.6&lt;/td&gt;
 &lt;td&gt;&lt;strong&gt;1.5&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;8.1&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Amsterdam, Netherlands&lt;/td&gt;
 &lt;td&gt;118.6&lt;/td&gt;
 &lt;td&gt;0.9&lt;/td&gt;
 &lt;td&gt;&lt;strong&gt;0.5&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;0.7&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Lille, France&lt;/td&gt;
 &lt;td&gt;120.3&lt;/td&gt;
 &lt;td&gt;&lt;strong&gt;12.9&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;14.2&lt;/td&gt;
 &lt;td&gt;110.9&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Munchen, Germany&lt;/td&gt;
 &lt;td&gt;129.1&lt;/td&gt;
 &lt;td&gt;&lt;strong&gt;7.6&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;10.2&lt;/td&gt;
 &lt;td&gt;7.7&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Zurich, Switzerland&lt;/td&gt;
 &lt;td&gt;130.8&lt;/td&gt;
 &lt;td&gt;10.6&lt;/td&gt;
 &lt;td&gt;&lt;strong&gt;2.8&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;25.7&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Cologne, Germany&lt;/td&gt;
 &lt;td&gt;131.8&lt;/td&gt;
 &lt;td&gt;9&lt;/td&gt;
 &lt;td&gt;&lt;strong&gt;5&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;20.4&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Groningen, Netherlands&lt;/td&gt;
 &lt;td&gt;133&lt;/td&gt;
 &lt;td&gt;&lt;strong&gt;4.3&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;5.7&lt;/td&gt;
 &lt;td&gt;&lt;strong&gt;4.3&lt;/strong&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Copenhagen, Denmark&lt;/td&gt;
 &lt;td&gt;137.9&lt;/td&gt;
 &lt;td&gt;15.6&lt;/td&gt;
 &lt;td&gt;&lt;strong&gt;4.1&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;25.3&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Antwerp, Belgium&lt;/td&gt;
 &lt;td&gt;139.7&lt;/td&gt;
 &lt;td&gt;5.5&lt;/td&gt;
 &lt;td&gt;&lt;strong&gt;4.2&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;4.4&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Stockholm, Sweden&lt;/td&gt;
 &lt;td&gt;142&lt;/td&gt;
 &lt;td&gt;32.7&lt;/td&gt;
 &lt;td&gt;&lt;strong&gt;5&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;23.6&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Madrid, Spain&lt;/td&gt;
 &lt;td&gt;142.4&lt;/td&gt;
 &lt;td&gt;45.2&lt;/td&gt;
 &lt;td&gt;&lt;strong&gt;2.5&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;25.1&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Paris, France&lt;/td&gt;
 &lt;td&gt;149.9&lt;/td&gt;
 &lt;td&gt;8.3&lt;/td&gt;
 &lt;td&gt;17.5&lt;/td&gt;
 &lt;td&gt;&lt;strong&gt;1.4&lt;/strong&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Cagliari, Italy&lt;/td&gt;
 &lt;td&gt;165.5&lt;/td&gt;
 &lt;td&gt;30.1&lt;/td&gt;
 &lt;td&gt;&lt;strong&gt;29.5&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;30.4&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Auckland, New Zealand&lt;/td&gt;
 &lt;td&gt;173.7&lt;/td&gt;
 &lt;td&gt;159.8&lt;/td&gt;
 &lt;td&gt;&lt;strong&gt;1.1&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;161.4&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Krakow, Poland&lt;/td&gt;
 &lt;td&gt;174.1&lt;/td&gt;
 &lt;td&gt;31&lt;/td&gt;
 &lt;td&gt;&lt;strong&gt;8.8&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;43.6&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Haifa, Israel&lt;/td&gt;
 &lt;td&gt;177.9&lt;/td&gt;
 &lt;td&gt;78.1&lt;/td&gt;
 &lt;td&gt;&lt;strong&gt;0.5&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;64.5&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Porto Alegre, Brazil&lt;/td&gt;
 &lt;td&gt;179.1&lt;/td&gt;
 &lt;td&gt;149.9&lt;/td&gt;
 &lt;td&gt;&lt;strong&gt;30.2&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;169.9&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Nagano, Japan&lt;/td&gt;
 &lt;td&gt;185.8&lt;/td&gt;
 &lt;td&gt;4.8&lt;/td&gt;
 &lt;td&gt;13.1&lt;/td&gt;
 &lt;td&gt;&lt;strong&gt;4.7&lt;/strong&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Sydney, Australia&lt;/td&gt;
 &lt;td&gt;204.8&lt;/td&gt;
 &lt;td&gt;159.2&lt;/td&gt;
 &lt;td&gt;&lt;strong&gt;3.3&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;166.2&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Hong Kong, China&lt;/td&gt;
 &lt;td&gt;206.8&lt;/td&gt;
 &lt;td&gt;2.3&lt;/td&gt;
 &lt;td&gt;&lt;strong&gt;2.2&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;65.6&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Melbourne, Australia&lt;/td&gt;
 &lt;td&gt;207.6&lt;/td&gt;
 &lt;td&gt;177.9&lt;/td&gt;
 &lt;td&gt;&lt;strong&gt;1.7&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;166.6&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Singapore, Singapore&lt;/td&gt;
 &lt;td&gt;238.7&lt;/td&gt;
 &lt;td&gt;137.1&lt;/td&gt;
 &lt;td&gt;13.3&lt;/td&gt;
 &lt;td&gt;&lt;strong&gt;3.5&lt;/strong&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Shanghai, China&lt;/td&gt;
 &lt;td&gt;249.1&lt;/td&gt;
 &lt;td&gt;153.5&lt;/td&gt;
 &lt;td&gt;308.3&lt;/td&gt;
 &lt;td&gt;&lt;strong&gt;87&lt;/strong&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Mumbai, India&lt;/td&gt;
 &lt;td&gt;280.3&lt;/td&gt;
 &lt;td&gt;259.1&lt;/td&gt;
 &lt;td&gt;&lt;strong&gt;1.6&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;263.5&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Johannesburg, South Africa&lt;/td&gt;
 &lt;td&gt;304.8&lt;/td&gt;
 &lt;td&gt;293.2&lt;/td&gt;
 &lt;td&gt;&lt;strong&gt;19.3&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;274.7&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;Average (ms)&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;&lt;strong&gt;144.1&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;&lt;strong&gt;57.65&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;&lt;strong&gt;16.98&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;&lt;strong&gt;59.15&lt;/strong&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;a href="https://mrmatt.io/img/AmazonCloudfront.png"&gt;&lt;img alt="Amazon Cloudfront" loading="lazy" src="https://mrmatt.io/img/AmazonCloudfront_thumb.png" title="Amazon Cloudfront"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="setting-up-and-configuring-amazon-cloudfront"&gt;Setting up and configuring Amazon Cloudfront&lt;/h3&gt;
&lt;p&gt;Setup was &lt;a href="http://www.labnol.org/internet/setup-content-delivery-network-with-amazon-s3-cloudfront/5446/"&gt;super easy&lt;/a&gt; with the latest S3 Organizer Firefox Add-on. You can also complete the setup with a &lt;a href="http://docs.amazonwebservices.com/AmazonCloudFront/latest/GettingStartedGuide/index.html?ToolsYouNeed.html"&gt;Curl Script&lt;/a&gt;.&lt;/p&gt;</content:encoded><media:content url="https://mrmatt.io/images/MattWalker-avatar.png" medium="image"/></item><item><title>Remote Control Extender, convert your IR remote to RF.</title><link>https://mrmatt.io/posts/remote-control-extender-convert-your-ir-remote-to-rf/</link><pubDate>Wed, 02 Jul 2008 00:00:00 -0400</pubDate><guid>https://mrmatt.io/posts/remote-control-extender-convert-your-ir-remote-to-rf/</guid><description>Review of the Next Generation Remote Control Extender — converts any IR remote to RF so it works through walls up to 100 feet away.</description><content:encoded>&lt;p&gt;&lt;img loading="lazy" src="https://mrmatt.io/img/IR_RF_Remote_header.jpg"&gt;&lt;/p&gt;
&lt;p&gt;Are you sick of playing target practice with your remote controls?&lt;/p&gt;
&lt;p&gt;I tried several -remote control extenders-. Most of them relayed the infrared signal from one room to another. You still had to point the remote at the sensor and in most cases aim it. When I stumbled across the &lt;a href="https://www.amazon.com/dp/B000C1Z0HA"&gt;Next Generation Remote Control Extender&lt;/a&gt; I was skeptical. -You put the what where?- Yes- you put a Radio Frequency (RF) transmitter in place of one of the batteries in your Infrared (IR) remote. Don&amp;rsquo;t ask me how the thing works, but it transmits the signal to the odd looking receiver in the other room. At first I thought the receiver was battery powered. But it comes with an AC Adapter and actually charges the second smaller battery that powers the transmitter. This way you always have a battery ready to go, brilliant.&lt;/p&gt;
&lt;p&gt;I am currently using Microsoft&amp;rsquo;s Media Center &lt;a href="https://www.amazon.com/dp/B00066FIO6"&gt;Remote Control&lt;/a&gt; and &lt;a href="https://www.amazon.com/dp/B000AOAAN8"&gt;Keyboard&lt;/a&gt; with it and that they both work WAY better. I was on the verge or replacing the keyboard because you really had to point it to have fluid mouse movements. Now both remotes work on command and flawless in any room in my house!&lt;/p&gt;
&lt;p&gt;&lt;a href="https://mrmatt.io/img/IR_RF_Remote1.jpg"&gt;&lt;img alt="Infrared to RF Converter" loading="lazy" src="https://mrmatt.io/img/IR_RF_Remote1_thumb.jpg" title="Infrared to RF Converter"&gt;&lt;/a&gt;
&lt;a href="https://mrmatt.io/img/IR_RF_Remote2.jpg"&gt;&lt;img alt="Infrared to RF Converter 2" loading="lazy" src="https://mrmatt.io/img/IR_RF_Remote2_thumb.jpg" title="Infrared to RF Converter 2"&gt;&lt;/a&gt;
&lt;a href="https://mrmatt.io/img/IR_RF_Remote3.jpg"&gt;&lt;img alt="Infrared to RF Converter Details" loading="lazy" src="https://mrmatt.io/img/IR_RF_Remote3_thumb.jpg" title="Infrared to RF Converter Details"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Pros:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Super easy to setup.&lt;/li&gt;
&lt;li&gt;Works like any RF remote, no direct line of sight required, through walls - up to 100&amp;rsquo; away&lt;/li&gt;
&lt;li&gt;433.92 MHz - seems like a good frequency with little or no perceived interference.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Cons:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;I have heard it doesn&amp;rsquo;t work with all remotes, but it has worked with every one I have tried.&lt;/li&gt;
&lt;li&gt;Battery doesn&amp;rsquo;t last long with one battery remotes.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Conclusion:&lt;/p&gt;
&lt;p&gt;The technology and engineering behind this device is award winning. It does exactly what it claims to do, convert almost any IR remote to RF. I have been using it for about a month with great success!&lt;/p&gt;
&lt;p&gt;Get yours here:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.amazon.com/dp/B000C1Z0HA"&gt;Next Generation Remote Control Extender on Amazon&lt;/a&gt;&lt;/p&gt;</content:encoded><media:content url="https://mrmatt.io/images/MattWalker-avatar.png" medium="image"/></item><item><title>Yellow Spring Tulips</title><link>https://mrmatt.io/photography/2008-04-23-yellow-spring-tulips/</link><pubDate>Wed, 23 Apr 2008 00:00:00 -0400</pubDate><guid>https://mrmatt.io/photography/2008-04-23-yellow-spring-tulips/</guid><description>When spring decides to go all-in on yellow. These lily-flowered tulips were putting on quite the coordinated show, each one reaching upward like they were trying to out-dramatic the others.</description><content:encoded/><media:content url="https://mrmatt.io/photography/2008-04-23-yellow-spring-tulips/photo_hu_2d9d659a37aeda26.webp" medium="image"/></item><item><title>Happy Earth Day</title><link>https://mrmatt.io/posts/happy-earth-day/</link><pubDate>Tue, 22 Apr 2008 00:00:00 -0400</pubDate><guid>https://mrmatt.io/posts/happy-earth-day/</guid><description>The irony of spotting a Hummer parked front and center on Earth Day — a quick photo snapped at lunch that perfectly captures the contradiction.</description><content:encoded>&lt;p&gt;&lt;img alt="Earth Day Hummer" loading="lazy" src="https://mrmatt.io/img/Earth-Day-Hummer-Full.jpg"&gt;&lt;/p&gt;
&lt;p&gt;I couldn-t help but take this picture at lunch today.&lt;/p&gt;</content:encoded><media:content url="https://mrmatt.io/images/MattWalker-avatar.png" medium="image"/></item><item><title>Business Cards - MOO Cards</title><link>https://mrmatt.io/posts/business-cards-moo-cards/</link><pubDate>Sat, 19 Apr 2008 00:00:00 -0400</pubDate><guid>https://mrmatt.io/posts/business-cards-moo-cards/</guid><description>Review of MOO MiniCards — 100 unique business cards for $20, printed from Flickr photos with first-class packaging and quality.</description><content:encoded>&lt;p&gt;&lt;img alt="MOO Cards" loading="lazy" src="https://mrmatt.io/img/MOO_Cards_banner.jpg"&gt;&lt;/p&gt;
&lt;p&gt;I know this contradicts my recent &lt;a href="https://mrmatt.io/posts/printing-on-paper-is-a-bad-habit/"&gt;anti-printer/paper post&lt;/a&gt;- I found myself needing small pieces of paper with my social coordinates on them. I tried offering to beam a vCard. Don-t ask me why- it just isn-t socially acceptable or in most cases even possible. So, I needed some cards.&lt;/p&gt;
&lt;p&gt;I spent quite a bit of effort researching different styles and formats. There are some &lt;a href="https://www.flickr.com/photos/dailypoetics/sets/72057594104389710/"&gt;amazing custom cards&lt;/a&gt; out there. I didn-t want to break the bank or wait six months to have them made. I did want something unique and fun. I found the perfect thing; MOO cards. Apparently they have been all the rage in Europe and among trendy start-ups. I couldn-t help giving them a try.&lt;/p&gt;
&lt;p&gt;The entire MOO card experience was first class all the way. It started at the website. The interface was clean, easy to understand and simple to use. You can import your pictures from Flickr or upload them one at a time. For twenty dollars you get 100 cards and each card can be unique. The only downside is that they are currently only printed in Europe. Standard shipping to the US costs seven dollars and take a couple of weeks. Once they arrived, I was delighted with the quality. The paper weight and print finish compliment the format nicely. They are packed in a hefty plastic box (made from post-consumer material) and the marketing material is pure genius.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Moo Cards" loading="lazy" src="https://mrmatt.io/img/moo/moo_01.jpg"&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="Moo Cards" loading="lazy" src="https://mrmatt.io/img/moo/moo_02.jpg"&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="Moo Cards" loading="lazy" src="https://mrmatt.io/img/moo/moo_03.jpg"&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="Moo Cards" loading="lazy" src="https://mrmatt.io/img/moo/moo_04.jpg"&gt;
If you find yourself needing or maybe even wanting these little pieces of paper, give MOO cards a try.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;-The business card is the single most successful networking tool of all time, predating cellphones, the internet, and PDAs by some 300 years. Not bad for a bit of paper.-&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a href="https://moo.com/products/minicards.php"&gt;https://moo.com&lt;/a&gt;&lt;br&gt;
&lt;a href="https://www.flickr.com/groups/moo/"&gt;Flickr Group - MOO MiniCards&lt;/a&gt;&lt;/p&gt;</content:encoded><media:content url="https://mrmatt.io/images/MattWalker-avatar.png" medium="image"/></item><item><title>CSS Naked Day</title><link>https://mrmatt.io/posts/css-naked-day/</link><pubDate>Tue, 08 Apr 2008 00:00:00 -0400</pubDate><guid>https://mrmatt.io/posts/css-naked-day/</guid><description>Observing CSS Naked Day 2008 to promote web standards — what a site looks like without stylesheets, sprites, or positioning.</description><content:encoded>&lt;p&gt;&lt;img alt="CSS Nake Day 08" loading="lazy" src="https://mrmatt.io/img/naked-day-08.png"&gt;&lt;/p&gt;
&lt;p&gt;No my site isn-t broken-&lt;/p&gt;
&lt;p&gt;Today I am observing &lt;a href="http://naked.dustindiaz.com/"&gt;CSS Naked Day&lt;/a&gt; to help promote web standards. Not that I am completely compliant, but who really is when you have browsers that don&amp;rsquo;t want to play nice.&lt;/p&gt;
&lt;p&gt;The topic of CSS for me always brings up the great debate of &lt;a href="http://www.decloak.com/Dev/CSSTables/CSS_Tables_01.aspx"&gt;CSS vs tables&lt;/a&gt;. You will notice that I use a mixture on this site. That is because I absolutely hate absolute positioning in css. Another thing (that is gone with no css) are my sprites. They are great to cut down on requests and improve site performance. Here is what you are missing:&lt;/p&gt;
&lt;p&gt;&lt;img alt="MrMatt57.org Sprites" loading="lazy" src="https://mrmatt.io/img/sprites.png"&gt;&lt;/p&gt;
&lt;p&gt;Now that I am looking at my site without css, maybe we should start an &lt;strong&gt;advertising naked day&lt;/strong&gt; to show how cool our sites would be without them.&lt;/p&gt;
&lt;p&gt;Fore more reading take a look at: &lt;a href="http://www.webstandards.org/learn/faq/"&gt;What are web standards and why should I use them?&lt;/a&gt;&lt;/p&gt;</content:encoded><media:content url="https://mrmatt.io/images/MattWalker-avatar.png" medium="image"/></item><item><title>Printing on paper is a bad habit</title><link>https://mrmatt.io/posts/printing-on-paper-is-a-bad-habit/</link><pubDate>Sat, 29 Mar 2008 00:00:00 -0400</pubDate><guid>https://mrmatt.io/posts/printing-on-paper-is-a-bad-habit/</guid><description>Why printing on paper is a bad habit in the digital age — the case for going paperless and breaking our dependence on physical printouts.</description><content:encoded>&lt;p&gt;&lt;img alt="College Math Papers" loading="lazy" src="https://mrmatt.io/img/printer_paper.jpg"&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Photo by &lt;a href="https://www.flickr.com/photos/loty/326761635/"&gt;-sweetpea.loty30?&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Why do we print things on 8.5-11? pieces of amalgamated fibers? It&amp;rsquo;s a valid question these days. We have trained ourselves over thousands of years to cling to something physical or tangible. I just call it stuff. The digital age has brought about new adaptation challenges and the bad habits that come with them. Printing on paper is just one of them.&lt;/p&gt;
&lt;p&gt;What makes us convert that index-able, indefinitely archivable, extensible digital file to a physical, fragile and perishable paper format anyway? The first answers that come to mind is portability and well- just plain habit. We want to take that information with us; in the car, to a meeting, or for later use. It is convenient (for the most part) to print something in expectation of needing it in the future. It is so common to want to hold onto that physical representation of information, it has become habit. But think about it - is printing really the best solution. Maybe in isolated cases, but for the most part there are much better options. The Internet for one is extremely portable and convenient. Do yourself a favor, keep those digital files- they aren-t going anywhere (as long as you have a backup strategy). Your mobile phone or portable computer most likely can (or will very soon) allow you access to ALL your files.&lt;/p&gt;
&lt;p&gt;Another reason printing it is a bad habit is the stress it causes. For me it has always been a nightmare. Before the affordable laser printer; dot-matrix and ink jet were the norm. Paper jams, head cleaning, calibration or just running out of ink were daily occurrences. I currently have a HP-1020 that for the most part has served me well (aside from the early vista driver issues). Most of what is printed is single use, never to be looked at again. The paper ends up in a stack and ultimately gets in your way. It takes tremendous amounts of time to sort papers. This is where the duplex scanner comes in, but why print on the paper it in the first place? If you really need to -print- it for an archive, try &lt;a href="http://www.dopdf.com/"&gt;doPDF&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I am sure you have heard about the paperless movement. It&amp;rsquo;s just another reason to ditch the printer. It sounds great, but we are just on the edge of making it a reality. I am nowhere near paperless, but like most of us, have already started the process. Most of my bills are now paperless. I recently changed my car insurance policy with Progressive, they even offered to plant a tree in a National forest if I went paperless! The incentives for businesses are huge. I think the individual/consumer has yet to see or feel the full value. Everyone wants less junk mail, but what about the other benefits. Getting rid of or reducing your printing is a big part of the equation and should be a motivator. It definitely is for me.&lt;/p&gt;
&lt;p&gt;Next time you print something, ask yourself- -do I really need this on a piece of paper?- Do yourself and the world a favor- break the habit.&lt;/p&gt;
&lt;p&gt;[Print this page](javascript: alert(&amp;rsquo;lol, Gotcha!&amp;rsquo;))&lt;/p&gt;
&lt;p&gt;&lt;img alt="R1064614" loading="lazy" src="https://mrmatt.io/img/printer_shred.jpg"&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Photo by &lt;a href="https://www.flickr.com/photos/spilt-milk/1968415680/"&gt;-yoppy-&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;</content:encoded><media:content url="https://mrmatt.io/images/MattWalker-avatar.png" medium="image"/></item><item><title>Old Technology Meets New (XBox HD DVD &gt; Asus Eee)</title><link>https://mrmatt.io/posts/old-technology-meets-new-xbox-hd-dvd-asus-eee/</link><pubDate>Sat, 23 Feb 2008 00:00:00 -0500</pubDate><guid>https://mrmatt.io/posts/old-technology-meets-new-xbox-hd-dvd-asus-eee/</guid><description>Using the extinct Xbox 360 HD DVD drive to install a slim Windows XP on the Asus Eee PC — first impressions and early mods.</description><content:encoded>&lt;p&gt;&lt;img alt="Asus Eee XP install with external xbox 360 HD-DVD" loading="lazy" src="https://mrmatt.io/img/eee_xboxHDDVD.jpg"&gt;&lt;/p&gt;
&lt;p&gt;Just as the news that the Microsoft Xbox 360 external HD DVD drive was declared &lt;a href="http://www.electronista.com/articles/08/02/23/ms.axes.x360.hd.dvd.drive/"&gt;extinct&lt;/a&gt;, I found a use for it: load up my new Asus Eee with Windows XP. It is hopefully the last OS that has to be loaded from an optical drive and lets not forget about the F6 floppy driver loading routine (thankfully not needed for the Eee).&lt;/p&gt;
&lt;p&gt;I was going to use a &lt;a href="http://www.newegg.com/Product/Product.aspx?Item=N82E16812156101"&gt;Bytecc IDE to USB&lt;/a&gt; with an old drive I had laying around. But why not let this already defunct technology marvel go out with a bang. It worked great.&lt;/p&gt;
&lt;p&gt;Anyway, I loaded up the Eee with an &lt;a href="http://www.nliteos.com/"&gt;nlite&lt;/a&gt;-ed version of XP for a mere 480mb footprint. I followed this &lt;a href="http://www.i64x.com/eeexp.php"&gt;great tutorial&lt;/a&gt; over at i64X.com, thanks Jason!&lt;/p&gt;
&lt;p&gt;My first impressions of the Asus Eee are very good. I am finding all sorts of uses for it. I already voided the Newegg warranty by installing a gig of Geil RAM, we-ll see about voiding the Asus warranty and installing some hardware hacks (maybe GPS, bluetooth and 3G?). I promise, this isn-t going to turn into a Eee blog, but I will have more on it soon. This thing is amazing!&lt;/p&gt;</content:encoded><media:content url="https://mrmatt.io/images/MattWalker-avatar.png" medium="image"/></item><item><title>Movie: Into the Wild</title><link>https://mrmatt.io/posts/movie-into-the-wild/</link><pubDate>Tue, 19 Feb 2008 00:00:00 -0500</pubDate><guid>https://mrmatt.io/posts/movie-into-the-wild/</guid><description>Into the Wild is a must-see film — so inspiring it made me sell all my DVDs on half.com. The consumer release drops March 4th.</description><content:encoded>&lt;p&gt;You have undoubtedly heard about this outstanding book/movie. Well if you haven&amp;rsquo;t seen the movie yet, the consumer copy is being released on March 4th. This is a must see (and/or physically own if your primitive squirrel instincts are in overdrive).&lt;/p&gt;
&lt;p&gt;I will warn you; when I saw this movie in the theater this past fall, it inspired me to do all sorts of things, including selling all my DVDs/CDs on half.com. With that said, the link to the right is to the Amazon Unboxed pre-order.&lt;/p&gt;
&lt;p&gt;This is one you can&amp;rsquo;t miss.&lt;/p&gt;</content:encoded><media:content url="https://mrmatt.io/images/MattWalker-avatar.png" medium="image"/></item><item><title>Thank you Microsoft, Vista SP1 is Nice!</title><link>https://mrmatt.io/posts/thank-you-microsoft-vista-sp1-is-nice/</link><pubDate>Sat, 16 Feb 2008 00:00:00 -0500</pubDate><guid>https://mrmatt.io/posts/thank-you-microsoft-vista-sp1-is-nice/</guid><description>Windows Vista SP1 first impressions — noticeable performance gains, 28% faster network file transfers, and a smooth 32-minute install.</description><content:encoded>&lt;p&gt;&lt;img alt="Windows Vista SP1 Complete" loading="lazy" src="https://mrmatt.io/img/vista_sp1.jpg"&gt;&lt;/p&gt;
&lt;p&gt;I just installed Windows Vista SP1 and so far have noticed quite a performance improvement. Nothing scientific here- just -click of the mouse- impressions. But everything seems more responsive and smooth. One tangible difference is network file transfers. While moving files, pre-SP1 to a network drive I would get consistently 25mb/sec. Now the same transfer is moving along at 32mb/sec. Again, nothing concrete, but that is a 28% improvement. This is just one of the &lt;a href="http://download.microsoft.com/download/f/4/a/f4a35b2b-2f62-4104-a3e6-5f7bc1318e9f/Notable%20changes%20in%20Windows%20Vista%20SP1.pdf"&gt;notable changes&lt;/a&gt; in SP1.&lt;/p&gt;
&lt;p&gt;I have always enjoyed Vista. There are a lot of XP die hards out there, understandably not wanting to upgrade. I built a new computer when I migrated to Vista, so I didn&amp;rsquo;t feel the impact as much. The benefits have been great; environment is pleasurable and reliability has been top notch. I picture the trade offs like this: sure Windows 98SE was faster than XP on the same hardware. It&amp;rsquo;s only natural that XPsp2 will be faster than Vista on the same hardware. Every OS has its issue, even the prim and proper Apple got slammed for &lt;a href="http://blog.wired.com/monkeybites/2007/11/this-cat-has-fl.html"&gt;Leopard bugs&lt;/a&gt;. Change always brings new issues, but it also brings good.&lt;/p&gt;
&lt;p&gt;I have to thank Microsoft for this service pack. Yes, I know - they technically fixed something that was &amp;ldquo;broken&amp;rdquo;. But, it isn&amp;rsquo;t often that you see this noticeable of a performance gain without upgrading hardware or bumping your vcore.&lt;/p&gt;
&lt;p&gt;Installation was flawless, 32 minutes on &lt;a href="https://mrmatt.io/posts/my-rig/"&gt;my system&lt;/a&gt;. (i twitter-logged it)&lt;/p&gt;
&lt;p&gt;I &lt;a href="http://msdn2.microsoft.com/en-us/windowsvista/bb898842.aspx"&gt;downloaded&lt;/a&gt; the stand-alone update via MSDN. Microsoft is expected to &lt;a href="http://www.ecommercetimes.com/story/Microsoft-Bows-to-Business-Pressure-for-Earlier-SP1-Release-61643.html?welcome=1203142244"&gt;push the release date&lt;/a&gt; up to late February as opposed to mid March. If you wait for automatic updates to kick in, you won&amp;rsquo;t get it until mid April.&lt;/p&gt;
&lt;p&gt;I am quite happy.&lt;/p&gt;</content:encoded><media:content url="https://mrmatt.io/images/MattWalker-avatar.png" medium="image"/></item><item><title>RC Airplane Flight Box/Field Bag</title><link>https://mrmatt.io/posts/rc-airplane-flight-boxfield-bag/</link><pubDate>Tue, 12 Feb 2008 00:00:00 -0500</pubDate><guid>https://mrmatt.io/posts/rc-airplane-flight-boxfield-bag/</guid><description>What I pack in my RC airplane field bag — charger, tools, spare props, and a full checklist so nothing gets left behind.</description><content:encoded>&lt;p&gt;&lt;a href="https://mrmatt.io/img/flightbag1.jpg"&gt;&lt;img alt="Flight Bag 1" loading="lazy" src="https://mrmatt.io/img/flightbag1t.jpg" title="Flight Bag 1"&gt;&lt;/a&gt;
&lt;a href="https://mrmatt.io/img/flightbag2.jpg"&gt;&lt;img alt="Flight Bag 2" loading="lazy" src="https://mrmatt.io/img/flightbag2t.jpg" title="Flight Bag 2"&gt;&lt;/a&gt;
&lt;a href="https://mrmatt.io/img/flightbag3.jpg"&gt;&lt;img alt="Flight Bag 3" loading="lazy" src="https://mrmatt.io/img/flightbag3t.jpg" title="Flight Bag 3"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;click images to enlarge&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Field boxes are an essential part of rc flight. As I graduate from backyard flying, the more I realize this. There is nothting worse than getting to the flying field close to sun-down only to realize you forgot something. Having the right tool/band-aid could make the difference of another flight or heading home. Here is a list of things I keep in my flight bag. I restock as items are used and complete a checklist from time to time to make everything is there.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.flickr.com/photos/matt-walker/2260620347/"&gt;&lt;img loading="lazy" src="https://mrmatt.io/img/2260620347.jpg"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Triton 2 charger&lt;/li&gt;
&lt;li&gt;eFlight wattmeter&lt;/li&gt;
&lt;li&gt;RPM meter&lt;/li&gt;
&lt;li&gt;Assortment of propellers&lt;/li&gt;
&lt;li&gt;Spare transmitter batteries&lt;/li&gt;
&lt;li&gt;Rubber bands&lt;/li&gt;
&lt;li&gt;Tooth picks&lt;/li&gt;
&lt;li&gt;Pens / sharpie marker&lt;/li&gt;
&lt;li&gt;Xacto Knife&lt;/li&gt;
&lt;li&gt;#10 single edge razors&lt;/li&gt;
&lt;li&gt;Straping Tape&lt;/li&gt;
&lt;li&gt;Electrical Tape&lt;/li&gt;
&lt;li&gt;Threadlocker (loctite)&lt;/li&gt;
&lt;li&gt;Foam safe CA (superglue) / excellerant&lt;/li&gt;
&lt;li&gt;No 1 &amp;amp; 2 phillips screwdriver&lt;/li&gt;
&lt;li&gt;Weller AA powered soldering iron&lt;/li&gt;
&lt;li&gt;Wire strippers&lt;/li&gt;
&lt;li&gt;Pliers&lt;/li&gt;
&lt;li&gt;Scissors&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The bag is a Craftsman Professional 11-Pocket 8 in. Electricians Tool Tote from &lt;a href="http://www.kmart.com/shc/s/p_10153_12605_00948343000P"&gt;Sears&lt;/a&gt;. It fits everything nicely and is compact and light.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Flight Box/Field Bag Contents" loading="lazy" src="https://mrmatt.io/img/flightbox.jpg"&gt;&lt;/p&gt;</content:encoded><media:content url="https://mrmatt.io/images/MattWalker-avatar.png" medium="image"/></item><item><title>Webmasters: Don't forget about DNS...</title><link>https://mrmatt.io/posts/webmasters-dont-forget-about-dns/</link><pubDate>Tue, 29 Jan 2008 00:00:00 -0500</pubDate><guid>https://mrmatt.io/posts/webmasters-dont-forget-about-dns/</guid><description>Why DNS deserves more attention — performance tuning with TTL, anycast networks, outsourcing options, and the SEO impact of DNS changes.</description><content:encoded>&lt;p&gt;&lt;img alt="DNS Error" loading="lazy" src="https://mrmatt.io/img/dns_error.jpg"&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&lt;quote&gt;-A study conducted by IDC determined that only 41% of small companies and 35% of large organizations monitor Internet DNS response times.-&lt;/quote&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&amp;ldquo;DNS failures account for as much as 29% of system downtime costing companies millions of dollars.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;The Domain Name System (DNS) is arguably the single most important part of the internet. Just think: If websites only had a numerical lookup- our Rolodexes would be massively confusing. Why don&amp;rsquo;t webmasters/hosts treat DNS with the same priority? DNS servers carry such a high importance. Without them, no internet. They are often the &lt;a href="https://en.wikipedia.org/wiki/DNS_Backbone_DDoS_Attacks"&gt;target&lt;/a&gt; of massive Distributed Denial of Service (DDOS) attacks. Are your DNS records configured properly and secure?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;DNS Performance &amp;amp; Security Enhancements&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Most of the Internet is run on Berkeley Internet Name Domain (BIND) based servers. There have been a lot of failed attempts to create a BIND alternative (GnuDIP, MooDNS, Dents, OakDNS, CustomDNS and dproxy). A couple of active projects are PowerDNS, NSD, djdns and MaraDNS. All boast better security and performance improvements in different environments. If you are hosting your own DNS, you owe it to yourself/network to &lt;a href="https://en.wikipedia.org/wiki/Comparison_of_DNS_server_software"&gt;review your options&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Anycast" loading="lazy" src="https://mrmatt.io/img/ip_anycast.gif"&gt;&lt;/p&gt;
&lt;p&gt;Another performance and security innovation that is becoming increasing popular across many stateless services on the internet is IP &lt;a href="https://en.wikipedia.org/wiki/Anycast"&gt;anycast networks&lt;/a&gt;. It is a network addressing and routing scheme where data is routed to the nearest or best destination. It helps provide higher availability and load balancing. Many of the TLD servers name servers are already running on anycast networks. In fact these servers survived the massive &lt;a href="http://blog.icann.org/?p=37"&gt;DDOS attack last year&lt;/a&gt;. UtraDNS, Netriplex and DNS Made Easy are among the few providers to deliver DNS over an anycast network. I imagine many more will follow suit.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Minimizing down-time with TTL&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Most of us have been affected by DNS outages. There is not much you can do when a record gets out of whack or a server goes down. You can flush your local cache, maybe even try to resolve against a different server, but the only real cure is time. That is because each DNS record has a setting called Time to Live (TTL). This is how long the record should persist. Once this time expires, a new record is fetched. Also, if the servers aren-t available, you don-t get a record.&lt;/p&gt;
&lt;p&gt;A good rule of thumb when setting your TTL is to use your average visitor time. For example, if the average visitor spends five minutes on your site (check your analytics), you should set your TTL to 300 (in seconds). The idea is; in the event of a failover, it has the least impact on visitors and doesn&amp;rsquo;t require a performance hit for numerous DNS lookups. Some DNS providers won&amp;rsquo;t allow a TTL setting this low. A lower TTL means more DNS queries and ultimately more expense.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Should I outsource my DNS?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;A lot of network architects will actually advise webmasters to host their own DNS records. The route/latency will be the same as HTTP traffic, thus making lookup time approximately the same as a web requests. It also reduces the risk of outages caused solely by DNS. If they are hosted on the same subnet, routing glitches are minimized. If they are on the same server as your website, it will even further reduce the risk of DNS only outages.&lt;/p&gt;
&lt;p&gt;However, if you don&amp;rsquo;t have the time/experience to monitor, review logs and maintain your DNS servers, outsourcing is a great option. But beware, all DNS networks/servers are not created equally. If you are hosting your records with a budget registrar, chances are you are at risk. They are often the target of DDOS attacks, endure lengthy outages/maintenance and have congested networks. Security by obscurity; in the last two years I have had clients with DNS outages lasting greater than 6 hours at two of the major registrars.&lt;/p&gt;
&lt;p&gt;The Cadillac of DNS currently is &lt;a href="http://www.neustarultraservices.biz/"&gt;NeuStar&amp;rsquo;s UltraDNS&lt;/a&gt;. They have a globally redundant/optimized network with proprietary DNS software powered by Oracle replication. However, they charge by the query, so if you have a busy site, it will get expensive in a hurry. Another high-end service is &lt;a href="http://www.netriplex.com/solutions/critical_dns/"&gt;Netriplex&lt;/a&gt;. For small to mid size companies the only way to afford either of these services is to increase unfortunately to your TTL.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://www.dnsmadeeasy.com/u/39743"&gt;&lt;img alt="DNS Made Easy" loading="lazy" src="https://mrmatt.io/img/dns-made-easy-icon.gif"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The best budget priced service with high-end features/support I have found so far is &lt;a href="http://www.dnsmadeeasy.com/u/39743"&gt;DNS Made Easy&lt;/a&gt;. They have a great feature-set, excellent and knowledgeable staff all at a great price.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Search Engine Optimization (SEO)&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;There have been &lt;a href="http://www.askdavetaylor.com/can_dns_changes_affect_search_engine_results_placement_serp.html"&gt;reports&lt;/a&gt; of falling Search Engine Results Placements (SERPs) taking drastic drops soon after a DNS change. Changing just the IP address has not caused any notable problems. It is domain contact and name server changes that have caused the speculation. Many claim it resets the site&amp;rsquo;s Google &amp;ldquo;trust points&amp;rdquo;. There are many influences on SERPs and SEO is always a moving target, so take this warning with a grain of salt. But I thought it was worth mentioning.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;For More Information&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;DNS Wiki - &lt;a href="https://en.wikipedia.org/wiki/Domain_name_system"&gt;https://en.wikipedia.org/wiki/Domain_name_system&lt;/a&gt;&lt;br&gt;
DNS Forum - &lt;a href="http://member.dnsstuff.com/forums/"&gt;http://member.dnsstuff.com/forums/&lt;/a&gt;&lt;br&gt;
Web-based DNS Tools - &lt;a href="http://www.dnstools.com/"&gt;http://www.dnstools.com/&lt;/a&gt;&lt;br&gt;
DNS Surveys - &lt;a href="http://www.seoconsultants.com/tools/dns/surveys/"&gt;http://www.seoconsultants.com/tools/dns/surveys/&lt;/a&gt;&lt;/p&gt;</content:encoded><media:content url="https://mrmatt.io/images/MattWalker-avatar.png" medium="image"/></item><item><title>QOS for SOHO VOIP Solved, Tomato Firmware</title><link>https://mrmatt.io/posts/qos-for-soho-voip-solved-tomato-firmware/</link><pubDate>Thu, 24 Jan 2008 00:00:00 -0500</pubDate><guid>https://mrmatt.io/posts/qos-for-soho-voip-solved-tomato-firmware/</guid><description>How to fix VoIP call quality on a home network using Tomato firmware on a WRT54G router with QoS traffic shaping and SIP prioritization.</description><content:encoded>&lt;p&gt;Whoa, easy on the acronyms.&lt;/p&gt;
&lt;p&gt;One of my biggest challenges setting up my Small Office and Home Office (SOHO) Voice Over IP (VOIP) network has been related to Quality of Service (QOS). Have you ever been on a VOIP call and had people complain that you sound like you are in a tin can? Most residential broadband connections have a capped upload speed. If your internet habits are anything like mine, at times you can max your connection in both directions. Creating room for voice traffic can be a challenge. Current voice coding algorithms require 16 - 80 kbps for a single voice connection. If the throughput is not available or the latency is too high (&amp;gt; 250ms one way), voice quality will suffer or with some clients completely drop. A lot of routers/switches claim to come with QOS, most of them are pretty crude and require bandwidth/node fixing. There are also a number of plug and play solutions claiming to clear up the problem. They are generally expensive and do not offer custom traffic shaping.&lt;/p&gt;
&lt;h2 id="the-solution"&gt;The Solution&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://mrmatt.io/img/wrtg.gif"&gt;&lt;img alt="WRT54G" loading="lazy" src="https://mrmatt.io/img/wrtg_thumb.gif" title="WRT54G"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;A couple years ago Linksys went open source on one of their most popular broadband router firmware, the WRT54G. There have been a number of different firmware releases. I tried everything I could get my hands on. The one that stood out from the pack with both features and usability was &lt;a href="http://www.polarcloud.com/tomato"&gt;Tomato by Jonathan Zarate&lt;/a&gt;. It has a number of enhancements over the default firmware, the most notable being:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;AJAX enabled interface&lt;/li&gt;
&lt;li&gt;Sweet &lt;a href="http://www.polarcloud.com/v/scbwm.htm"&gt;bandwidth usage monitor&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Advanced QOS&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.polarcloud.com/v/screst.htm"&gt;Access Restrictions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;New wireless features such as WDS and &lt;a href="http://www.polarcloud.com/v/scclient.htm"&gt;wireless client modes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Raises the limits on maximum connections for P2P&lt;/li&gt;
&lt;li&gt;Allows you to run your custom scripts or telnet/ssh in and do all sorts of things like re-program the SES/AOSS button&lt;/li&gt;
&lt;li&gt;Adds wireless site survey to see your wifi neighbors&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="installing-tomato"&gt;Installing Tomato&lt;/h2&gt;
&lt;p&gt;Just a couple of notes here. Make sure you check your device&amp;rsquo;s hardware version number. Unfortunately you can&amp;rsquo;t walk into your local computer superstore and pick one up anymore, v5+ hardware is not supported. If your WRT54G is a couple of years old, chances are you have one of the &lt;a href="http://www.polarcloud.com/tomatofaq#what_will_this_run_on"&gt;supported devices&lt;/a&gt;. Installation is pretty straight forward, just flash it with the &lt;a href="http://www.polarcloud.com/firmware"&gt;latest firmware&lt;/a&gt;. This will wipe your settings, so make sure you grab screenshots/write them down before you get started. The default GUI username is -admin- or -root- (username is required), ssh and telnet username is always -root-, and the default password is -admin-.&lt;/p&gt;
&lt;h2 id="configuring-basic-firewall-functions"&gt;Configuring Basic Firewall Functions&lt;/h2&gt;
&lt;p&gt;This step will vary depending on your ISP, network configuration and VOIP provider. WAN/LAN configuration is straight forward and should be configured the same as it was in your default firmware. Port forwarding depends on what VOIP gateway hardware you have. The standard signaling port for SIP is 5060-5063 UDP and RTP voice travels on 16384 - 16482 UDP (some phones may need ranges up to 10000 - 20000 UDP). I have set up a &lt;a href="http://www.trixbox.org/"&gt;Trixbox&lt;/a&gt; PBX locally for handling calls (thinking of trying asterisk on linode). Forwarding the above ports allows me to authenticate a trunk with my provider, VoIP your life. Make a couple of test calls over a quiet internet connection to ensure everything is working. Audio in both directions should be without glitch.&lt;/p&gt;
&lt;h2 id="setting-up-quality-of-service"&gt;Setting up Quality of Service&lt;/h2&gt;
&lt;p&gt;With Tomato you can classify data by IP or Mac Address, Source/Destination Port and how much data is being transferred. You will want to adjust these setting to match your usage. For example, I am digesting a shoutcast stream 24/7 and have set 8000-8006 to highest priority to avoid interruptions.&lt;/p&gt;
&lt;h3 id="enabling-qos"&gt;Enabling QOS&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Log-in to your router&lt;/li&gt;
&lt;li&gt;Open the QOS &amp;gt; Basic Settings Menu&lt;/li&gt;
&lt;li&gt;Check -Enable QOS-&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img alt="Enabling QOS" loading="lazy" src="https://mrmatt.io/img/QOS_enabled.gif"&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Note: if you use applications that do a lot of ACKnowledgment requests (BitTorrent), you might want to consider turning this option off&lt;/em&gt;&lt;/p&gt;
&lt;h3 id="outbound-rate--limit"&gt;Outbound Rate / Limit&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Max Bandwidth: this is your maximum outbound (upload) bandwidth. You can determine your speed at &lt;a href="http://www.dslreports.com/stest"&gt;DSL Reports&lt;/a&gt;, &lt;a href="http://www.speakeasy.net/speedtest/"&gt;SpeakEasy&lt;/a&gt; or &lt;a href="http://www.speedtest.net/"&gt;Speedtest.net&lt;/a&gt;. A hack to ensure you have enough overhead is to intentionally low-ball this number. You would only want to do this if absolutely necessary as you would not be fully utilizing your bandwidth.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img alt="QOS Outbound Rate / Limit" loading="lazy" src="https://mrmatt.io/img/QOS_Outbound_Limit.gif"&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Note: These are the settings that work for me, you will most likely have to tweak them&lt;/em&gt;&lt;/p&gt;
&lt;h3 id="inbound-limit"&gt;Inbound Limit&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Max Bandwidth: Use the inbound (download) results from your tests above.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img alt="QOS Inbound Limit" loading="lazy" src="https://mrmatt.io/img/QOS_Inbound_limit.gif"&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Note: These are the settings that work for me, you will most likely have to tweak them&lt;/em&gt;&lt;/p&gt;
&lt;h3 id="classifications"&gt;Classifications&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Open the QOS &amp;gt; Classifications Menu&lt;/li&gt;
&lt;li&gt;Add Entry for Any Address, TCP/UDP, Src or Dst 5060 (your SIP Signaling port), Highest Priority&lt;/li&gt;
&lt;li&gt;Add Entry for Any Address, TCP/UDP, Src or Dst 16384-16482 (your RTP Voice port range), Highest Priority&lt;/li&gt;
&lt;li&gt;Move them to the top of the list&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Note: If you have any other traffic (P2P) on ports these ports, you should try the SIP I7-Filter.&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Note: Another solution is to setup a QOS classification for the IP/Mac addresss of your standalone VoIP phones or adapters if they are connecting to a trunk over the internet&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Note: Some phones require a different RTP range for example, my Linksys SPA942&amp;rsquo;s call for 10,000 - 20,000 UDP. Check with your phone or ATA documentation to determine the actual RTP port range.&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a href="https://mrmatt.io/img/QOS_Classifications_full.gif"&gt;&lt;img alt="QOS Classifications" loading="lazy" src="https://mrmatt.io/img/QOS_Classifications.gif" title="QOS Classifications"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="testing-testing-testing"&gt;Testing, Testing, Testing&lt;/h2&gt;
&lt;p&gt;Now that you have established a baseline for your QOS, it&amp;rsquo;s time to see if it works. First, if possible test on a clean connection to make sure nothing is out of whack. Now for the fun part; max your connection out. Start your P2P, BitTorrent, Large file Uploads, Video Streaming and anything else you can think of. You can check how much you are using in the Bandwidth &amp;gt; Real Time menu. Tomato also comes with two very useful tools to debug your QOS settings.&lt;/p&gt;
&lt;h3 id="distribution-graphs"&gt;Distribution Graphs&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Use these graph to determine where your connections are being classified. If you see something out of balance, you can adjust your classifications accordingly.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a href="https://mrmatt.io/img/QOS_Graph_full.gif"&gt;&lt;img alt="QOS Distribution Graph" loading="lazy" src="https://mrmatt.io/img/QOS_Graph.gif" title="QOS Distribution Graph"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="detailed-view"&gt;Detailed View&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;This shows what traffic is currently flowing and how it is being classified. Take a look at each of the connections and make sure it is classified correctly. This report is also useful for determining the source of rouge traffic.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a href="https://mrmatt.io/img/QOS_Details_full.gif"&gt;&lt;img alt="QOS Details" loading="lazy" src="https://mrmatt.io/img/QOS_Details.gif" title="QOS Details"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="the-downside-"&gt;The Downside-&lt;/h2&gt;
&lt;p&gt;Yea, there is usually a con with every pro. To make this setup work correctly, you are essentially capping your throughput. Some networks offer pooled connections and have -boost- speeds. You will not be able to take advantage of these features. Most of the bandwidth related troubles with SOHO VOIP is outbound, so one workaround is to turn off the Inbound Limits. It is not fool-proof, but in some setups will work just fine.&lt;/p&gt;
&lt;h2 id="summary"&gt;Summary&lt;/h2&gt;
&lt;p&gt;As you can see, the Tomato firmware gives you granular traffic shaping control. Implementing these QOS settings has not only eliminated my VOIP problems, it has also made a noticeable difference in the overall speed and consistency of my connection. DNS queries resolve faster, multiple HTTP requests are balanced and I can transfer large files in the background. Even if you are not ready to take the leap to VOIP, I highly recommend Tomato Firmware.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Banner photo by &lt;a href="https://flic.kr/p/8yLiUh"&gt;-Peter Castleton-&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;</content:encoded><media:content url="https://mrmatt.io/images/MattWalker-avatar.png" medium="image"/></item><item><title>Pet Fish Archive</title><link>https://mrmatt.io/posts/pet-fish-archive/</link><pubDate>Fri, 18 Jan 2008 00:00:00 -0500</pubDate><guid>https://mrmatt.io/posts/pet-fish-archive/</guid><description>A look back at my pet fish over the years — red belly piranhas, a saltwater tank, and Scooby the miracle guppy who survived as a feeder fish.</description><content:encoded>&lt;p&gt;I have had pet fish on and off ever since I can remember. I don&amp;rsquo;t currently have fish, but can&amp;rsquo;t wait to set up another tank. Here are some of my recent tanks.&lt;/p&gt;
&lt;h2 id="three-red-belly-piranhas"&gt;Three Red Belly Piranhas&lt;/h2&gt;
&lt;p&gt;I started these fish in a 20 gallon long aquarium while I was waiting for the biomass to build in the 55 gallon. They were the size of a quarter when I bought them. They eventually outgrew the 55 gallon and I had to return them to the fish store. They were a lot of fun, but also a lot of work.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Red Belly Piranhas" loading="lazy" src="https://mrmatt.io/img/fish_1.jpg"&gt;&lt;/p&gt;
&lt;p&gt;Here is a short video of them at I think about a year old.&lt;/p&gt;
&lt;div class="videoWrapper"&gt;
 &lt;iframe title="My Red Belly Piranhas" width="1280" height="720" src="https://www.youtube.com/embed/d1pfTSdFcYs?rel=0&amp;amp;controls=0" frameborder="0" allowfullscreen&gt;&lt;/iframe&gt;
&lt;/div&gt;
## First Saltwater Tank
&lt;p&gt;This tank was started with a variety of damsels and later the home for a maroon clown, coral beauty and a crab.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Fist Saltwater Tank" loading="lazy" src="https://mrmatt.io/img/fish_2.jpg"&gt;&lt;/p&gt;
&lt;h2 id="scooby-the-guppy"&gt;Scooby the Guppy&lt;/h2&gt;
&lt;p&gt;This fish was amazing, she lived to be one and a half years old! She started her life as a feeder fish for my red belly piranhas. She found a comfortable hiding spot between one of the logs and a plant and managed to survive for almost a month. She deserved a home of her own. I moved her into a 5 gallon tank where she somehow managed to give birth to 5 babies! This was truly a miracle fish. She ultimately died of natural causes.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Scooby the Guppy" loading="lazy" src="https://mrmatt.io/img/fish_3.jpg"&gt;&lt;/p&gt;</content:encoded><media:content url="https://mrmatt.io/images/MattWalker-avatar.png" medium="image"/></item><item><title>Funny IT Voicemail, "That Projector Stinks"</title><link>https://mrmatt.io/posts/voicemail-funny-it-momment/</link><pubDate>Wed, 16 Jan 2008 00:00:00 -0500</pubDate><guid>https://mrmatt.io/posts/voicemail-funny-it-momment/</guid><description>A real voicemail from my IT manager days — a projector that smelled so bad nobody could sit in the same room with it.</description><content:encoded>&lt;p&gt;&lt;img alt="Voicemail from the entertainment capitol of the world" loading="lazy" src="https://mrmatt.io/img/voicemail.jpg"&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Photo by &lt;a href="https://www.flickr.com/photos/duchamp/8155917/"&gt;-Duchamp-&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;This is a classic:&lt;/p&gt;
&lt;p&gt;-Matt, hi, it&amp;rsquo;s Rob. I wanted to give you an update on the power point projector that we loaned out. We haven&amp;rsquo;t used it yet, and there is a reason. Um - before, we were going to use it for a client presentation, we had a supplier come in and borrow it to present some of his own things to us - and the stink in the room was so bad, we thought it was the guy. Today we tried the power point LCD projector again and it is the projector. Matt, I don&amp;rsquo;t know what it is, but the ventilation of this projector is blowing out a stink like BO like you would not believe. We can&amp;rsquo;t even sit in same room with it. So I just wanted to give you a heads up. I don&amp;rsquo;t know what is wrong with it, but I can&amp;rsquo;t see anyone being able to use it for a presentation. You might want to look into maybe cleaning the filter or something on that thing. That was it, thanks, bye.&amp;quot;&lt;/p&gt;
&lt;p&gt;This is a real voice mail I received a couple of years ago when I was an IT Manager for a media buying company.&lt;/p&gt;</content:encoded><media:content url="https://mrmatt.io/images/MattWalker-avatar.png" medium="image"/></item><item><title>Hello World; new server, theme, content, collaboration</title><link>https://mrmatt.io/posts/hello-world-new-server-theme-content-collaboration/</link><pubDate>Wed, 16 Jan 2008 00:00:00 -0500</pubDate><guid>https://mrmatt.io/posts/hello-world-new-server-theme-content-collaboration/</guid><description>Site migration complete — moved to Linode hosting, Amazon S3 for static files, and a custom scratch-built WordPress theme.</description><content:encoded>&lt;p&gt;&lt;img alt="H" loading="lazy" src="https://mrmatt.io/img/hello-world.jpg"&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Photo by &lt;a href="https://www.flickr.com/photos/audreyjm529/192646613/"&gt;-audreyjm529?&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Welcome.&lt;/p&gt;
&lt;p&gt;If you are reading this post, the migration is complete. As promised, an all new site, with all new content.&lt;/p&gt;
&lt;p&gt;With this much needed upgrade, I chose Linode.com as a host. They have many distros to choose from and a great management console. I am also partial to hosting facilities located in Texas for the geographic advantages. In the migration, I implemented Amazon S3 storage for all my static files. I have also scratch built a wordpress theme with minimalist styling and speed in mind. I still have a lot of tweaking to go, but it was time for launch.&lt;/p&gt;
&lt;p&gt;More to follow-&lt;/p&gt;</content:encoded><media:content url="https://mrmatt.io/images/MattWalker-avatar.png" medium="image"/></item><item><title>Google Chart API, Wow!</title><link>https://mrmatt.io/posts/google-chart-api-wow/</link><pubDate>Fri, 21 Dec 2007 00:00:00 -0500</pubDate><guid>https://mrmatt.io/posts/google-chart-api-wow/</guid><description>First look at Google's Chart API — generating line, bar, and pie charts from a simple URL string with no server-side code.</description><content:encoded>&lt;p&gt;I have always been a sucker for statistical eye candy. Last week Google launched their chart APIs. I have always done charts client-side with a java applet or SWF application. Server-side solutions were always kludgey. They required way too much customization and each provider/chart style had a different interface. Thanks to Google, static charts are now super simple. The API currently supports line, bar, pie charts, venn diagrams and scatter plots. Most of the standard Google API rules apply. You are going to see these popping up all over the place. I know I am definitely going to give them a try.&lt;/p&gt;
&lt;p&gt;This:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;http://chart.apis.google.com/chart?cht=lc&amp;amp;chd=s:cEAELFJHHHKU
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ju9uuXUc&amp;amp;chco=76A4FB&amp;amp;chls=2.0,0.0,0.0&amp;amp;chs=200x125&amp;amp;chg=20,50,
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;1,0&amp;amp;chxt=x,y&amp;amp;chxl=0:|0|1|2|3|4|5|1:|0|50|100
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;turned into a rendered chart image.&lt;/li&gt;
&lt;li&gt;Beautiful, isn&amp;rsquo;t it.&lt;/li&gt;
&lt;/ul&gt;</content:encoded><media:content url="https://mrmatt.io/images/MattWalker-avatar.png" medium="image"/></item><item><title>How Projects Really Work</title><link>https://mrmatt.io/posts/how-projects-really-work/</link><pubDate>Thu, 20 Dec 2007 00:00:00 -0500</pubDate><guid>https://mrmatt.io/posts/how-projects-really-work/</guid><description>The classic 'How Projects Really Work' cartoon — a timeless take on the gap between what clients want and what gets built.</description><content:encoded>&lt;p&gt;This is an oldie, but a goodie-&lt;a href="https://mrmatt.io/img/project.jpg"&gt;&lt;img alt="How Projects Really Work" loading="lazy" src="https://mrmatt.io/img/project_thumb.jpg" title="How Projects Really Work"&gt;&lt;/a&gt; &lt;a href="http://www.projectcartoon.com/"&gt;http://www.projectcartoon.com/&lt;/a&gt;&lt;/p&gt;</content:encoded><media:content url="https://mrmatt.io/images/MattWalker-avatar.png" medium="image"/></item><item><title>RC Plane Hanger</title><link>https://mrmatt.io/posts/my-rc-planes/</link><pubDate>Wed, 19 Dec 2007 00:00:00 -0500</pubDate><guid>https://mrmatt.io/posts/my-rc-planes/</guid><description>My RC airplane collection — Soarstar trainer, E-flite Yak 54F with ailerons, and a scratch-built Slowfly foamie.</description><content:encoded>&lt;p&gt;&lt;img alt="RC Airplanes" loading="lazy" src="https://mrmatt.io/img/rc-airplane.jpg" title="RC Airplanes"&gt;&lt;/p&gt;
&lt;p&gt;Here is a overview of the planes I have been flying. More details to come-&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Soarstar Electric ARF&lt;/strong&gt; by Super Flying Models&lt;br&gt;
&lt;a href="https://mrmatt.io/img/soarstar2.jpg"&gt;&lt;img alt="Soarstar" loading="lazy" src="https://mrmatt.io/img/soarstar2_t.jpg" title="Soarstar"&gt;&lt;/a&gt;&lt;br&gt;
*This is my first real plane. It was a great trainer&lt;br&gt;
for both the build process and flight.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Yak 54F&lt;/strong&gt; by E-flite (Quique Somenzini designed)&lt;br&gt;
&lt;a href="https://mrmatt.io/img/yak.jpg"&gt;&lt;img alt="eFlite Yak 54F" loading="lazy" src="https://mrmatt.io/img/yak_t.jpg" title="eFlite Yak 54F"&gt;&lt;/a&gt;&lt;br&gt;
*This is my first plane with ailerons, it flies great. 20-20 Hacker&lt;br&gt;
Style motor, 25amp Towpro ESC, 3S 1300 Lipos&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Slowfly&lt;/strong&gt; plans by Sloper Steve&lt;br&gt;
&lt;a href="https://mrmatt.io/img/slofly.jpg"&gt;&lt;img alt="Slofly Scratchbuilt Foamie" loading="lazy" src="https://mrmatt.io/img/slofly_t.jpg" title="Slofly Scratchbuilt Foamie"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;This is my first scratch built. I am currently building&lt;br&gt;
another and will post details.&lt;/em&gt;&lt;/p&gt;</content:encoded><media:content url="https://mrmatt.io/img/rc-airplane.jpg" medium="image"/></item><item><title>December is for Cynics</title><link>https://mrmatt.io/posts/december-is-for-cynics/</link><pubDate>Fri, 14 Dec 2007 00:00:00 -0500</pubDate><guid>https://mrmatt.io/posts/december-is-for-cynics/</guid><description>The Matches' 'December Is for Cynics' — a holiday season favorite discovered on Radio Wazee and heard live at the Masquerade in Atlanta.</description><content:encoded>&lt;p&gt;&lt;img alt="The Matches" loading="lazy" src="https://mrmatt.io/img/match.jpg"&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Photo by &lt;a href="https://www.flickr.com/photos/amagill/176876227/"&gt;-AMagill-&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;-Tis the season for some of the most awful music. Thanks to The Matches, it&amp;rsquo;s not all bad. This song has been a favorite since the first time I heard it on &lt;a href="http://wazee.org"&gt;Radio Wazee&lt;/a&gt;. I was also fortunate to hear them live at the Masquerade in Atlanta, GA.&lt;/p&gt;
&lt;div class="videoWrapper"&gt;
 &lt;iframe title="The Matches - December Is for cynics (Live - 2003)" width="1280" height="720" src="https://www.youtube.com/embed/i0dosqpNp6Y?rel=0&amp;amp;controls=0" frameborder="0" allowfullscreen&gt;&lt;/iframe&gt;
&lt;/div&gt;</content:encoded><media:content url="https://mrmatt.io/images/MattWalker-avatar.png" medium="image"/></item><item><title>Live Music Keeps Me Going</title><link>https://mrmatt.io/posts/live-music/</link><pubDate>Tue, 11 Dec 2007 00:00:00 -0500</pubDate><guid>https://mrmatt.io/posts/live-music/</guid><description>The world of live music recordings — from trading tapes and B&amp;Ps to lossless FLAC archives on archive.org and BitTorrent.</description><content:encoded>&lt;p&gt;&lt;img alt="Particle Live" loading="lazy" src="https://mrmatt.io/img/live-music.jpg"&gt;&lt;/p&gt;
&lt;p&gt;Who doesn-t like live music? To me it is one of the purest forms of art. It used to be the only way you take it in was, well to go to a concert. That is unless you happened to find a mono cassette copy or got really lucky and scored a soundboard recording. Most bands know for their live creativity have an open &lt;a href="http://bluestraveler.net/btpolicy.html"&gt;taping policy&lt;/a&gt;. This is great for people who only get out a couple times a year to take in a show. With a good stereo or set of headphones, you can enjoy a concert while you relax at home.&lt;/p&gt;
&lt;p&gt;Recording technology has come a long way. Just a couple of years ago, the only way to get your hands on live recordings was to network/trade with tapers/collectors or request blanks and postage (B&amp;amp;P) exchanges. Most taping policies do not allow recordings to be bought or sold. B&amp;amp;Ps are a great way to get started, friendly collectors (or people who accidentally burned two copies) would offer up shows. All you had to do was send blank media with a return mailer. This was how I got started with my collection.&lt;/p&gt;
&lt;p&gt;I haven-t traded for almost 4 years now. You can see my collection at &lt;a href="https://db.etree.org/mrmatt57"&gt;https://db.etree.org/mrmatt57&lt;/a&gt;. I still have most of these shows. If you want one, drop me a line, I-d be happy to do a trade or B&amp;amp;P.Collectors started of with analog recordings, then DATs/CDs, now it-s all lossless digital. Two popular formats are Shorten (shn) and Free Lossless Audio Codec (flac). Over the years flac has grown to be the defacto-standard. Unless you are streaming audio over the internet, live recordings should never be archived or stored in compressed codecs, like MP3.&lt;/p&gt;
&lt;p&gt;Many websites are poping up hosting BitTorrents and archives of shows. The largest collection by far is &lt;a href="https://www.archive.org/details/etree"&gt;archive.org&lt;/a&gt;. This is the same website that brought us the &lt;a href="https://www.archive.org/web/web.php"&gt;way back machine&lt;/a&gt;. Take a look, your favorite band just might be listed.Much of this is only possible because a dedicated few that make the journeys to the shows and lug around their equipment. Much thanks goes out to all the tapers!&lt;/p&gt;
&lt;p&gt;Although music is the main reason most of us go to shows, the energy and visual experience of being there in person can not be understated. Nothing truly replaces physically being there. But there is something to be said about listening to a concert at 9am in the morning. I enjoy listening to shows with headphones. My favorite phones are Sennheiser HD580?s. It takes ample volume to bring you that true concert sound. If you find your headphones aren-t loud enough, pickup or build a &lt;a href="https://en.wikipedia.org/wiki/Cmoy"&gt;cMoy&lt;/a&gt; pocket amp, you won-t regret it &lt;em&gt;(disclaimer: not responsible for any hearing loss/damage)&lt;/em&gt;. So, next time you are jonesing for a concert, check out the new digital world of live music.&lt;/p&gt;</content:encoded><media:content url="https://mrmatt.io/images/MattWalker-avatar.png" medium="image"/></item><item><title>My Computing Equipment</title><link>https://mrmatt.io/posts/my-rig/</link><pubDate>Sat, 08 Dec 2007 00:00:00 -0500</pubDate><guid>https://mrmatt.io/posts/my-rig/</guid><description>Full specs of my 2007 workstation — overclocked Core 2 Duo, triple Samsung displays, RAID 5 storage, and Sennheiser HD580 headphones.</description><content:encoded>&lt;p&gt;&lt;a href="https://www.flickr.com/photos/matt-walker/2218793987/"&gt;&lt;img loading="lazy" src="https://mrmatt.io/img/2218793987.jpg"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;People have been asking what I am running for my current system so here is a post dedicated to just that. Several of the components have been carried over from many configurations ago. I have been through many iterations and a lot of research to derive this system. Suggestions and Questions are welcome, please comment.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://mrmatt.io/img/computer3.jpg"&gt;&lt;img alt="My Computer" loading="lazy" src="https://mrmatt.io/img/computer3_thumb.jpg" title="My Computer"&gt;&lt;/a&gt;
&lt;a href="https://mrmatt.io/img/computer2.jpg"&gt;&lt;img alt="My Computer 2" loading="lazy" src="https://mrmatt.io/img/computer3_thumb.jpg" title="My Computer 2"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Triple Display:&lt;/strong&gt;
Samsung 225BW 22? LCD&lt;br&gt;
Two Samsung 920N 19? LCD&lt;br&gt;
Ergotron DS100 Stand&lt;strong&gt;System:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Intel D975XBX (Rev A0)&lt;br&gt;
Intel E6600 Core 2 Duo @ 3072mhz (1365.7mhz FSB, x9 Multiplier, 1.259v Core)&lt;br&gt;
Scythe Ninja Air Cooler w/ 2 x Thermaltake 120mm 95cfm&lt;br&gt;
4Gb Geil @ 853.8Mhz 5-5-5-15 (4:5 FSB:DRAM)&lt;br&gt;
EVGA GeForce 8800GTS @ 576Mhz 640MB @ 1.7Ghz&lt;br&gt;
Biostar GeForce 8400GS 256MB (KVM Display)&lt;br&gt;
SuperMicro 5bay Hot Swapable HDD Enclosures x 2&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Drives:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;NEC ND-3520AW DVD_RW&lt;br&gt;
Plextor PX-W5224A CD-R&lt;br&gt;
NewEgg 4Gig USB2 (ReadyBoost)&lt;br&gt;
IronKey 4Gig USB2 (Portable Storage/Apps)&lt;br&gt;
Western Digital 74Gig 10,000 RPM Raptor (Primary OS)&lt;br&gt;
4 - Seagate 320Gig 7200.10 (Raid 5 Storage)&lt;br&gt;
Old SATA drives for backup&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Peripherals:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Color Balanced with Pantone Huey Pro&lt;br&gt;
Microsoft/Razor Reculsa Keyboard&lt;br&gt;
Logitech G5 Mouse&lt;br&gt;
Func F30.R Mouse Pad (Get one while they last)&lt;br&gt;
IOGear Miniview Symphony Multi-function 4-Port KVM Switch&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Sound:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Swan M10 2.1 Desktop System&lt;br&gt;
Sennheiser HD580 Headphones&lt;br&gt;
Shure E4c Sound Isolating Headphones&lt;/p&gt;
&lt;p&gt;** Software:**&lt;/p&gt;
&lt;p&gt;Micorsoft Windows Vista 32-bit (Yuk, if only 64-bit software was up to par)&lt;br&gt;
Editplus (By far my favorite text editor)&lt;br&gt;
Microsoft Office 2007 (Love it!)&lt;br&gt;
Virtual Ubuntu and Red Hat Distros&lt;br&gt;
Various Virtual Development Environements (clients, servers, stress-tests, etc)&lt;/p&gt;
&lt;p&gt;** Network:**&lt;/p&gt;
&lt;p&gt;Linksys WRTG 54 v3 - Tomato Firmware (QOS rocks!)&lt;br&gt;
Linksys EG005W Gigabit 5-port Workgroup Switch&lt;br&gt;
Pure Cat6 Cables&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Power:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Monster PowerCenter Hts 2000 Mkii&lt;br&gt;
CyberPower CP1350AVRLCD UPS Battery Backup - 1350VA&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Printer:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;HP LaserJet 1020 (B&amp;amp;W)&lt;br&gt;
Target/Kinkos (Color Photo Prints)&lt;/p&gt;</content:encoded><media:content url="https://mrmatt.io/images/MattWalker-avatar.png" medium="image"/></item><item><title>1994 Honda Accord</title><link>https://mrmatt.io/posts/1994-honda-accord/</link><pubDate>Tue, 04 Dec 2007 00:00:00 -0500</pubDate><guid>https://mrmatt.io/posts/1994-honda-accord/</guid><description>Full mod list and photos of my 1994 Honda Accord — engine work, suspension, stereo, and the car that taught me to turn every bolt.</description><content:encoded>&lt;p&gt;&lt;img loading="lazy" src="https://mrmatt.io/img/Accord.jpg"&gt;&lt;/p&gt;
&lt;p&gt;This one is really digging into the archives- Here are some pictures and specs of my first car (not my parent&amp;rsquo;s station wagon). I learned a lot and have many great memories from this car. I often joked, that I literally turned every bolt on this car. I didn&amp;rsquo;t have access to air tools either, all old-fashioned Craftsman hand tools. There were many long cold nights completing mods so that I would be ready to drive to work the next day. I wish I had better pictures, but here they are (Mostly scanned 35mm, if I find the original print/negatives, I will rescan).&lt;/p&gt;
&lt;p&gt;&lt;a href="https://mrmatt.io/img/accord_engine1.jpg"&gt;&lt;img alt="1994 Honda Accord" loading="lazy" src="https://mrmatt.io/img/accord_engine1_thumb.jpg" title="1994 Honda Accord"&gt;&lt;/a&gt; &lt;a href="https://mrmatt.io/img/accord_engine2.jpg"&gt;&lt;img alt="1994 Honda Accord 2" loading="lazy" src="https://mrmatt.io/img/accord_engine2_thumb.jpg" title="1994 Honda Accord 2"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Engine/Performance:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Port/Polished Cylinder Head (Gude)&lt;br&gt;
Bored Throttle Body (Gude)&lt;br&gt;
Port/Polished Intake Manifold (Gude)&lt;br&gt;
Reprogrammed ECU (Gude)&lt;br&gt;
Racing valve grind (Gude)&lt;br&gt;
Top-End Cam (Gude)&lt;br&gt;
MSD SCI-L Ignition&lt;br&gt;
MSD Blaster SS Coil&lt;br&gt;
MSD 8.5mm Plug Wires&lt;br&gt;
B&amp;amp;M Short Shifter&lt;br&gt;
Centerforce Clutch&lt;br&gt;
AEM Cold Air Intake&lt;br&gt;
AEM Cam Gear&lt;br&gt;
DC Sports Ceramic Headers&lt;br&gt;
DC Sports Catback Exhaust&lt;br&gt;
Autometer Air/Fuel Ratio Gauge&lt;br&gt;
Autometer Fuel Pressure Gauge (Full sweep electric 0-100psi)&lt;br&gt;
Denso Iridium Spark Plugs&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Suspension:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Eibach Sportline Springs&lt;br&gt;
KYB Gas Struts&lt;br&gt;
Ingalls Camber Correction Kit&lt;br&gt;
Axis Wheel Site Mesh 17-7&lt;br&gt;
BF Goodrich Euro T/A Tires 205-40-17&lt;br&gt;
Powerstop Cross drilled rotors&lt;br&gt;
Axxis Carbon Metallic Pads&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Appearance:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;25% Smoke Tint&lt;br&gt;
Clear lenses (Marker, Bumper, Tail, Side-marker)&lt;br&gt;
Color match painted trim&lt;br&gt;
DC Sports Red Billet Oil Cap&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Stereo / Entertainment:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Alpine 3DA-W882&lt;br&gt;
PPI A600&lt;br&gt;
3 JL Audio 8&amp;quot; W6 Subs in Custom Ported Box&lt;br&gt;
DEI 5401&lt;br&gt;
Boston Acoustic 6-9&amp;quot;&lt;br&gt;
Boston Acoustic 6.3 (Pro Series Component Set)&lt;br&gt;
Lightning Audio Connections (Fuses, Wires, Etc.)&lt;br&gt;
Lightning Cap 1 Farad&lt;br&gt;
Stinger 1 Farad Cap&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Alarm:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Python 3000HF&lt;br&gt;
Proxy/Dual Stage Shock Sensors&lt;br&gt;
1 Mile Pager System&lt;/p&gt;
&lt;p&gt;Check out my very first web-log, &lt;a href="http://mrmatt57.org/1994-Honda-Accord-Journal.html"&gt;My 1994 Honda Accord Journal&lt;/a&gt;**&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Also, check out my Flickr stream for more pictures.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://mrmatt.io/img/accord2.jpg"&gt;&lt;/p&gt;</content:encoded><media:content url="https://mrmatt.io/images/MattWalker-avatar.png" medium="image"/></item><item><title>Swans HiVi - M10 Speakers</title><link>https://mrmatt.io/posts/swans-hivi-m10-speakers/</link><pubDate>Mon, 26 Nov 2007 00:00:00 -0500</pubDate><guid>https://mrmatt.io/posts/swans-hivi-m10-speakers/</guid><description>Review of the Swans HiVi M10 2.1 desktop speakers — studio-quality sound, excellent build quality, and a great option for close-range listening.</description><content:encoded>&lt;p&gt;Well, I finally replaced my workhorse Boston Acoustic BA4800. After much research, I decided on the recently released Swans M10s. I was looking for studio quality sound on a budget. I looked at Mackies, M-Audio and several other 2.1 systems. I really wanted a two speaker system, but could not find anything with excellent sound quality at a prosumer price. The sweet spot for bottom-end studio quality monitors is about $300. So I decided to look at higher-end 2.1 systems and ran right into these M10s.&lt;/p&gt;
&lt;p&gt;After a week of listening, these speakers definitely fit the bill. The imaging and balance is amazing. I had never heard of Swans or the new holding company HiVi. They have built their reputation with the slightly more expensive M200 model. From what I understand; what they lack in customer support, they more than make up for in build and sound quality. So far I have not had to use their customer support channels, but I have been thoroughly enjoying listening to these speakers. The sound is truly amazing.&lt;br&gt;
&lt;a href="https://mrmatt.io/img/swan04.jpg"&gt;&lt;img alt="Swans HiVi M10 Speakers" loading="lazy" src="https://mrmatt.io/img/swan04_thumb.jpg" title="Swans HiVi M10 Speakers"&gt;&lt;/a&gt; &lt;a href="https://mrmatt.io/img/swan01.jpg"&gt;&lt;img alt="Swans HiVi M10 Speakers" loading="lazy" src="https://mrmatt.io/img/swan01_thumb.jpg" title="Swans HiVi M10 Speakers"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://mrmatt.io/img/swan03.jpg"&gt;&lt;img alt="Swans HiVi M10 Speakers" loading="lazy" src="https://mrmatt.io/img/swan03_thumb.jpg" title="Swans HiVi M10 Speakers"&gt;&lt;/a&gt; &lt;a href="https://mrmatt.io/img/swan02.jpg"&gt;&lt;img alt="Swans HiVi M10 Speakers" loading="lazy" src="https://mrmatt.io/img/swan02_thumb.jpg" title="Swans HiVi M10 Speakers"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Pros:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Build Quality - These things are solid.&lt;/li&gt;
&lt;li&gt;Sound Quality - They are not true monitors, but have a balanced and for the most part accurate sound. If you are looking for big boomy bass, these are not the speakers. If you like Jazz, you are in for a treat.&lt;/li&gt;
&lt;li&gt;Packaging - First class packing all the way. They even come in cloth bags, to protect them from scratches.&lt;/li&gt;
&lt;li&gt;Interface - The back of the speaker has RCA connections for easy speaker extensions and stereo hookups. They include a stereo RCA to 3.5 mm Minijack plug.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Cons:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Volume control on sub-woofer - This is not necessarily a negative if the speakers are used as intended.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Summary:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;If you are looking for a close range desktop speaker system, these are a great option. The highs are amazingly crisp, the mids are nicely filled in and the lows are adequate and pronounced.&lt;/p&gt;</content:encoded><media:content url="https://mrmatt.io/images/MattWalker-avatar.png" medium="image"/></item><item><title>Peaks of Otter Camping/Hiking</title><link>https://mrmatt.io/posts/peaks-of-otter-campinghiking/</link><pubDate>Mon, 22 Oct 2007 00:00:00 -0400</pubDate><guid>https://mrmatt.io/posts/peaks-of-otter-campinghiking/</guid><description>Camping and hiking Sharp Top Mountain at Peaks of Otter on the Blue Ridge Parkway in Virginia — trail photos and tips.</description><content:encoded>&lt;p&gt;&lt;img alt="Peaks of Otter, VA" loading="lazy" src="https://mrmatt.io/img/peaks-hiking.jpg"&gt;&lt;/p&gt;
&lt;p&gt;Jaimie and I went camping at Peaks of Otter on the Blue Ridge Parkway in Virginia last weekend. We tent camped at site #21 and Hikes around the lake the first evening. It was a beautiful day and was quite crowded around the lodge. The park is very well maintaned and the staff is very friendly. This was the first trip with our new Mountain Hardware Casa 4 tent. Setup time is a about 2 minutes more than the average tent, but worth the effort. The design offers a roomy interior with a spacious gear vestibule. The next morning we woke up and hiked the sharp top mountain trail. We just about had the trail to ourselves on the way up. At the top, we ran into a group that took the bus up. The views are sweet, you can see a complete 360 degrees. It is also worth the trip over to Buzzards Roost. There are some cool climbing rocks and more breath taking views. Here are some pics:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://mrmatt.io/img/peaks-of-otter-camping-hiking1.jpg"&gt;&lt;img alt="Peaks of Otter Trail" loading="lazy" src="https://mrmatt.io/img/peaks-of-otter-camping-hiking1_t.jpg" title="Peaks of Otter Trail"&gt;&lt;/a&gt;
&lt;a href="https://mrmatt.io/img/peaks-of-otter-camping-hiking3.jpg"&gt;&lt;img alt="Peaks of Otter" loading="lazy" src="https://mrmatt.io/img/peaks-of-otter-camping-hiking3_t.jpg" title="Peaks of Otter"&gt;&lt;/a&gt;
&lt;a href="https://mrmatt.io/img/peaks-of-otter-camping-hiking4.jpg"&gt;&lt;img alt="Peaks of Otter" loading="lazy" src="https://mrmatt.io/img/peaks-of-otter-camping-hiking4_t.jpg" title="Peaks of Otter"&gt;&lt;/a&gt;
&lt;a href="https://mrmatt.io/img/peaks-of-otter-camping-hiking2.jpg"&gt;&lt;img alt="Peaks of Otter" loading="lazy" src="https://mrmatt.io/img/peaks-of-otter-camping-hiking2_t.jpg" title="Peaks of Otter"&gt;&lt;/a&gt;
&lt;a href="https://mrmatt.io/img/peaks-of-otter-camping-hiking5.jpg"&gt;&lt;img alt="Peaks of Otter" loading="lazy" src="https://mrmatt.io/img/peaks-of-otter-camping-hiking5_t.jpg" title="Peaks of Otter"&gt;&lt;/a&gt;
&lt;a href="https://mrmatt.io/img/peaks-of-otter-camping-hiking6.jpg"&gt;&lt;img alt="Peaks of Otter" loading="lazy" src="https://mrmatt.io/img/peaks-of-otter-camping-hiking6_t.jpg" title="Peaks of Otter"&gt;&lt;/a&gt;
&lt;a href="https://mrmatt.io/img/peaks-of-otter-camping-hiking7.jpg"&gt;&lt;img alt="Peaks of Otter" loading="lazy" src="https://mrmatt.io/img/peaks-of-otter-camping-hiking7_t.jpg" title="Peaks of Otter"&gt;&lt;/a&gt;
&lt;a href="https://mrmatt.io/img/peaks-of-otter-camping-hiking8.jpg"&gt;&lt;img alt="Peaks of Otter" loading="lazy" src="https://mrmatt.io/img/peaks-of-otter-camping-hiking8_t.jpg" title="Peaks of Otter"&gt;&lt;/a&gt;&lt;/p&gt;</content:encoded><media:content url="https://mrmatt.io/img/peaks-hiking.jpg" medium="image"/></item><item><title>James Bond Style USB Thumbdrive - IronKey</title><link>https://mrmatt.io/posts/james-bond-style-usb-key-ironkey/</link><pubDate>Sun, 07 Oct 2007 00:00:00 -0400</pubDate><guid>https://mrmatt.io/posts/james-bond-style-usb-key-ironkey/</guid><description>Review of the IronKey 4GB encrypted USB drive — hardware encryption, rugged build, and why it might be the most secure thumb drive available.</description><content:encoded>&lt;p&gt;&lt;img alt="IronKey" loading="lazy" src="https://mrmatt.io/img/ironkey.jpg" title="IronKey"&gt;&lt;/p&gt;
&lt;p&gt;I have been looking around for a good secure USB key/software solution for quite awhile. I have used TrueCrypt for years and it works great for what it does. But I wanted more. One watch of the IronKey demo and believe me, you will want one.&lt;/p&gt;
&lt;p&gt;Boo. IronKey took the video down!&lt;/p&gt;
&lt;p&gt;They are quite pricey at $149 ($37.25/Gig) for the 4 gig model, so I did a bit of research before I took the plunge. Everything I read for the most part was good. Now that I have the device I must agree, it is a great solution to many peoples problems. My only gripe is that you can&amp;rsquo;t add shortcuts to the management interface. Not a big deal, but makes the interface pretty useless besides managing the device, backups, etc. If you are looking for a USB drive to carry on your keychain everywhere you go, this might currently be your best solution. Here are some pictures of the drive.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://mrmatt.io/img/ironkey/ironkey1.jpg"&gt;&lt;img alt="IronKey" loading="lazy" src="https://mrmatt.io/img/ironkey/ironkey1_thumb.jpg" title="IronKey"&gt;&lt;/a&gt;
&lt;a href="https://mrmatt.io/img/ironkey/ironkey2.jpg"&gt;&lt;img alt="IronKey" loading="lazy" src="https://mrmatt.io/img/ironkey/ironkey3_thumb.jpg" title="IronKey"&gt;&lt;/a&gt;
&lt;a href="https://mrmatt.io/img/ironkey/ironkey2.jpg"&gt;&lt;img alt="IronKey" loading="lazy" src="https://mrmatt.io/img/ironkey/ironkey2_thumb.jpg" title="IronKey"&gt;&lt;/a&gt;
&lt;a href="https://mrmatt.io/img/ironkey/ironkey4.jpg"&gt;&lt;img alt="IronKey" loading="lazy" src="https://mrmatt.io/img/ironkey/ironkey4_thumb.jpg" title="IronKey"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The HD Tach results are pretty good for a USB2 thumb drive. I will post my current workstation specs soon. For more information, Digital Reviews has a &lt;a href="http://www.digitalreviews.net/index2.php?option=com_content&amp;amp;task=view&amp;amp;id=298&amp;amp;Itemid=67&amp;amp;pop=1&amp;amp;page=0"&gt;great IronKey review&lt;/a&gt;.&lt;/p&gt;</content:encoded><media:content url="https://mrmatt.io/images/MattWalker-avatar.png" medium="image"/></item><item><title>Coosa Backcountry Trail, Chattahoochee National Forest</title><link>https://mrmatt.io/posts/coosa-backcountry-trail-chattahoochee-national-forest/</link><pubDate>Thu, 20 Sep 2007 00:00:00 -0400</pubDate><guid>https://mrmatt.io/posts/coosa-backcountry-trail-chattahoochee-national-forest/</guid><description>Photos from hiking the Coosa Backcountry Trail near Vogel State Park in Georgia's Chattahoochee National Forest.</description><content:encoded>&lt;p&gt;&lt;img alt="Vogal State Park, GA" loading="lazy" src="https://mrmatt.io/img/vogel.jpg"&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;these picks are from last October in Vogal State Park, Georgia.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a href="https://mrmatt.io/img/coosa-backcountry-trail.jpg"&gt;&lt;img alt="Coosa Trail" loading="lazy" src="https://mrmatt.io/img/coosa-backcountry-trail_t.jpg" title="Coosa Trail"&gt;&lt;/a&gt;
&lt;a href="https://mrmatt.io/img/coosa-backcountry-trail1.jpg"&gt;&lt;img alt="Coosa Trail" loading="lazy" src="https://mrmatt.io/img/coosa-backcountry-trail1_t.jpg" title="Coosa Trail"&gt;&lt;/a&gt;
&lt;a href="https://mrmatt.io/img/coosa-backcountry-trail2.jpg"&gt;&lt;img alt="Coosa Trail" loading="lazy" src="https://mrmatt.io/img/coosa-backcountry-trail2_t.jpg" title="Coosa Trail"&gt;&lt;/a&gt;
&lt;a href="https://mrmatt.io/img/coosa-backcountry-trail3.jpg"&gt;&lt;img alt="Coosa Trail" loading="lazy" src="https://mrmatt.io/img/coosa-backcountry-trail3_t.jpg" title="Coosa Trail"&gt;&lt;/a&gt;
&lt;a href="https://mrmatt.io/img/coosa-backcountry-trail4.jpg"&gt;&lt;img alt="Coosa Trail" loading="lazy" src="https://mrmatt.io/img/coosa-backcountry-trail4_t.jpg" title="Coosa Trail"&gt;&lt;/a&gt;
&lt;a href="https://mrmatt.io/img/coosa-backcountry-trail5.jpg"&gt;&lt;img alt="Coosa Trail" loading="lazy" src="https://mrmatt.io/img/coosa-backcountry-trail5_t.jpg" title="Coosa Trail"&gt;&lt;/a&gt;&lt;/p&gt;</content:encoded><media:content url="https://mrmatt.io/images/MattWalker-avatar.png" medium="image"/></item><item><title>Life after Lasik!</title><link>https://mrmatt.io/posts/life-after-lasik/</link><pubDate>Thu, 20 Sep 2007 00:00:00 -0400</pubDate><guid>https://mrmatt.io/posts/life-after-lasik/</guid><description>Two weeks after LASIK surgery — vision improving every day and wishing I hadn't waited so long to get it done.</description><content:encoded>&lt;p&gt;It has been two weeks since my LASIK surgery and I can&amp;rsquo;t believe I waited this long to have it done. My vision is still a little hazy but every day it gets sharper and sharper. It is absolutely amazing. If you are still wearing glasses, don&amp;rsquo;t wait another day - get it done!&lt;/p&gt;</content:encoded><media:content url="https://mrmatt.io/images/MattWalker-avatar.png" medium="image"/></item><item><title>Building a LiPo Charging Station</title><link>https://mrmatt.io/posts/building-a-lipo-charging-station/</link><pubDate>Fri, 03 Aug 2007 00:00:00 -0400</pubDate><guid>https://mrmatt.io/posts/building-a-lipo-charging-station/</guid><description>Building a safe LiPo battery charging station for RC planes using a Honeywell lockbox, Pyrex pan, and charging bag.</description><content:encoded>&lt;p&gt;&lt;img alt="Focal Intent" loading="lazy" src="https://mrmatt.io/img/lipo-station.jpg"&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Photo by &lt;a href="https://www.flickr.com/photos/focalintent/1333439650/"&gt;-Focal Intent-&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;If you haven&amp;rsquo;t seen/heard about it yet, LiPo batteries can be dangerous, &lt;a href="https://www.youtube.com/results?search_query=lipo+fire&amp;amp;search=Search"&gt;check it out&lt;/a&gt;. It is very rare that a lipo would ignite under normal use. It would take charging it at a higher rate than recommended (typically 1C) or a internal short. People have had serious property damage, &lt;a href="https://www.rcgroups.com/forums/showthread.php?t=209187"&gt;here&lt;/a&gt; is a list of what causes the fire and what is damaged. They make commercial battery bunkers, some people use cinder blocks and bags of sand. I have also seen ammo cans used, but I could not find them locally and I didn&amp;rsquo;t want to pay for shipping. I found a Honeywell steel lockbox at WalMart for $10, that is pretty stout. I don&amp;rsquo;t have the desire to test it (yet), but I think it should hold just fine. Also to help suppress any fire that could arise, I put a Pyrex pan over the battery. I did buy one of those LiPo charging bags, but it is a pain to open and close every time I want to charge a battery. I have it lining the bottom of the box. It is definitely not necessary, but since I have it, why not. The way I see it, all this effort is the cheapest fire insurance available.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://mrmatt.io/img/lipo-battery-charging1.jpg"&gt;&lt;img alt="Battery Charging Station" loading="lazy" src="https://mrmatt.io/img/lipo-battery-charging1_t.jpg" title="Battery Charging Station"&gt;&lt;/a&gt;
&lt;a href="https://mrmatt.io/img/lipo-battery-charging2.jpg"&gt;&lt;img alt="Battery Charging Station 2" loading="lazy" src="https://mrmatt.io/img/lipo-battery-charging2_t.jpg" title="Battery Charging Station 2"&gt;&lt;/a&gt;
&lt;a href="https://mrmatt.io/img/lipo-battery-charging3.jpg"&gt;&lt;img alt="Battery Charging Station 3" loading="lazy" src="https://mrmatt.io/img/lipo-battery-charging3_t.jpg" title="Battery Charging Station 3"&gt;&lt;/a&gt;
&lt;a href="https://mrmatt.io/img/lipo-battery-charging4.jpg"&gt;&lt;img alt="Battery Charging Station 4" loading="lazy" src="https://mrmatt.io/img/lipo-battery-charging4_t.jpg" title="Battery Charging Station 4"&gt;&lt;/a&gt;&lt;/p&gt;</content:encoded><media:content url="https://mrmatt.io/images/MattWalker-avatar.png" medium="image"/></item><item><title>Terminal Velocity Pentax Optio S6</title><link>https://mrmatt.io/posts/terminal-velocity-pentax-optio-s6/</link><pubDate>Fri, 20 Jul 2007 00:00:00 -0400</pubDate><guid>https://mrmatt.io/posts/terminal-velocity-pentax-optio-s6/</guid><description>Lessons learned the hard way — my Pentax Optio S6 camera fell off my RC plane mid-dive and was destroyed on impact.</description><content:encoded>&lt;p&gt;&lt;img alt="Lucky Lightning Shot" loading="lazy" src="https://mrmatt.io/img/terminal-velocity.jpg"&gt;&lt;/p&gt;
&lt;p&gt;I learned two very valuable lessons today-&lt;/p&gt;
&lt;p&gt;1.) Don&amp;rsquo;t rush sending electronics airborne&lt;br&gt;
2.) When attaching &lt;strong&gt;anything&lt;/strong&gt; to a RC plane, make sure it&amp;rsquo;s on there good.&lt;/p&gt;
&lt;p&gt;I had so much fun the other day taking the video with my Soarstar, I rushed out there today and look what I did-&lt;/p&gt;
&lt;p&gt;&lt;img alt="Pentax Optio S6 - Terminal Velocity 1" loading="lazy" src="https://mrmatt.io/img/pentax-optio-s6_1.jpg"&gt;
&lt;img alt="Pentax Optio S6 - Terminal Velocity 1" loading="lazy" src="https://mrmatt.io/img/pentax-optio-s6_2.jpg"&gt;
&lt;img alt="Pentax Optio S6 - Terminal Velocity 1" loading="lazy" src="https://mrmatt.io/img/pentax-optio-s6_3.jpg"&gt;
&lt;img alt="Pentax Optio S6 - Terminal Velocity 1" loading="lazy" src="https://mrmatt.io/img/pentax-optio-s6_4.jpg"&gt;
The last time, I attached the camera on top of the wing, in the center facing forward. It worked well, but I noticed after the flight, I was blocking the air flow to the motor and it got pretty hot. So I figured I would try attaching it under the wing, facing down to hopefully get better footage and not overheat the motor. Everything worked great. I took it waaayyy up there almost so I could tell the orientation of the plane. As I was descending I got impatient and did a dive. I have done this before, but not with a camera strapped to the plane. When I pulled back, it took longer than usual to level out. As it leveled out I noticed a little black dot falling from the plane crap, it was the camera. I followed it to the ground and watched it bounce about 20 feet back into the air. It ran to where it fell, about 50 yards for some reason hoping it would be ok, lol. It was destroyed, and I didn&amp;rsquo;t even capture the fall, because it shorted out and didn&amp;rsquo;t save anything to the memory card. I guess that is the end of my aerial video for awhile. So if you ever find the urge to strap a camera to an airplane, take your time and make sure it&amp;rsquo;s on there good.&lt;/p&gt;</content:encoded><media:content url="https://mrmatt.io/img/terminal-velocity.jpg" medium="image"/></item><item><title>Soarstar RC Video (First Attempt)</title><link>https://mrmatt.io/posts/hello-world/</link><pubDate>Tue, 17 Jul 2007 00:00:00 -0400</pubDate><guid>https://mrmatt.io/posts/hello-world/</guid><description>First attempt at strapping a Pentax Optio S6 camera to my Soarstar RC plane for aerial video footage.</description><content:encoded>&lt;p&gt;&lt;img alt="Soarstar" loading="lazy" src="https://mrmatt.io/img/soarstar.jpg"&gt;&lt;/p&gt;
&lt;p&gt;I had been flying my Soarstar for a couple of months and finally decided to attach my Pentax Optio S6 camera to it. The easiest setup was to just strap it down and record video. Eventually I would like to set up a servo to take pictures. These cameras are not great at shooting video, but here it is:&lt;/p&gt;
&lt;div class="videoWrapper"&gt;
 &lt;iframe title="Soarstar RC Video (First Attempt) HIRES" width="1280" height="720" src="https://www.youtube.com/embed/GL5vE61UHjs?rel=0&amp;amp;controls=0" frameborder="0" allowfullscreen&gt;&lt;/iframe&gt;
&lt;/div&gt;</content:encoded><media:content url="https://mrmatt.io/images/MattWalker-avatar.png" medium="image"/></item><item><title>AirHogs Just Not Enough Anymore</title><link>https://mrmatt.io/posts/airhogs-just-not-enough-anymore/</link><pubDate>Fri, 29 Jun 2007 00:00:00 -0400</pubDate><guid>https://mrmatt.io/posts/airhogs-just-not-enough-anymore/</guid><description>Moving beyond AirHogs to a Spektrum DX6 transmitter, FSOne flight simulator, and building my first real RC plane — a SoarStar.</description><content:encoded>&lt;p&gt;I have had a lot of fun with them, but I&amp;rsquo;m just itching for more. My biggest desire is to have control surfaces. You can move the rudder and elevators on the AirHogs, but not in-flight. I have been doing quite a bit of research online and finally committed to the next step. In reading most of the -Beginners Guides- they recommend joining a local flying club and learning with a buddy box (where you can tether your transmitter to an instructor). I am sure this is a great way to learn, but this is the digital age right? Yup, they have several RC flight simulators that are very realistic. The most popular is Great Planes Realflight. However, there is a new kid on the block. Horizon Hobbies has built FSOne. Apparently their physics engine is unmatched. You can get the software with a USB transmitter or with a USB adapter for a real transmitter. I decided, why not get a real transmitter and learn on that. So, I went to the LHS (Local Hobby Shop) and picked up a Spektrum DX6 Transmitter and ordered the flight-sim online. They had a Real-Flight kiosk, but something about all the -add-ons- really didn&amp;rsquo;t appeal to me.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Specktrum DX6 Transmitter" loading="lazy" src="https://mrmatt.io/img/spektrumdx6.jpg"&gt;
&lt;img alt="FSOne Flight Sim" loading="lazy" src="https://mrmatt.io/img/fsone.jpg"&gt;
When I bought the Transmitter, the guy at the hobby shop talked me into buying a plane to -actually fly-. I wasn&amp;rsquo;t planning on getting a plane until I tried several models in the simulator. I am glad I did though, I didn&amp;rsquo;t even think about the building part of the hobby. He recommended a SoarStar, it is a simple 3 channel plane that is easy to build. It was a great exercise to get familiar with servos, linkage, epoxy, esc (electronic speed controllers), receivers and working with foam. I really learned a lot. I found out really quick that the batteries that the transmitter came with wasn&amp;rsquo;t going to work. It had a trickle charger without peak sensing and it was only 600mh. I found some rechargeable AA NiMh-s on eBay for really cheap and found a battery tray from RadioShack that fits in the transmitter. 8 AA-s work out to the same voltage and have 2400mah of juice.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Transmitter Battery Pack" loading="lazy" src="https://mrmatt.io/img/transmitter-battery.jpg"&gt;
Before I figured the Transmitter batteries out I thought oh no- I should have got the Simulator that came with the transmitter. There would be nothing worse than having the transmitter die in the middle of a flying session. The point of the simulator is to learn and spend more time flying than building, fixing, charging, etc. Turns out it didn&amp;rsquo;t matter anyway. When the transmitter is plugged into the USB converter, it draws a small amount of current. Not enough to charge the battery, but enough to keep it at the same voltage (or so it seems).I haven&amp;rsquo;t flown the SoarStar yet, I am still logging some time on the simulator. It is not easy, but it&amp;rsquo;s a lot of fun. I have saved at least a thousand dollars in all the planes I would have wrecked.&lt;/p&gt;</content:encoded><media:content url="https://mrmatt.io/images/MattWalker-avatar.png" medium="image"/></item><item><title>AirHogs are a blast!</title><link>https://mrmatt.io/posts/airhogs-are-a-blast/</link><pubDate>Wed, 27 Jun 2007 00:00:00 -0400</pubDate><guid>https://mrmatt.io/posts/airhogs-are-a-blast/</guid><description>Comparing AirHogs AeroAce models and figuring out which backyard RC planes are worth buying and which to avoid.</description><content:encoded>&lt;p&gt;I have been researching the different models of AirHogs and it is quite difficult to see what they actually offer. The AirHogs site is horrible. All I have to say is infomercials and the web don&amp;rsquo;t mix. They just don&amp;rsquo;t have any information on the different models. It appears that all of the ones setup with the two props are the AeroAce model. They have a BiPlane, Jet and Diamond Aircraft look alike. I have found that the other models are no good. I tried the F-22 -Park- jet and it was not much fun. It used a weird pulse for throttle control/roll and the aerodynamics/glide were really non-existent. Estes makes a few nice models, but steer clear of the larger -Park- fliers. Stick with the ones labeled -Backyard-.&lt;/p&gt;</content:encoded><media:content url="https://mrmatt.io/images/MattWalker-avatar.png" medium="image"/></item><item><title>Getting Started With RC Airplanes</title><link>https://mrmatt.io/posts/getting-started-with-rc-airplanes/</link><pubDate>Fri, 15 Jun 2007 00:00:00 -0400</pubDate><guid>https://mrmatt.io/posts/getting-started-with-rc-airplanes/</guid><description>How I got into RC airplanes starting with a $30 AirHogs Aero Ace from Walmart — the best toy for the price.</description><content:encoded>&lt;p&gt;I have always been a fan of radio control; cars, robots, computers now planes. As a child I had several RC Cars that were a bunch of fun. At the time airplanes were too expensive and the technology was not all that great. I had left RC altogether for the past 10 years or so.&lt;/p&gt;
&lt;p&gt;Recently my Dad sent me an AirHogs Aero Ace and boy I am glad he did. They are a ton of fun. You can get them at WalMart or Target for $30. Without a doubt the best toy for the price. They have a 2-axis controler for throttle and lef-right. There are two small motors setup in a push configuration. The throttle controls the prop speed and the left-right governs the speed to allow it to pitch side to side. They fly great and take a lot of abuse. Here are some pictures:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://mrmatt.io/img/rc-airplanes1.jpg"&gt;&lt;img alt="AirHogs AeroAce" loading="lazy" src="https://mrmatt.io/img/rc-airplanes1_t.jpg" title="AirHogs AeroAce"&gt;&lt;/a&gt;
&lt;a href="https://mrmatt.io/img/rc-airplanes2.jpg"&gt;&lt;img alt="AirHogs Aero Ace Pic 2" loading="lazy" src="https://mrmatt.io/img/rc-airplanes2_t.jpg" title="AirHogs Aero Ace Pic 2"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;As you can see it can take some abuse. The plane itself charges from the 4 AA batteries in the transmitter. It is an amazing use of technology. More to follow.&lt;/p&gt;</content:encoded><media:content url="https://mrmatt.io/images/MattWalker-avatar.png" medium="image"/></item><item><title>Blue Backlit Keyboard</title><link>https://mrmatt.io/photography/2006-12-22-blue-backlit-keyboard/</link><pubDate>Fri, 22 Dec 2006 00:00:00 -0500</pubDate><guid>https://mrmatt.io/photography/2006-12-22-blue-backlit-keyboard/</guid><description>Late-night desk setup with a blue-backlit keyboard casting a cool glow across the workspace. The kind of lighting that made you feel like a hacker in 2006.</description><content:encoded/><media:content url="https://mrmatt.io/photography/2006-12-22-blue-backlit-keyboard/photo_hu_d8e99782e9594542.webp" medium="image"/></item><item><title>Bar Harbor Sunrise</title><link>https://mrmatt.io/photography/2003-07-14-bar-harbor-sunrise/</link><pubDate>Mon, 14 Jul 2003 00:00:00 -0400</pubDate><guid>https://mrmatt.io/photography/2003-07-14-bar-harbor-sunrise/</guid><description>Dawn breaks over Bar Harbor with stunning bands of color sweeping across the sky—from deep purple and pink above to warm orange and gold near the horizon. The islands of Frenchman Bay emerge as dark silhouettes beneath the glowing clouds, creating a serene and painterly scene.</description><content:encoded/><media:content url="https://mrmatt.io/photography/2003-07-14-bar-harbor-sunrise/photo_hu_310c4a3ea37cb855.webp" medium="image"/></item></channel></rss>