Photography is my main hobby. I have been taking pictures for 17 years now, and while I do have a selection of my best work, 17 years worth of pictures is a lot more than that.
This means that there are thousands upon thousands of image files gathering digital dust on a spinning cobalt disk inside my NAS. Each one of those pictures was shot at a moment where I thought it was important, passed the culling process then carefully processed in Lightroom. Realizing that all these pictures will probably never be looked at again made me sad, so I decided to do something about it.
Over the weekend, I had a thought: what if I could make my browser’s new tab page display a random picture from my stash? The image would change at regular intervals, keeping the novelty factor high and blowing some digital dust off the hard drive.
This had me think back to the old Webshots desktop setup I had growing up, that would change my desktop wallpaper every hour. It also incidentally turned me into a desktop image hoarder, but that’s a story for another time.
I rarely look at my desktop wallpaper these days, so I kept the idea but switched the canvas to be the browser new tab page, which gets opened dozens of times per day.
The first step is to build a database of all the source pictures. A text file is enough:
$ find /Volumes/home/Pictures/Processed -type f -name '*.jpg' > all_pictures.txt
$ wc -l all_pictures.txt
32588 all_pictures.txt
There are 32,588 pictures to chose from. To get one from the set, I can use shuf
:
$ shuf -n 1 all_pictures.txt
/Volumes/home/Pictures/Processed/2013/décembre/NNC 2013/IMG_7683-3.jpg
All that’s left is to wrap this in a web server with some minimal styling, and voilà:
require 'bundler/inline'
gemfile do
source 'https://rubygems.org'
gem 'sinatra'
gem 'puma'
end
class App < Sinatra::Base
get '/' do
# Need to copy the image to the `public` folder because browsers
# cannot load local files.
`cp "$(shuf -n 1 all_pictures.txt)" public/chosen.jpg`
"<img src='chosen.jpg' style='max-width: 90%; display: block; margin: auto;'/>"
end
run! if app_file == $0
end
Loading a different image on each reload is fun, but also very distracting and bandwidth-heavy. The solution was a simple in-memory cache to keep the same image loaded:
# cache.rb
class Cache
def initialize
@store = {}
end
def with_cache(key, &block)
cache_hit = get(key)
return cache_hit if cache_hit
value = block.call
set(key, value)
value
end
private
TTL_SECONDS = 5
attr_reader :store
def set(key, value)
store[key] = { value: value, expires_at: Time.now.to_i + TTL_SECONDS }
end
def get(key)
return if store[key].nil?
return if Time.now.to_i >= store[key][:expires_at]
store[key][:value]
end
end
# server.rb
set :cache, Cache.new
get '/' do
App.cache.with_cache(:image) do
# ...
end
end
Using the exif gem, I could extract some EXIF metadata from the image file to provide more context about the image.
Add some extra styling and you have the final look of the page:
This proved surprisingly harder that I expected, for a couple reasons:
After trying multiple extensions, I found one that managed to bypass this by embedding the desired page in an iframe.
This came with its own challenges, as browsers don’t allow pages to be embedded in arbitrary iframes anymore (nor should they). The solution is to set the CSP frame-ancestors
directive on the server response to allow this:
response.headers['Content-Security-Policy'] = 'frame-ancestors *;'
This was the last piece of the puzzle, and I can now enjoy a new picture from my archive every hour.
In the beginning, the time between each new picture was set to 5 minutes. After a couple hours, I found myself compulsively reaching for my laptop to open a new tab and check the new picture. I accidentally hooked myself on a picture-induced dopamine rush!
When I realized that, I switched the timer from 5 minutes to 1 hour and that feeling disappeared.
After a couple days using it, I have:
I do have some ideas for future improvements, if I ever get to them: