You may have noticed a few changes around these parts recently. Welcome to the 2024 site re-design. Gone is the tailwindcss grid and columns, in favour of a pure pixel grid based layout. With lots of ideas planned for future updates, we're rolling back the clock and going to re-design like it's 2004 again.
Firstly, A bit of history
I've been building on the web since the late 1990's. I've seen the rise of great powers, and fall of others. I was there for spacer.gifs and tables for layouts. I've used <FONT> tags in FrontPage Express and wrangled with DHTML in Netscape Navigator. I saw in the dawn of CSS, the Zen Garden, and the Semantic Web. I've given up trying to keep up with latest in javascript frameworks, and now I yearn of a simpler website.
When Bootstrap hit the scene in 2011, it was revolutionary. Suddenly web design became easier and, complex responsive designs could be built very quickly, and an entire ecosystem of components and functionality became available. But, Bootstrap came with a dark reality, nearly all the sites built with bootstrap ended up looking like a site built with bootstrap. At some point, I used bootstrap to build this site. It made sense, I'm not a designer, I'm not a frontend engineer. I needed something that I knew would work and I would be easy to build with. But I was never truly happy with the outcome.
And so things mostly stayed until 2019, when TailwindCSS was released. Tailwind did away with the rigidity of bootstrap, and gave web designers even more flexibility to build complex websites. Tailwind also came with a CLI tool to compile your CSS on the fly, and only include the parts of TailwindCSS library that you're using, as well as providing greater customisation tools of sizes and colours. I was happy to switch to TailwindCSS as soon as I had some free time, and I've been using TailwindCSS for all my web design ever since.
However, for all the modern utilities and responsive layout tools these frameworks provide, nearly all the sites built with them always end up being just another centre aligned column layout, there's usually a hamburger menu, and there's probably a strap-line in big centred text, maybe a hero image. There's nothing wrong with modern web design, it's the natural evolution of responsive design that can expand vertically to accommodate dynamic content. But it's getting a little predictable. I long for the time when we built websites in photoshop, when 3D graphics were exciting and used for no other reason than that. A time when typography was exciting, and text alignment was pixel perfect. If you're too young to remember this time, or you're ready for the nostalgia trip, I recommend checking out the Golden Age of Web Design at the Web Design Museum.
The New Design
The general design plan is to try and capture the compact feel of the early webs sites. The whole site will be laid out with pixels, no REMs, no percentages. It will be best viewed on a desktop, but will resize as you'd expect for smaller screens. Empty space will likely get filled with decorative blocks.
Although the primary site layout will be a 720px wide left aligned column, some pages will make full use of the horizontal space.
A key element of the design will be the relationship of the page content and the background canvas. The canvas is currently just drawing the page grid, but in the future I want to render shapes, patterns, and typographic elements. A lot of the canvas designs will be previewed on the Canvas section of the site.
Another, as yet not implemented design requirement, is a really strong typographic system. The typography today is pretty non-existent.
In addition to the design, I want each page to just have more visualisation of any relevant data. I want the site to have the user experience of a dashboard, were everything can be visualised, and everything is linked.
Above all, I want design elements purely for the sake of showing off design. This will not be a minimalist re-design, whitespace will probably get filled with design flourishes, patterns, additional metadata, or visual helpers related to the content. In the future, I'll put together another post with the design inspiration.
The Grid
The whole site layout is built around a 120px x 120px grid system. All sections on the site are forced to fit within this grid, both horizontal and vertical. Horizontal is easily handled by CSS, but the vertical fitting has to be handled via a Javascript callback that runs when necessary to resize elements on the page to fit the grid. It's not perfect (yet), but it's good enough for now.
The Canvas
Instead of a background colour or image, the design makes use of an absolutly positioned canvas element, that currently draws the grid crosshairs, but will be used to add additional type and imagery based on the current page. The canvas will be a living background. I've got lots of ideas of where to take the canvas, but it could have animation, and react to user interaction.
The Future
The re-design is happening live, in realtime, warts and all. Although I've migrated quite a lot of the elements to the new grid, there's still a lot to do, and a lot of bugs to fix. Some pages will take some time to migrate, and others will probably look broken for quite some time.
This post is a test of the features of this publishing system. Specifically testing formatting and layout
Heading 1: Markdown test
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
Heading level 2: Basic Markdown Formatting
This paragraph contains bold and italic text. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam.
Heading level 2: Lists
Heading 3: Bullets
Red
Green
Blue
Heading 3: Ordered
Ultraviolet
Visible Spectrum
Infrared
Heading level 2: Quotes
Big things have small beginnings
– Lawrence of Arabia
# Ruby codeblock
class Person
attr_accessor :name, :surname
def initialize name: "John", surname: "Smith"
end
def full_name
[name, surname].join(" ")
end
def greet
$stdout.write "Hello #{full_name}"
end
end
# Javascript
let headings = document.getElementsByName("h1")
for (let heading of headings) {
heading.style.backgroundColor = "#336699"
}
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
Cromer Pier Cromer Pier
Water Processing Water Processing
Macro Flower
Rocky mountain at the Rila Lakes Rocky mountain at the Rila Lakes
It's been a long time coming, but this site is now free of cookies! This means no need to implement a cookie banner, as there is no user data being stored in cookies. Not using cookies also means users are not tracked for analytics purposes, although simple page hit statistics are captured by the server monitoring systems, these do not include any user data.
So, now for the technical details. This site is currently built with the Rails framework and the Ruby programming language. Rails provides easy access to configure how cookies store data on the users browser, as well as easy methods to remove cookies from the users browser. For this site, cookies are only used when accessing the admin pages; normal users of the site will never have any information tracked in cookies. To achieve this, we can user a Rails after_action instruction in the application_controller that ensures cookies are removed from every page, except when a user is logged into the admin pages:
# app/controllers/application_controller.rb
after_action do
cookies.delete(Rails.application.config.session_options[:key]) unless user_signed_in?
request.session_options[:skip] = !(user_signed_in? || devise_controller?)
end
For my main Photo library, I use Adobe Lightroom Classic. It does everything you would want, and does it really well. But I only use it for my photographic photos. That is, photos that have a bit of thought put into them, the kind of photos that are taken with a tripod, or a circular polariser. I also use Apple Photos as my everything-ever-taken library, so all my phone photos, all my photographic photos, all my holiday photos, random snaps, and everything in-between.
Apple Photos has become a really powerful photo library management tool and image editor. It seamlessly syncs across all my devices, as well as iCloud Photos, which makes it a really useful part of my backup process.
As a managed library, there is a database that holds all the information about the photos and videos in the library. This database is an SQLite3 database, a very popular open source database that has plenty of tools to query the data held within. The only tricky part is figuring out how the data is structured.
In this post, I'm showing a few picked highlights from my Apple Photos library. This is not an exhaustive report, but given the dataset, it does show a good overview of the photographic equipment I've used over the years, and how my photographic style has changed to reflect this. There's quite a lot of data to make sense of, and this is a mostly un-edited dump of stats.
Camera Makes
Most of my cameras have been Canon DSLRs, Canon M series, and Apple iPhones.
Canon: 28903
Canon Cameras have been my go to choice ever since I bought my first DSLR, the Canon 350D. I've never felt the need to try other systems, with Canon there's a well stocked used market, as well as the biggest ecosystem of lenses and other accessories, as well as a vast selection of second hand choices. I almost exclusively shoot still photographs, and for me, Canons image quality and colour science have always enabled me to produce photos I'm proud of.
Apple: 5989
I've been an iPhone user since the iPhone 3G came out in 2008, and I currently have the latest generation iPhone 15 Pro.
EASTMAN KODAK COMPANY: 268
All Kodak photos were taken using the first digital camera our family owned, the Kodak DC280. It was released around 1999, with 2 megapixels, a maximum usable ISO of 400, and almost no creative controls what so ever.
Camera Models
KODAK DC280 ZOOM DIGITAL CAMERA: 268
Again, this is the first digital camera our family had. Although the ISO performance was terrible, and the battery was equally bad, it did show a glimpse of the future where you could be editing a photo with minutes of taking it. This might not sound impressive today, but bear in mind this was a time when film cameras were still the goto choice for amateurs and professionals.
W610i: 1; W900i: 17
Mobile phones before the iPhone had cameras, and yes, they were terrible.
These are all the iPhones, and iPads, that I've owned, and show the general purpose usefulness of having a good camera with you all the time.
Canon DIGITAL IXUS 500: 2005; Canon IXY DIGITAL 25 IS: 993
These Canon Ixus/Ixy cameras were my secondary cameras, my backup cameras, and my easy to pocket and take on holiday cameras. They were tough and had great performance.
Canon EOS 20D: 755; Canon EOS 350D DIGITAL: 4915; Canon EOS 40D: 2432
The Canon 350D was my first digital camera. A great starting camera. The 20D was my dad's camera around the same time. The 40D was my second camera, which I upgraded to whilst travelling in Japan.
Canon EOS M: 10217; Canon EOS M5: 6828
I switched to the Canon EOS-M just before my wife and I spent 6 months living in and exploring Bulgaria. I wanted to cut back on the weight and bulk of the 40D + EF 28-135mm. The original EOS-M was a good little camera with all the features of the DSLRs, but lacked physical controls, and required a lens adapter to continue to use EF lenses. I've not heavily invested in EF-M lenses, but I have had the 22mm F2 since day one, and owning that lens pretty much justifies the EOS-M ecosystem. Since then, I've upgraded to have two EOS-M5s, one of which has been converted to a 720nm Infrared camera, but I've continued to purchase mostly second hand EF lenses, due to the availability and cost for performance that the vast EF lens catalogue gives you.
Lenses
Canon EF / EF-S / EF-M
EF
EF-S
EF-M
EF 24-105mm f/4L IS USM
2413
EF-S 10-22mm f/3.5-4.5 USM
7656
EF-M 22mm f/2 STM
3941
EF 28mm f/1.8 USM
876
EF-S 18-55mm f/3.5-5.6 IS II
421
EF 28-135mm f/3.5-5.6 IS USM
1286
EF 40mm f/2.8 STM
487
EF 50mm f/1.8 II
3407
EF 50mm f/1.8 STM
1963
EF 70-200mm f/4L USM
994
EF 75-300mm
1206
I've had many Canon DSLRs and mirrorless cameras over the years, and in that time I've been able to try out lots of different Lenses. It's worth point out that all of the cameras have been crop sensors (1.6x), so I've never use the EF lenses at their native focal lengths. However, Canon have always had excellent adapters for mounting EF lenses on to their crop sensor bodies, which allows me to purchase reasonably prices second hand lenses. Stand outs from this collections are:
EF 24-105mm f/4L IS USM: This is my main carry lens. The image quality is good, the stabilisation is good. It's a good lens, but it weighs a ton. As my cameras have been crop sensor, I should really be looking for a better crop sensor carry about lens.
EF-S 10-22mm f/3.5-4.5 USM: I've had this lens a very long time, It's my oldest and longest lived lens. The image quality is good in the centre, but drops off quickly in the edges, but that 10mm field of view means it'll always have a place in my camera bag.
EF-M 22mm f/2 STM: This is an amazing little lens. it's tiny, but has excellent image quality. If you have a EOS-M series camera, you basically want to have this lens. 20mm on a crop sensor camera is a general use focal length, F2 is bright enough to capture night shots, and it's tiny, two tiny that it makes the camera pocketable.
EF 40mm f/2.8 STM: This is a new lens in my collection, and it's mostly used on my infrared M5 conversion, as it has excellent Infrared performance. It's also the sharpest lens I have, the resolution it can project on the sensor is, in my opinion, L series level sharpness. It's also tiny, and works a great compliment with the EF-M 22mm.
iPhone 7 back camera 3.99mm f/1.8: 1663; iPhone 7 front camera 2.87mm f/2.2: 11
I've had many iPhones before the iPhone 7, but I'd say the 7 is probably when the cameras got good.
iPhone XR back camera 4.25mm f/1.8: 1195; iPhone XR front camera 2.87mm f/2.2: 16
The iPhone XR has been a stalwart of a device for me. It's happily lasted many years, and only really got upgraded due to an unfortunate accident in a hot tub. the 12 megapixel back camera was good, not great, but certainly good enough for daily snapshots. As with the iPhone 7, I don't really take selfies, so the front camera gets virtually no use.
iPhone 15 Pro back triple camera 2.22mm f/2.2: 34; iPhone 15 Pro back triple camera 6.86mm f/1.78: 39; iPhone 15 Pro back triple camera 9mm f/2.8: 25
I've only just upgraded to the iPhone 15 Pro, and the camera usage is still very low. However, I do seem to be using all three of the lenses at about the same rate.
About the Stats
All this data is pretty much the raw unfiltered data directly from my Apple Photos SQLite3 library database. The SQLite3 database is a single file contained within the /Users/user_name/Pictures/Photos Library.photoslibrary package. In the database are tables for each asset in your library, as well as an extendedattirbutes table that contains all the data related to camera models and lenses for each asset. Explaining the database here is beyond the scope of this post, and I'll go over it in a future post, but for those interested, I'm creating a tool to automate the process on my GitHub account https://github.com/phil/photos-stats
Wave Function Collapse, it sounds like a complicated quantum mechanics process, partly because it is, but it's also an algorithm to generate 2D tiled layouts.
The Wave Function Collapse algorithm works by reducing the possibilities for each tile on the grid, and then collapses the tile with the lowest entropy (i.e., the tile that has the least number of possibilities). Once collapsed, the new constrain is propagated to all neighbour tiles, and then the next tile is selected for collapsing.
With terrain generation, we have 5 types of tiles: Sea, Sand, Land, Forrest, and Mountains. Each tile then defines its possible neighbours:
Sea tiles can be boarded by Sea and Sand tiles
Sand tiles can be boarded by Sea, Sand, and Land tiles
Land tiles can be boarded by Sand, Land, Forrest, and Mountain tiles
Forrest tiles can be boarded by Forrest and Land tiles
Mountain tiles can be boarded by Land and Mountain tiles
With these simple constraints, the algorithm can loop over each tile, collapse, and then propagate the new constraints to each neighbour.
Check out how the algorithm propagates and collapses tile on the canvas playground
Terrain generated by a Wave Function Collapse algorithm Terrain generated by a Wave Function Collapse algorithm
Un-balanced tile generation
This implementation is very basic. The entropy calculation on looks at tiles with the least number of possibilities, which allows Sea and Sand tiles to take over the generation, as they typically reduce to the lowest number possibilities quickly, and then take over. To combat this, I've increased the chance that, where possible, tiles will collapse to a Land tile. This also means the initial tile will likely collapse to a land tile, causing most generations to start with Land in the top left corner, and Sea and Sand tiles generating on the right. A better entropy calculation that takes into account neighbouring tiles might help to reduce this issue.
Performance is generally good, but takes increasingly longer the larger the grid becomes, as each time a tile is collapsed, the changes need to propagated to an increasing number of neighbouring tiles. Without animation, a 50x50 grid typically takes around 2-3 seconds. Further performance improvements can be made with the propagation routine.
The Wave Function Collapse algorithm is a general purpose algorithm. I've use it here for terrain generation, but it can also be used to generate any kind of structured random pattern. It's also possible to train the algorithm by providing an input reference, that is scanned to determine the possible tile combinations.
Other Wave Function Collapse uses would be generating floor plans, village/town maps, pseudo circuitry etc, as well as textures like stone walls and wood grain.
Pixel Type is a simple experiment into pixel fonts that can react to real-time parameters. Pixel Type is a brand new, monospaced, 5x5 pixel grid typeface. Currently only the uppercase characters are included, but there is a full compliment of punctuation glyphs. Pixel Type can also react to real-time jitter, that is, individual pixels of each character can be affected by a global jitter, which if taken too far will corrupt the type display beyond legibility.
I've added a new section to maniacalrobot.co.uk that has been in the planning for a long time. Canvas is a playground for experimenting with procedurally generated artwork. Canvas artwork will work in the browser, and will built with HTML canvas elements are drawn with the standard 2d context javascript library.
As much as possible, I'm avoiding using any 3rd party libraries. The purpose of Canvas is to experiment, and learn the technical skills, as well as the user experience when interacting with the canvas.
There are no rules as to what will be included, it's a free space to try new experimental ideas.