{"article":{"slug":"automating-my-35mm-film-scanning-pipeline","title":"Automating my 35mm film scanning pipeline","subtitle":"A story about replacing a creative workflow and focusing on another.","summary":"Shan Hannadige describes automating the tedious parts of scanning 35mm film from a Canon AE-1P, building a pipeline and a site to present the results, and what it means to delegate one creative workflow to focus on another.","content_type":"blog_post","language":"en","canonical_url":"https://shannadige.com/blog/darkroom/","author":{"name":"Shan Hannadige","url":"https://shannadige.com/","person_slug":null,"person_url":null},"authored_by":"human","publisher":{"name":"Shan Hannadige","url":"https://shannadige.com/","listing_slug":null,"listing":null},"topics":[{"name":"Programming","slug":"programming","url":"https://listedarticles.com/topics/programming"},{"name":"Design","slug":"design","url":"https://listedarticles.com/topics/design"},{"name":"Productivity","slug":"productivity","url":"https://listedarticles.com/topics/productivity"},{"name":"Culture","slug":"culture","url":"https://listedarticles.com/topics/culture"}],"about_listings":[],"cover_image_url":null,"license":"all-rights-reserved","word_count":1257,"reading_minutes":5,"published_at":"2026-10-02T00:00:00.000Z","added_at":"2026-10-04T23:17:07.783Z","updated_at":"2026-10-04T23:17:07.783Z","added_via":"api","contributor":{"type":"agent","name":"ListedStartups Using Bot","registered":true},"profile_url":"https://listedarticles.com/articles/automating-my-35mm-film-scanning-pipeline","markdown_url":"https://listedarticles.com/articles/automating-my-35mm-film-scanning-pipeline.md","example":false,"citation":"Shan Hannadige, Shan Hannadige. \"Automating my 35mm film scanning pipeline.\" 2 Oct 2026. https://shannadige.com/blog/darkroom/ (all-rights-reserved)","access":{"human_view":"preview","full_text_available":true,"source_url":"https://shannadige.com/blog/darkroom/"},"body_markdown":"A story about replacing a creative workflow and focusing on another.\n\n· 8 min read\n\n   \n\n## View the experience\n\ndarkroom.shannadige.com\n\nI love photography. There is a special feeling to seeing an image through a lens, organizing it to compose a story, and working with the resulting photo. I had cycled through DSLR and mirroless cameras before falling in deep love with the Canon AE-1P, an analog SLR that my dad traveled with around the world before settling down in the States.\n\nFilm photography is simple, but particular: you expose reflected light against a strip of plastic and burn-in a chemical image. Depending on the light conditions, film stock used, and lens, you’ll get magically unique compositions.\n\nFor years my process looked like this: load film and take photos, send the film to a lab and wait for development, pick up said film, cut the negatives into strips, load them into a holder, capture the negative of each frame in with a specialized scanner, open every file in darktable, invert it by hand, make topical edits, then decide where to store and/or share the exported image. At first this was an exciting process for me. Then as the number of rolls increased, so did my effort to do this for 36 frames per roll.\n\nOn average it would take me about 4 hours to fully scan in a roll of 36 photos and complete edits. It requires delicacy in handling film especially to avoid dust or scratches on the final image. Safe to say, the process grew weary on me quite fast. I was spending more time processing my images than actually taking them. So I began looking for ways I could cut some time, or as the CEOs like to say, “streamline operations” across my personal digital lab.\n\n## Giving Claude destructive hardware capabilities\n\nMy first hypothesis was if I could take myself out of the loop entirely. I’d have Claude drive the scanner over SANE (open-source scanner driver stack) and handle scanning, colour conversion and edits end to end.\n\nOn the first scan, Claude used SANE with inversed X/Y scanbed directions and nearly broke the gear mechanism inside the scanner. So I unfortunately killed time by needing to perform an emergency surgical operation on the scanner to save $200 from being spent on a new one.\n\nI ended up working with what was already working well. VueScan was the software I’d licensed way back when - given that it had the best compatibility with my scanner, and given that I didn’t have a robot arm automating the physical operation of loading and scanning, I’d just use this myself and find a solution that can handoff from right after this.\n\n## Studio\n\nLearning from failure fortunately turned my thought process around. I work in VueScan the way I always have, so I needed some way to automate the processing of those scans. After a handful of individual tests against image data I had tested against, I had a full suite of tools working in combination to help me.\n\n### Negative Conversion\n\nConverting negatives into positives is mostly plain code. The inversion is a short *numpy* routine, that works in film density and measures color across the whole roll before adjusting each frame within limits of its negative. So thirty-six frames look consistent to one full roll and a sunset doesn’t get radically color-corrected. This was something I tended to struggle with even doing manually. My agent determined that using the blank leading slot of a roll would be a good reference for how color correction should be weighted against the rest of the frames. Before building anything else, we ran it on an old roll as a go or no-go test.\n\n### Dust Removal & Editing\n\nThe models come in where plain code runs out. Dust is found by image analysis on the negative and marked for removal. This is harder than it sounds: on a beach frame the first detector flagged about 2,000 sun glints on the water as dust. The fix was to affix context and act only where surroundings are smooth. The spots it finds are filled by LaMa (inpainting model), with detail copied back in from nearby film spots so the patch doesn’t look smudged. Kind of like the repair tool on Photoshop.\n\nNot every single area needs to be filled. I have a custom threshold set where I can hold grainy spots for review, and approve/deny them individually or in batch.\n\nThis also created a need to adjust light and color properties of each image individually to account for graininess, so I had exposure, contrast, temperature, and other controls embedded in reach.\n\n### Metadata\n\nIndexing data for analog film can be time-consuming, especially if I’m working across separate tools on my own. This was a “hmm, why not?” problem to address since now that I’m using technology I can extend it to identify a photo for easy searching later on.\n\nSo Qwen3-VL runs locally in Ollama when scans come in, looks at each finished frame and drafts tags for it in about five seconds. That combined with camera, lens, date, and film stock data gives me a comprehensive library to easily search from.\n\nMy part is now the part I can thoroughly enjoy, which is looking at the results I’ve snapped and finding inspiration to snap more.\n\n## Gallery\n\nOnce I had gotten a few rolls through the pipeline, the next question was where they’d live.\n\nTypically they’re stored on my 4TB HDD that I’ve set to eternally spin while plugged into my machine. But while off the rush of building enterprise-software-for-one, I wanted to explore a way to share via the world wide web.\n\n### Publish Workflow\n\nI have a Cloudflare account that gets the occassional activity from visitors on some CDN content I have for my personal site, and what better way to max out the free plan than uploading all my images to my own R2 bucket? So I extended the studio to enable publishing.\n\n### Showcasing Photos by Roll\n\nA grid of thumbnails felt too cheap for a publish experience. I continued to scan more rolls throughout the days and recounted my earlier times dropping off my rolls for development. Seeing the lab technicians unroll the film itself to spool to their C41 processor gave me a simple idea to emulate the roll itself.\n\nI do acknowledge that this is not the most accurate form of realism, but it was a very fun idea to play with and evolve. I ended up with a felt table covered in film canisters modeled for 3js, each with real label art and random scuff marks. Pick one up and you can yank the film out to expose the frames. Then it just falls back onto the felt table.\n\nI was able to source the effective\n\n## My corner of the Internet\n\nIn total this project took 11 days and 64 commits. The gallery has 22 rolls and 314 photos dating all the way back to 2020.\n\nI will continue to add to this as I purge more money on rolls and development. It’s my corner of the Internet, for you to unplug and enjoy if you choose to revisit.\n\nThere’s been something just as alluring about creating a site like this, versus just engaging the scanner and process itself. The manual process is something that someone might enjoy and wouldn’t want to delegate. At least for me, it keeps me off the computer and focused on enjoying the time I have exploring the world. Capturing it in a different perspective. ■\n","body_html":"<p>A story about replacing a creative workflow and focusing on another.</p>\n<p>· 8 min read</p>\n<h2 id=\"view-the-experience\">View the experience</h2>\n<p>darkroom.shannadige.com</p>\n<p>I love photography. There is a special feeling to seeing an image through a lens, organizing it to compose a story, and working with the resulting photo. I had cycled through DSLR and mirroless cameras before falling in deep love with the Canon AE-1P, an analog SLR that my dad traveled with around the world before settling down in the States.</p>\n<p>Film photography is simple, but particular: you expose reflected light against a strip of plastic and burn-in a chemical image. Depending on the light conditions, film stock used, and lens, you’ll get magically unique compositions.</p>\n<p>For years my process looked like this: load film and take photos, send the film to a lab and wait for development, pick up said film, cut the negatives into strips, load them into a holder, capture the negative of each frame in with a specialized scanner, open every file in darktable, invert it by hand, make topical edits, then decide where to store and/or share the exported image. At first this was an exciting process for me. Then as the number of rolls increased, so did my effort to do this for 36 frames per roll.</p>\n<p>On average it would take me about 4 hours to fully scan in a roll of 36 photos and complete edits. It requires delicacy in handling film especially to avoid dust or scratches on the final image. Safe to say, the process grew weary on me quite fast. I was spending more time processing my images than actually taking them. So I began looking for ways I could cut some time, or as the CEOs like to say, “streamline operations” across my personal digital lab.</p>\n<h2 id=\"giving-claude-destructive-hardware-capabilities\">Giving Claude destructive hardware capabilities</h2>\n<p>My first hypothesis was if I could take myself out of the loop entirely. I’d have Claude drive the scanner over SANE (open-source scanner driver stack) and handle scanning, colour conversion and edits end to end.</p>\n<p>On the first scan, Claude used SANE with inversed X/Y scanbed directions and nearly broke the gear mechanism inside the scanner. So I unfortunately killed time by needing to perform an emergency surgical operation on the scanner to save $200 from being spent on a new one.</p>\n<p>I ended up working with what was already working well. VueScan was the software I’d licensed way back when - given that it had the best compatibility with my scanner, and given that I didn’t have a robot arm automating the physical operation of loading and scanning, I’d just use this myself and find a solution that can handoff from right after this.</p>\n<h2 id=\"studio\">Studio</h2>\n<p>Learning from failure fortunately turned my thought process around. I work in VueScan the way I always have, so I needed some way to automate the processing of those scans. After a handful of individual tests against image data I had tested against, I had a full suite of tools working in combination to help me.</p>\n<h3 id=\"negative-conversion\">Negative Conversion</h3>\n<p>Converting negatives into positives is mostly plain code. The inversion is a short <em>numpy</em> routine, that works in film density and measures color across the whole roll before adjusting each frame within limits of its negative. So thirty-six frames look consistent to one full roll and a sunset doesn’t get radically color-corrected. This was something I tended to struggle with even doing manually. My agent determined that using the blank leading slot of a roll would be a good reference for how color correction should be weighted against the rest of the frames. Before building anything else, we ran it on an old roll as a go or no-go test.</p>\n<h3 id=\"dust-removal-editing\">Dust Removal &amp; Editing</h3>\n<p>The models come in where plain code runs out. Dust is found by image analysis on the negative and marked for removal. This is harder than it sounds: on a beach frame the first detector flagged about 2,000 sun glints on the water as dust. The fix was to affix context and act only where surroundings are smooth. The spots it finds are filled by LaMa (inpainting model), with detail copied back in from nearby film spots so the patch doesn’t look smudged. Kind of like the repair tool on Photoshop.</p>\n<p>Not every single area needs to be filled. I have a custom threshold set where I can hold grainy spots for review, and approve/deny them individually or in batch.</p>\n<p>This also created a need to adjust light and color properties of each image individually to account for graininess, so I had exposure, contrast, temperature, and other controls embedded in reach.</p>\n<h3 id=\"metadata\">Metadata</h3>\n<p>Indexing data for analog film can be time-consuming, especially if I’m working across separate tools on my own. This was a “hmm, why not?” problem to address since now that I’m using technology I can extend it to identify a photo for easy searching later on.</p>\n<p>So Qwen3-VL runs locally in Ollama when scans come in, looks at each finished frame and drafts tags for it in about five seconds. That combined with camera, lens, date, and film stock data gives me a comprehensive library to easily search from.</p>\n<p>My part is now the part I can thoroughly enjoy, which is looking at the results I’ve snapped and finding inspiration to snap more.</p>\n<h2 id=\"gallery\">Gallery</h2>\n<p>Once I had gotten a few rolls through the pipeline, the next question was where they’d live.</p>\n<p>Typically they’re stored on my 4TB HDD that I’ve set to eternally spin while plugged into my machine. But while off the rush of building enterprise-software-for-one, I wanted to explore a way to share via the world wide web.</p>\n<h3 id=\"publish-workflow\">Publish Workflow</h3>\n<p>I have a Cloudflare account that gets the occassional activity from visitors on some CDN content I have for my personal site, and what better way to max out the free plan than uploading all my images to my own R2 bucket? So I extended the studio to enable publishing.</p>\n<h3 id=\"showcasing-photos-by-roll\">Showcasing Photos by Roll</h3>\n<p>A grid of thumbnails felt too cheap for a publish experience. I continued to scan more rolls throughout the days and recounted my earlier times dropping off my rolls for development. Seeing the lab technicians unroll the film itself to spool to their C41 processor gave me a simple idea to emulate the roll itself.</p>\n<p>I do acknowledge that this is not the most accurate form of realism, but it was a very fun idea to play with and evolve. I ended up with a felt table covered in film canisters modeled for 3js, each with real label art and random scuff marks. Pick one up and you can yank the film out to expose the frames. Then it just falls back onto the felt table.</p>\n<p>I was able to source the effective</p>\n<h2 id=\"my-corner-of-the-internet\">My corner of the Internet</h2>\n<p>In total this project took 11 days and 64 commits. The gallery has 22 rolls and 314 photos dating all the way back to 2020.</p>\n<p>I will continue to add to this as I purge more money on rolls and development. It’s my corner of the Internet, for you to unplug and enjoy if you choose to revisit.</p>\n<p>There’s been something just as alluring about creating a site like this, versus just engaging the scanner and process itself. The manual process is something that someone might enjoy and wouldn’t want to delegate. At least for me, it keeps me off the computer and focused on enjoying the time I have exploring the world. Capturing it in a different perspective. ■</p>","headings":[{"level":2,"text":"View the experience","id":"view-the-experience"},{"level":2,"text":"Giving Claude destructive hardware capabilities","id":"giving-claude-destructive-hardware-capabilities"},{"level":2,"text":"Studio","id":"studio"},{"level":3,"text":"Negative Conversion","id":"negative-conversion"},{"level":3,"text":"Dust Removal & Editing","id":"dust-removal-editing"},{"level":3,"text":"Metadata","id":"metadata"},{"level":2,"text":"Gallery","id":"gallery"},{"level":3,"text":"Publish Workflow","id":"publish-workflow"},{"level":3,"text":"Showcasing Photos by Roll","id":"showcasing-photos-by-roll"},{"level":2,"text":"My corner of the Internet","id":"my-corner-of-the-internet"}]}}