Skip to content

Commit cb7dd0d

Browse files
committed
PERF: optimize images and automate it with a pre-commit hook
Photos were landing in the repo as camera originals (up to 6960x4640 / 12MB) while the site never displays them larger than ~800px, and gallery thumbnails were being resized by hand — inconsistently, and in one case not at all (stories/thumbs/26_lovelog was a 3255x3255 / 2.9MB "thumbnail"). Tooling: - tools/optimize-images.sh resizes to ~3x the CSS display size, re-encodes, strips EXIF (also removing GPS from lab photos), generates missing gallery thumbnails, and rewrites HTML paths when a file changes extension. - .githooks/pre-commit runs it on staged images so originals never enter history. Enable with tools/setup-hooks.sh; bypass with --no-verify. - Processed files carry a log-opt-v1 marker in their image comment, so repeat runs skip rather than re-compress. One-time pass over existing assets: - stories originals 131MB -> 26MB (max 2560px, q82) - stories/thumbs 6.9MB -> 3.1MB (600px square, q80) - lab_photo 8.0MB -> 540KB (home hero was a 5.1MB PNG) - group_profile 2.4MB -> 520KB (was 31KB-514KB for the same 512x512) - research_focus 2.0MB -> 332KB (displayed at 165px, stored at 2310px) Also removed an exact duplicate of the hero photo (stories/lab_photo (6).png) and three superseded research_focus/*_outdated.png files. Gallery filenames are normalized to lowercase .jpg; all HTML references were updated to match and verified to resolve.
1 parent fc7e665 commit cb7dd0d

160 files changed

Lines changed: 446 additions & 46 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.githooks/pre-commit

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
#!/usr/bin/env bash
2+
#
3+
# Optimize any image being committed, and generate its gallery thumbnail.
4+
#
5+
# Enable with: tools/setup-hooks.sh
6+
# Bypass once: git commit --no-verify
7+
#
8+
# Photos are resized, re-encoded and stripped of EXIF before they enter git
9+
# history, so the repository never stores a 10MB camera original. Rules live in
10+
# tools/optimize-images.sh.
11+
12+
set -euo pipefail
13+
14+
REPO="$(git rev-parse --show-toplevel)"
15+
cd "$REPO"
16+
17+
# Staged images in the directories the optimizer manages.
18+
mapfile -t STAGED < <(
19+
git diff --cached --name-only --diff-filter=ACMR \
20+
| grep -Ei '^(stories|lab_photo|group_profile|research_focus)/.*\.(jpe?g|png)$' || true
21+
)
22+
23+
[ "${#STAGED[@]}" -gt 0 ] || exit 0
24+
25+
if ! command -v convert >/dev/null 2>&1; then
26+
cat >&2 <<'EOF'
27+
28+
This commit adds images, but ImageMagick is not installed so they cannot be
29+
optimized. Unoptimized camera photos bloat the repository permanently.
30+
31+
Ubuntu/Debian : sudo apt install imagemagick
32+
macOS : brew install imagemagick
33+
34+
To commit anyway: git commit --no-verify
35+
36+
EOF
37+
exit 1
38+
fi
39+
40+
# Remember the HTML files' state so we can stage only the ones the optimizer
41+
# actually rewrites (when a file changes extension).
42+
declare -A HTML_BEFORE
43+
for page in *.html; do
44+
[ -f "$page" ] && HTML_BEFORE["$page"]="$(git hash-object "$page")"
45+
done
46+
47+
echo "pre-commit: optimizing ${#STAGED[@]} image(s)..."
48+
tools/optimize-images.sh "${STAGED[@]}"
49+
50+
# Re-stage each image: it may have been shrunk in place, or renamed to .jpg.
51+
for path in "${STAGED[@]}"; do
52+
if [ -f "$path" ]; then
53+
git add -- "$path"
54+
else
55+
renamed="${path%.*}.jpg"
56+
if [ -f "$renamed" ]; then
57+
git add -- "$renamed"
58+
git rm --cached --quiet --ignore-unmatch -- "$path"
59+
fi
60+
fi
61+
done
62+
63+
# A new photo in stories/ needs its gallery thumbnail generated and staged.
64+
for path in "${STAGED[@]}"; do
65+
case "$path" in
66+
stories/thumbs/*) continue ;;
67+
stories/*) ;;
68+
*) continue ;;
69+
esac
70+
base="$(basename "${path%.*}")"
71+
thumb="stories/thumbs/${base}.jpg"
72+
if [ ! -f "$thumb" ]; then
73+
source_file="$path"
74+
[ -f "$source_file" ] || source_file="${path%.*}.jpg"
75+
convert "$source_file" -auto-orient -resize "600x600^" -gravity center \
76+
-extent 600x600 -strip -interlace Plane -sampling-factor 4:2:0 \
77+
-quality 80 -set comment "log-opt-v1" "$thumb"
78+
chmod 644 "$thumb"
79+
echo " generated thumbnail $thumb"
80+
fi
81+
git add -- "$thumb"
82+
done
83+
84+
# Stage HTML only where a path reference was rewritten.
85+
for page in "${!HTML_BEFORE[@]}"; do
86+
[ -f "$page" ] || continue
87+
if [ "$(git hash-object "$page")" != "${HTML_BEFORE[$page]}" ]; then
88+
git add -- "$page"
89+
echo " staged rewritten references in $page"
90+
fi
91+
done

CLAUDE.md

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,45 @@ before touching any margin, padding, or gap.
3030
**Asset directories:**
3131
- `logo/` — sponsor and collaborator logos (PNG/SVG); referenced in `index.html` and `research.html`
3232
- `research_focus/` — thumbnail images for research area cards in `research.html`
33-
- `stories/` — gallery photos for `gallery.html` (year-prefixed filenames, e.g., `25_iclr.jpeg`)
33+
- `stories/` — gallery photos for `gallery.html` (year-prefixed filenames, e.g., `25_iclr.jpg`)
34+
- `stories/thumbs/`**generated**, never hand-made; see Images below
3435
- `group_profile/` — member headshots for `group.html` (filename = person's name slug)
36+
- `lab_photo/` — home page hero photo
37+
38+
## Images
39+
40+
**Do not resize or compress images by hand.** Drop the original in, commit, and the
41+
pre-commit hook does it — resizing, re-encoding, stripping EXIF (which also removes
42+
GPS coordinates from lab photos), generating the gallery thumbnail, and updating any
43+
HTML path if the extension changes.
44+
45+
```bash
46+
tools/setup-hooks.sh # once per clone — enables the hook
47+
tools/optimize-images.sh # optimize everything that needs it
48+
tools/optimize-images.sh --check # report only, change nothing
49+
```
50+
51+
Requires ImageMagick (`apt install imagemagick` / `brew install imagemagick`). The hook
52+
refuses the commit if it is missing rather than letting a 10MB camera original into
53+
history; `git commit --no-verify` bypasses it.
54+
55+
Targets are ~3x the CSS display size, so images stay sharp on retina screens:
56+
57+
| Directory | Max long edge | Format | Displayed at |
58+
|---|---|---|---|
59+
| `stories/` | 2560px | JPEG q82 | click-through full view |
60+
| `stories/thumbs/` | 600px square | JPEG q80 | 112–180px grid tiles |
61+
| `lab_photo/` | 1600px | JPEG q85 | ≤800px hero |
62+
| `group_profile/` | 512px | JPEG q85 | 140px avatar |
63+
| `research_focus/` | 500px | keep (PNG stays PNG — line art) | 165px card |
64+
| `logo/` | untouched || logos must stay crisp |
65+
66+
Processed files carry a `log-opt-v1` marker in their image comment, so re-running is
67+
safe — already-optimized files are skipped, never re-compressed. To force a full
68+
re-pass after changing the rules, bump `MARKER` in `tools/optimize-images.sh`.
69+
70+
Gallery photos are stored as lowercase `.jpg`; the optimizer normalizes `.png`/`.jpeg`/
71+
`.JPG` and rewrites the HTML reference for you.
3572

3673
**Scratch files** (not published pages, do not modify unless explicitly asked): `index_v1.html`, `design_demo.html`, `font_compare.html`
3774

@@ -110,6 +147,8 @@ When adding or modifying any page, verify all of the following:
110147
- [ ] External links open in a new tab (`target="_blank"`) and have `rel="noopener"`
111148
- [ ] Images have descriptive `alt` text
112149
- [ ] Large images use `loading="lazy"`
150+
- [ ] **Images were never hand-resized** — the pre-commit hook handles it; see Images above
151+
- [ ] **No hand-made files in `stories/thumbs/`** — thumbnails are generated from `stories/`
113152
- [ ] Dates are written consistently: `MMM YYYY` (e.g., `Mar 2025`)
114153
- [ ] Paper/thesis links use lowercase labels: `[paper]`, `[thesis]`, `[code]`, `[slides]`
115154

gallery.html

Lines changed: 33 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -349,22 +349,22 @@ <h2>Gallery</h2>
349349
</div>
350350

351351
<div class="gallery-item">
352-
<a href="stories/26_das_dinner.jpeg" target="_blank">
353-
<img src="stories/thumbs/26_das_dinner.jpeg" alt="LOG-DASLab group dinner" loading="lazy">
352+
<a href="stories/26_das_dinner.jpg" target="_blank">
353+
<img src="stories/thumbs/26_das_dinner.jpg" alt="LOG-DASLab group dinner" loading="lazy">
354354
<span class="tooltip">LOG-DASLab group dinner</span>
355355
</a>
356356
</div>
357357

358358
<div class="gallery-item">
359-
<a href="stories/26_icml_poster.jpeg" target="_blank">
360-
<img src="stories/thumbs/26_icml_poster.jpeg" alt="ICML 2026 poster" loading="lazy">
359+
<a href="stories/26_icml_poster.jpg" target="_blank">
360+
<img src="stories/thumbs/26_icml_poster.jpg" alt="ICML 2026 poster" loading="lazy">
361361
<span class="tooltip">ICML 2026 poster</span>
362362
</a>
363363
</div>
364364

365365
<div class="gallery-item">
366-
<a href="stories/26_icml.jpeg" target="_blank">
367-
<img src="stories/thumbs/26_icml.jpeg" alt="ICML 2026" loading="lazy">
366+
<a href="stories/26_icml.jpg" target="_blank">
367+
<img src="stories/thumbs/26_icml.jpg" alt="ICML 2026" loading="lazy">
368368
<span class="tooltip">ICML 2026</span>
369369
</a>
370370
</div>
@@ -384,29 +384,29 @@ <h2>Gallery</h2>
384384
</div>
385385

386386
<div class="gallery-item">
387-
<a href="stories/26_kcc_cliff.jpeg" target="_blank">
387+
<a href="stories/26_kcc_cliff.jpg" target="_blank">
388388
<img src="stories/thumbs/26_kcc_cliff.jpg" alt="KCC 2026 Jeju coast" loading="lazy">
389389
<span class="tooltip">KCC 2026 - Jeju</span>
390390
</a>
391391
</div>
392392

393393
<div class="gallery-item">
394-
<a href="stories/26_kcc_yaut.jpeg" target="_blank">
394+
<a href="stories/26_kcc_yaut.jpg" target="_blank">
395395
<img src="stories/thumbs/26_kcc_yaut.jpg" alt="KCC 2026 yacht tour" loading="lazy">
396396
<span class="tooltip">KCC 2026 - Yacht Tour</span>
397397
</a>
398398
</div>
399399

400400
<div class="gallery-item">
401-
<a href="stories/26_lovelog.JPG" target="_blank">
402-
<img src="stories/thumbs/26_lovelog.JPG" alt="Love LOG" loading="lazy">
401+
<a href="stories/26_lovelog.jpg" target="_blank">
402+
<img src="stories/thumbs/26_lovelog.jpg" alt="Love LOG" loading="lazy">
403403
<span class="tooltip">Love LOG</span>
404404
</a>
405405
</div>
406406

407407

408408
<div class="gallery-item">
409-
<a href="stories/26_iclrdinner.jpeg" target="_blank">
409+
<a href="stories/26_iclrdinner.jpg" target="_blank">
410410
<img src="stories/thumbs/26_iclrdinner.jpg" alt="ICLR Dinner" loading="lazy">
411411
<span class="tooltip">ICLR Dinner</span>
412412
</a>
@@ -427,28 +427,28 @@ <h2>Gallery</h2>
427427
</div>
428428

429429
<div class="gallery-item">
430-
<a href="stories/26_bowling.png" target="_blank">
430+
<a href="stories/26_bowling.jpg" target="_blank">
431431
<img src="stories/thumbs/26_bowling.jpg" alt="Bowling Night" loading="lazy">
432432
<span class="tooltip">Bowling Night</span>
433433
</a>
434434
</div>
435435

436436
<div class="gallery-item">
437-
<a href="stories/26_post-icml.jpeg" target="_blank">
437+
<a href="stories/26_post-icml.jpg" target="_blank">
438438
<img src="stories/thumbs/26_post-icml.jpg" alt="Post-ICML 2026" loading="lazy">
439439
<span class="tooltip">post-ICML 2026</span>
440440
</a>
441441
</div>
442442

443443
<div class="gallery-item">
444-
<a href="stories/25_yearendparty.png" target="_blank">
444+
<a href="stories/25_yearendparty.jpg" target="_blank">
445445
<img src="stories/thumbs/25_yearendparty.jpg" alt="Year-end Party" loading="lazy">
446446
<span class="tooltip">Year-end Party</span>
447447
</a>
448448
</div>
449449

450450
<div class="gallery-item">
451-
<a href="stories/25_boardgame.jpeg" target="_blank">
451+
<a href="stories/25_boardgame.jpg" target="_blank">
452452
<img src="stories/thumbs/25_boardgame.jpg" alt="Board Game Night" loading="lazy">
453453
<span class="tooltip">Board Game Night</span>
454454
</a>
@@ -462,7 +462,7 @@ <h2>Gallery</h2>
462462
</div>
463463

464464
<div class="gallery-item">
465-
<a href="stories/25_readinggroup.jpeg" target="_blank">
465+
<a href="stories/25_readinggroup.jpg" target="_blank">
466466
<img src="stories/thumbs/25_readinggroup.jpg" alt="Reading Group" loading="lazy">
467467
<span class="tooltip">Reading Group</span>
468468
</a>
@@ -476,7 +476,7 @@ <h2>Gallery</h2>
476476
</div>
477477

478478
<div class="gallery-item">
479-
<a href="stories/25_postech.png" target="_blank">
479+
<a href="stories/25_postech.jpg" target="_blank">
480480
<img src="stories/thumbs/25_postech.jpg" alt="POSTECH" loading="lazy">
481481
<span class="tooltip">POSTECH</span>
482482
</a>
@@ -490,7 +490,7 @@ <h2>Gallery</h2>
490490
</div>
491491

492492
<div class="gallery-item">
493-
<a href="stories/25_iclr.jpeg" target="_blank">
493+
<a href="stories/25_iclr.jpg" target="_blank">
494494
<img src="stories/thumbs/25_iclr.jpg" alt="ICLR 2025" loading="lazy">
495495
<span class="tooltip">ICLR 2025</span>
496496
</a>
@@ -504,14 +504,14 @@ <h2>Gallery</h2>
504504
</div>
505505

506506
<div class="gallery-item">
507-
<a href="stories/24_logspringouting.JPG" target="_blank">
507+
<a href="stories/24_logspringouting.jpg" target="_blank">
508508
<img src="stories/thumbs/24_logspringouting.jpg" alt="LOG Spring Outing" loading="lazy">
509509
<span class="tooltip">LOG Spring Outing</span>
510510
</a>
511511
</div>
512512

513513
<div class="gallery-item">
514-
<a href="stories/24_logspring.JPG" target="_blank">
514+
<a href="stories/24_logspring.jpg" target="_blank">
515515
<img src="stories/thumbs/24_logspring.jpg" alt="LOG Spring Outing" loading="lazy">
516516
<span class="tooltip">LOG Spring Outing</span>
517517
</a>
@@ -539,21 +539,21 @@ <h2>Gallery</h2>
539539
</div>
540540

541541
<div class="gallery-item">
542-
<a href="stories/23_christmasparty.png" target="_blank">
542+
<a href="stories/23_christmasparty.jpg" target="_blank">
543543
<img src="stories/thumbs/23_christmasparty.jpg" alt="Christmas Party" loading="lazy">
544544
<span class="tooltip">Christmas Party</span>
545545
</a>
546546
</div>
547547

548548
<div class="gallery-item">
549-
<a href="stories/23_cake.png" target="_blank">
549+
<a href="stories/23_cake.jpg" target="_blank">
550550
<img src="stories/thumbs/23_cake.jpg" alt="LOG Cake" loading="lazy">
551551
<span class="tooltip">LOG Cake</span>
552552
</a>
553553
</div>
554554

555555
<div class="gallery-item">
556-
<a href="stories/23_santa.JPG" target="_blank">
556+
<a href="stories/23_santa.jpg" target="_blank">
557557
<img src="stories/thumbs/23_santa.jpg" alt="Santa Dongyeop" loading="lazy">
558558
<span class="tooltip">Santa Dongyeop</span>
559559
</a>
@@ -567,70 +567,70 @@ <h2>Gallery</h2>
567567
</div>
568568

569569
<div class="gallery-item">
570-
<a href="stories/23_postech.JPG" target="_blank">
570+
<a href="stories/23_postech.jpg" target="_blank">
571571
<img src="stories/thumbs/23_postech.jpg" alt="POSTECH" loading="lazy">
572572
<span class="tooltip">POSTECH</span>
573573
</a>
574574
</div>
575575

576576
<div class="gallery-item">
577-
<a href="stories/23_movienight.jpeg" target="_blank">
577+
<a href="stories/23_movienight.jpg" target="_blank">
578578
<img src="stories/thumbs/23_movienight.jpg" alt="Movie Night" loading="lazy">
579579
<span class="tooltip">Movie Night</span>
580580
</a>
581581
</div>
582582

583583
<div class="gallery-item">
584-
<a href="stories/23_halonabeachcove.jpeg" target="_blank">
584+
<a href="stories/23_halonabeachcove.jpg" target="_blank">
585585
<img src="stories/thumbs/23_halonabeachcove.jpg" alt="Halona Beach Cove, Hawaii" loading="lazy">
586586
<span class="tooltip">Halona Beach Cove, Hawaii</span>
587587
</a>
588588
</div>
589589

590590
<div class="gallery-item">
591-
<a href="stories/23_icml3.jpeg" target="_blank">
591+
<a href="stories/23_icml3.jpg" target="_blank">
592592
<img src="stories/thumbs/23_icml3.jpg" alt="Makapu'u Lookout, Hawaii" loading="lazy">
593593
<span class="tooltip">Makapu'u Lookout, Hawaii</span>
594594
</a>
595595
</div>
596596

597597
<div class="gallery-item">
598-
<a href="stories/23_icml2.jpeg" target="_blank">
598+
<a href="stories/23_icml2.jpg" target="_blank">
599599
<img src="stories/thumbs/23_icml2.jpg" alt="ICML 2023" loading="lazy">
600600
<span class="tooltip">ICML 2023</span>
601601
</a>
602602
</div>
603603

604604
<div class="gallery-item">
605-
<a href="stories/23_icml.jpeg" target="_blank">
605+
<a href="stories/23_icml.jpg" target="_blank">
606606
<img src="stories/thumbs/23_icml.jpg" alt="ICML 2023" loading="lazy">
607607
<span class="tooltip">ICML 2023</span>
608608
</a>
609609
</div>
610610

611611
<div class="gallery-item">
612-
<a href="stories/23_boardgame.png" target="_blank">
612+
<a href="stories/23_boardgame.jpg" target="_blank">
613613
<img src="stories/thumbs/23_boardgame.jpg" alt="Board Game Night" loading="lazy">
614614
<span class="tooltip">Board Game Night</span>
615615
</a>
616616
</div>
617617

618618
<div class="gallery-item">
619-
<a href="stories/23_teachersday.png" target="_blank">
619+
<a href="stories/23_teachersday.jpg" target="_blank">
620620
<img src="stories/thumbs/23_teachersday.jpg" alt="Teacher's Day" loading="lazy">
621621
<span class="tooltip">Teacher's Day</span>
622622
</a>
623623
</div>
624624

625625
<div class="gallery-item">
626-
<a href="stories/23_logday.jpeg" target="_blank">
626+
<a href="stories/23_logday.jpg" target="_blank">
627627
<img src="stories/thumbs/23_logday.jpg" alt="LOG Day" loading="lazy">
628628
<span class="tooltip">LOG Day</span>
629629
</a>
630630
</div>
631631

632632
<div class="gallery-item">
633-
<a href="stories/23_noscreenday.jpeg" target="_blank">
633+
<a href="stories/23_noscreenday.jpg" target="_blank">
634634
<img src="stories/thumbs/23_noscreenday.jpg" alt="No Screen Day" loading="lazy">
635635
<span class="tooltip">No Screen Day</span>
636636
</a>

0 commit comments

Comments
 (0)