SH
I
EMO
← All projects

themodumaliq.

A portfolio for Lagos photographer Moses Momodu that loads fast without flattening his work.

themodumaliq on desktop
Role
Design & development
Timeline
4 days
Type
Client project
Stack
Next.jsTypeScriptGSAP

The challenge

Moses needed a portfolio that felt as considered as his photography, across five categories from corporate events to portraits. Template sites made his work look like everyone else's.

The photos were the other problem. 88 originals came to 652 MB, with single headshots close to 24 MB. Serving them as they were would make the site crawl on mobile data, and compressing them by hand every time he adds a shoot wasn't going to last.

How it works

Every photo takes this path, from Moses's camera to the gallery:

Build timeIn the browser
88 original photos, 652 MB in total
sharp rotates each one and fits it within 2400px
Encoded as WebP at quality 82
17 MB of WebP photos
Manifest of sizes and tiny blurs
Gallery shows the blur, then the sharp photo
Scroll intro Flips into the grid, then a full-screen viewer
Build timeIn the browser
88 original photos, 652 MB in total
sharp rotates each one and fits it within 2400px
Encoded as WebP at quality 82
17 MB of WebP photos
Manifest of sizes and tiny blurs
Gallery shows the blur, then the sharp photo
Scroll intro Flips into the grid, then a full-screen viewer

Key decisions

  1. 01

    A photo pipeline he never has to think about

    A build script runs every original through sharp: it corrects the rotation, fits the photo within 2400px, keeps the colour profile and saves WebP at quality 82. It skips photos that haven't changed, removes outputs whose original is gone, and writes a manifest with each photo's size and a 16px blur preview.

  2. 02

    Galleries that open like a shoot

    Each category opens with a scroll intro that zooms and pans through the lead photos on custom smooth scrolling. Past a set point, GSAP Flip morphs them into the full grid while the rest of the photos fade in from a blur.

  3. 03

    A menu of hanging prints

    Hovering a category in the full-screen menu drops its cover print in on a peg, where it swings to rest with an elastic ease while the last one lifts away. A custom cursor frames links and photos like a viewfinder, and everything calms down for visitors who prefer reduced motion.

Code from scripts/photos.mjs:
const [inStat, outStat] = await Promise.all([stat(src), exists(out)]);
if (!outStat || outStat.mtimeMs < inStat.mtimeMs) {
  await sharp(src)
    .rotate()
    .resize({ width: LONG_EDGE, height: LONG_EDGE, fit: "inside", withoutEnlargement: true })
    .keepIccProfile()
    .webp({ quality: QUALITY, effort: 5 })
    .toFile(out);
  made += 1;
} else {
  kept += 1;
}

const { width, height } = await sharp(out).metadata();
const preview = await sharp(out).resize(16).webp({ quality: 40 }).toBuffer();
manifest[slug].push({
  src: `/photos/${slug}/${name}`,
  width,
  height,
  blur: `data:image/webp;base64,${preview.toString("base64")}`,
});

Decision 01, in code

scripts/photos.mjs

  • Lines 1–2

    Only re-encodes a photo if it's new or the original changed since the last run.

  • Lines 3–8

    Rotates from the camera's data, fits within 2400px, keeps the colour profile and encodes WebP.

  • Lines 15–22

    Records each photo's exact size and a 16px blur, so the gallery reserves space and paints something instantly.

Results

  • 97%smaller: 652 MB of originals down to 17 MB
  • 88photos across five categories
  • 16pxblur previews, so no photo pops in
  • 1command to add a new shoot

Screens

themodumaliq on desktop
themodumaliq on mobile