← Back Mosaic Collage Maker logoMosaic Collage Maker

Blog

I used to rely on LumaPix FotoFusion to lay out big batches of family photos into printable pages — point it at a folder, get back a set of nicely arranged pages ready to print or turn into a photobook. FotoFusion has been defunct for years, and I never found anything that did quite the same job, so eventually I built my own version of it.

It started as a single self-contained HTML file with no build step, put together over a series of conversations with Claude (Anthropic), and has since grown into a proper native app for both Mac and Windows. This page is where I'll keep a running, dated log of what's changing — confirmed feature requests, bugs I'm actively chasing down, and things I've learned from actually using the app myself at real scale — newest first.

One thing worth being upfront about: I'm a solo developer. I build and maintain Mosaic Collage Maker on my own, in my own time, without the resources of a large organisation behind me. I read everything that comes in, and I genuinely want the app to work well for you — I use it myself for my own family photos — but I can't always reply instantly, and fixes and new features arrive at the pace one person can realistically manage rather than overnight. I'd much rather set that expectation honestly than over-promise. Thank you for your patience and understanding — it's what makes a small independent project like this possible.

Update Feature request Bug investigation Learning

The spacious update is now on Windows too

Following the Mac release a couple of days ago, the same update is now live on the Microsoft Store, so Windows and Mac are running the same version again. That means the new Coverage slider — for letting a page background colour or your own imported background show through around and between the photos — the Fit whole photo option, and the more consistent text sizing are all available on Windows now as well.

Thank you, as ever, to everyone trying the app and sending feedback — it's what decides what I work on next.

The more spacious update is now on the Mac App Store

The update I previewed a couple of weeks ago is live — it's available now on the Mac App Store. It's about giving your pages room to breathe. There's a new Coverage slider that lets the collage sit smaller so the page background — a colour, or a photo you've imported — shows through around and between the pictures. At full coverage it's exactly as before; ease it down and the page opens up, which makes the imported-background feature far more worthwhile than when the background sat almost entirely hidden behind the photos.

Alongside it, a new Fit whole photo option shows each picture complete inside its frame instead of cropping it to fill. This release also makes text sizing consistent — your captions and titles now stay the same real size on the printed page regardless of your window or screen — and fixes a couple of smaller things turned up in testing.

The Windows update follows next week — it's the same code, so it's just a matter of building and submitting it. I'll note here once it's out.

As ever, thank you to everyone trying the app and sending feedback — it's genuinely what decides what I work on next.

Now on Windows too — and a more spacious look is coming

The Windows version is now available on the Microsoft Store, so Windows and Mac are finally running the same release: pinned frames and text, Generate from Template, any-colour and imported backgrounds, changing page size, margins and spacing without losing your work, and margins that are real millimetre measurements. It's the same code as the Mac version — getting a build through each store just takes its own amount of time.

The next update, which I'm testing now, is about giving your pages room to breathe. Until now the mosaic always filled the whole page. There's now a Coverage slider that lets the collage sit smaller so the page background — a colour, or a photo you've imported — shows through around and between the pictures. At full coverage it's exactly as before; ease it down and the page opens up. It makes the imported-background feature far more worthwhile: until now that background was almost entirely hidden behind the photos.

Alongside it, a new Fit whole photo option shows each picture complete inside its frame instead of cropping it to fill. Both are in testing now and will follow in the next release on both platforms.

As ever, thank you to everyone trying the app and sending feedback — it's genuinely what decides what I work on next.

The new version is now on the Mac App Store

The update I've been writing about over the past couple of weeks is live — it's available now on the Mac App Store. It's the biggest set of changes since launch: pinned frames and text, Generate from Template, any-colour and imported backgrounds, changing page size, margins and spacing without losing your work, tidier frame alignment, and margins that are now real millimetre measurements. The entries below go through each of these in turn.

The Windows update follows within the next seven days — it's the same code, so it's just a matter of building and submitting it. I'll note here once it's out.

Thank you to everyone who's tried the app and sent feedback. It genuinely shapes what I work on next — a couple of the changes in this release came directly from things people asked for.

Changing your mind without starting over

Testing the next update turned up something that had quietly annoyed me for ages without my ever stopping to name it: if you wanted to change the page size, the margins or the spacing between photos, you had to press Generate — which rebuilt everything and threw away whatever arrangement you'd already worked on. A small change of mind cost you all your work.

Worse, changing to a page of a different shape — A4 to square, say — simply centred your old layout on the new page and left empty bands down two sides. Technically it avoided distorting anything. In practice it was useless: to make anything of it you had to resize every frame by hand, which is exactly the work this app exists to avoid.

So there are now four buttons that apply a setting to the pages you already have, rather than rebuilding them: resize the pages, apply new margins, apply new spacing, and tidy up alignment. Your photos, their crops and zoom, and all your text stay exactly as they were. And when the new page is a different shape, the app now asks whether you'd like it re-flowed into a fresh mosaic built for that shape, or your existing arrangement stretched to fit.

The part I enjoyed was discovering all three were the same problem. The gap between photos isn't really part of the layout — the page is divided into tiles that fit together exactly, and the gap is just an inset applied afterwards. Since the app records the gap it used, that's perfectly reversible: you can recover the original division, move it wherever you like, and re-apply any gap you want. Three features, one piece of arithmetic.

Six pixels

On page 20 of a 25-page book I was testing, the bottom edges of the two top photos didn't line up. Not by much — about half a millimetre. But it looked wrong in a way I couldn't ignore, and I'd noticed the same thing before without ever pinning it down.

The cause is in how the mosaic is built. The page is divided in two, then each half divided again, and so on — with each division choosing its own random proportions. Nothing makes one half agree with the other. On that page the left column divided at one point and the right column six pixels lower, and there's no rule that says they shouldn't.

What makes it look like a mistake rather than variety is precisely how small it is. Things should either line up exactly or differ obviously; six pixels reads as a failed attempt at alignment. When I checked the whole file, 9 of the 25 pages had at least one — the worst a hair-splitting 0.9 pixels apart.

The fix snaps edges that land within a whisker of each other onto a single line before the page is drawn, leaving genuinely different divisions well alone. Across 1,500 test layouts the offsets small enough to look like mistakes essentially vanished, while the layouts stayed just as varied.

Then I realised the fix had a hole in it. It only applies as a page is being created, so it can't reach a book you've already made — and reopening my test project showed the same six pixels sitting there. The only way to clean up an existing page was to reshuffle it, which fixes the alignment by rearranging the page: not much use for a book you've already laid out.

So there's now a Tidy frame alignment button that nudges those edges into line on pages you already have, without moving anything else. On my test book it cleared 22 near-misses down to 3 — and those three are large enough to look intentional. Pinned frames don't move, and running it twice does nothing the second time.

The lesson worth writing down: a fix applied at the moment something is created doesn't repair what already exists. Obvious in hindsight, easy to miss when you've just watched your fix work perfectly on a fresh page.

What's in the next update — pinning, templates and backgrounds

The next release is currently in testing. It's the largest batch of changes since launch, and most of it comes from actually using the app on my own photobooks and running into the limits.

  • Pinned frames and text. Pin anything you don't want moved — a title, a logo, a favourite photo — and Reshuffle re-tessellates the mosaic around it instead of landing on top.
  • Generate from Template. Save a design as a template, then repeat it across as many pages as you like, pouring your photos in as it goes. The pinned branding stays put on every page while the image frames reshuffle, so pages share a family resemblance without being identical.
  • Backgrounds. Any colour you like rather than just black or white, and you can import your own background image.
  • Resize pages without regenerating. Changing page size used to mean rebuilding from scratch and losing your arrangement. Now you can rescale existing pages onto a new size, keeping the layout, each photo's zoom and crop, and your text.
  • Every photo gets used. Number of Pages is now a target rather than a cap — the page count flexes so nothing is silently left out, and the app tells you plainly if it had to differ from what you asked for.
  • Margins in millimetres. Each margin slider now shows its real size in mm. More on why that turned out to matter below.

I'd rather test this properly than ship it quickly, so it'll go out when I'm satisfied it behaves — the entries below are some of what testing has turned up so far.

Margins that quietly changed size depending on your window

This one started as a small puzzle and turned into the most worthwhile fix of the batch. While testing I noticed the margin readout changing when I resized the app window. The slider hadn't moved, so why was the number different?

The answer was in how margins were stored. Margins, gaps and border thickness were all held as screen pixels and converted to real page pixels using the current on-screen scale — which depends on how big the window happens to be. So the same slider setting baked a different physical size depending on the size of your window at the moment you pressed Generate.

On its own that sounds academic. Then I thought about how I actually use this app: a travel photobook covering a year, built in sections over several months, sometimes on a different machine. Each section made at a slightly different window size would print with slightly different margins — and I wouldn't find out until the finished book arrived. A silent inconsistency that only shows up on paper is about the worst kind of bug there is.

Margins, gaps and borders are now genuine physical measurements, converted through the page's print resolution. A given slider value is the same real-world size regardless of your window, your screen, your machine, or even the page size. A margin of 10 mm is 10 mm on A5, A4, A3 or a custom size. The mm readout is now exact and stops moving when you resize.

Worth noting this wasn't a new fault introduced by the new features — it had been there since the very first version, including the one currently on both stores. It simply took building something long enough, in enough separate sittings, to notice.

The same bug, hiding behind a second door

Testing Generate from Template, I found photos landing on top of the template's text boxes. The text was still readable — it draws last, so it sits over the top — but the layout underneath was plainly wrong.

The cause was straightforward enough. The mosaic flows around pinned items, and the template feature assumed the template's text would be pinned. Templates where I'd remembered to pin the text worked perfectly; one where I hadn't broke silently. Since a template's text is fixed branding by definition, the fix was to treat it as protected whether or not it had been pinned by hand.

I fixed that, built it, tested it — and it still happened. Which was the genuinely useful part.

The fix only covered the From Template button. But I'd been reproducing it a different way: load an old template and press Reshuffle. That's a completely separate entry point into the same underlying behaviour, and it wasn't covered. One root cause, two doors into it, and I'd only shut one.

The lesson I'm taking from it: when you find the cause of a bug, don't stop at the path you happened to be testing. Ask what else reaches the same code. Fixing it at the point where the template is loaded — rather than at one button that happens to use it — closed both doors at once.

Why saving a large project used 18GB of memory

Exporting a big archive was pushing memory use to somewhere between 14 and 18GB. It wasn't a leak — it would climb, drop back sharply, then climb again — but it was far more than the job actually needed.

The reason was simple once I looked: the app built the entire ZIP in memory first, then handed the finished thing over to be written to disk. Every photo in the project had to be held at once before a single byte was saved. The bigger the project, the bigger the peak.

It now writes each item straight to disk as it goes, so only one photo is in memory at a time rather than the whole archive. Rendering a set of pages now holds steady at around 2GB regardless of how many pages there are, and the peak no longer scales with the size of the project. As a bonus, writing in larger chunks became safe once memory pressure was gone, which made the write itself considerably quicker too.

The general lesson, which I keep relearning: it's worth asking whether you need the whole thing assembled before you start, or whether you can just deal with it a piece at a time.

When Reshuffle appears to stop working

While testing pinned items I hit something that looked like a bug and turned out to be geometry. On a heavily pinned template, the first Reshuffle rearranged everything nicely — and every Reshuffle after that only swapped the photos around, leaving the frame sizes identical.

What's happening is that pinned items carve the page into separate pockets of free space. On the template I was testing, five pinned text boxes left exactly six gaps, and I had six photos — so each photo got a gap to itself. When a gap holds a single photo, that gap is the frame: there's nothing left to subdivide, so its size can't vary. The only freedom remaining is which photo sits in which gap.

I'd already added that photo-swapping deliberately, so repeat reshuffles still visibly do something rather than appearing dead. But it's worth understanding the trade-off: the more you pin, the less room the mosaic has to work with. If you want frame sizes to keep varying, leave it some larger gaps to play in.

I've decided to leave this as it is. Resizing frames further would mean either overlapping the things you deliberately pinned, or leaving blank holes in the page — both worse than the honest answer, which is that you've told it exactly where everything goes.

Now live on the Mac App Store and the Microsoft Store

Mosaic Collage Maker is now available on both stores — the Mac App Store and the Microsoft Store — the culmination of the recent round of scale-testing, fixes, and submissions logged in the entries below. The Mac version has cleared Apple's review and is live alongside the Windows one.

Reaching this point as a one-person project has been slow and deliberate by necessity — I'd much rather ship something I've actually tested at real scale than rush it out. If you've installed it, thank you. If anything doesn't behave the way you'd expect, the Support page is the best way to reach me, and I read everything that comes in.

Real-world timings on two very different machines

To get a proper sense of how the app behaves at scale, I ran the exact same job — a 380-page project built from 1,400 photos, producing a 2.1GB archive — on two machines at opposite ends of the spectrum:

Same 380-page / 1,400-photo job Mac Studio (M2, 32GB, macOS 15.6) HP Pavilion (i5-7400, 8GB, Win 10)
Save 2.1GB archive ~3½ min ~22 min
Render & export all 380 pages to JPEG ~4 min ~24 min

That's roughly a six-fold difference — but it's worth being clear about why, because it isn't really a Mac-versus-Windows story. The HP is a modest 2017 machine (a low-power quad-core with just 8GB of memory); the Mac Studio is a current high-performance chip with four times the RAM. Almost the entire gap is that hardware difference, not the operating system — a modern Windows PC would land far closer to the Mac. The takeaway isn't "use a Mac," it's "on older or low-memory hardware, large projects genuinely take real time — the app is working, not stuck."

One result stood out as genuinely useful, though: on the Windows machine I tested writing the output to a fast SSD versus an old spinning hard drive, and there was no discernible difference either way. If the app were limited by disk speed, the SSD would have been dramatically faster — the fact that it wasn't confirms the real bottleneck is the internal data-handoff overhead inside the app, not the disk (the earlier entry below on why the native app is slower than a browser tab goes into that overhead in detail).

Mac and Windows are back on one shared codebase

The Windows port had picked up several fixes during its own testing that hadn't made it back into the Mac build yet, so the two had quietly forked. Reconciled everything into a single index.html that both platforms now build from, tracked in one private repo instead of hand-copied files with version-number suffixes — should make it a lot harder for the two to drift apart silently again.

Along the way this also caught and fixed a real, pre-existing bug on Mac: toggling a border checkbox, moving the frame-size slider, or swapping two photos would silently break Alt+drag frame-moving on the current page until something else forced a full re-render. Fixed before this round of submissions went out.

Large Save/Archive/Export operations could hang indefinitely

Found while testing a ~2GB archive: the native save dialog would appear, a 0-byte file would get created, and then nothing would ever happen — no error, no progress, no timeout. Traced it to how the app handed a large file over to the native save code internally, which turned out to silently stall at that scale. Rewritten to write in fixed-size chunks instead, which has since completed cleanly on multi-gigabyte archives with steady, visible progress.

Windows testing hadn't shown this exact symptom (a similar save was just slow, not hung) — but given it's the same underlying mechanism, the fix is going into the Windows build too before its next submission, rather than assuming it's fine there.

Why the native app is slower than the plain browser version

Same file, same photos, same 400-page archive: under 30 seconds in a plain browser tab, several minutes in the installed app. Not a regression — a real architectural cost. A browser can hand a finished file straight to its own download machinery with no extra steps. A sandboxed native app can't take that shortcut — every chunk of data has to cross an internal bridge between the app's web content and its native file-writing code, and that crossing has real overhead per call, independent of how much data is in each chunk.

Practical upshot: this is tunable (fewer, larger chunks means fewer crossings) but won't ever fully close the gap to a bare browser tab. Measured throughput on native writes has been holding around 10–11 MB/s in testing so far — see the Help Guide for planning-purposes timing estimates at various library sizes.