Media
This page explains how to author media (images, videos) once and let Homepage Builder create the local and deployed representations.
The independend pipelines keep authored source files in , create disposable generated output, and publish deployed media to Cloudflare R2. The Homepage then chooses between local or deployed files.
Homepage CDN
The shared library is available from every Content folder.
The shared library is available from every Content folder.
Manage ready-to-serve images and videos manually under . Choose your own folder structure; use lowercase descriptive filenames with hyphens. For example, is referenced as:


The leading always selects the shared library. The generator resolves it to on the current Homepage host. Localhost reads local files. Deployed and, once connected, read the dedicated R2 bucket through the existing Homepage Worker. No separate CDN Worker or subdomain is needed. Existing layout and playback modifiers apply, including image links, galleries and slideshows. Prepared HLS playlists can be referenced with their filename; keep their segments alongside them.
The CDN preserves your files unchanged and does not run image/video conversion. Normal deployment mirrors all visible regular files, including unreferenced assets, replacements, and local deletions. Update Content references when renaming or removing a file. Missing references fail generation. Keep the source folder present, even when intentionally emptied; hidden files are omitted and symlinks are rejected.
Preview the R2 mirror without changing it
make cdn-sync-dry-runThe standalone runs the same mirror immediately. Uploads finish before remote deletions begin. Stable URLs revalidate and bypass the service-worker cache so replacements and deletions become visible. CDN object credentials must have read/write access to . If a later GitHub push or Cloudflare rollout fails, the completed R2 mirror is not automatically rolled back.
Quick start
The shortest path from a source file to a working Homepage preview.
Put source media beside the that uses it.
Use a virtual reference when the Homepage should generate WebP variants. Use a concrete image filename when the original should stay the delivered representation.
Use as the local video source and reference from authored Markdown.
Run the relevant Make command before checking or deploying the Homepage.
Terminal: Generate and check media
cd ~/Development/Artificial-Colleagues/Homepage-Builder/work
make media
make checkincludes media processing, upload, validation, and cleanup in the normal deployment flow. It does not replace the local check when you are still authoring content.
How media moves
Authored sources stay simple; generated files are an intermediate result, not another place to edit.
Authored sources stay simple; generated files are an intermediate result, not another place to edit.
Author source media and references in .
Convert managed images and source videos into .
Emit the local files required by Homepage preview into .
Publish deployed media to R2 and let the runtime use the matching R2 representation.
| Location | Responsibility |
|---|---|
| Authored files and original source media. | |
| Disposable converted files and the shared media manifest. | |
| Local runtime assets emitted for Homepage preview. | |
| R2 | Deployed media addressed by deterministic public URLs. |
The converter and uploader use the manifest rather than scanning the frontend asset tree. Generated files are not authored files and should not be edited by hand.
Images
Choose the delivery behavior at the reference: .img means managed WebP; a concrete image extension means the original.
Choose the delivery behavior at the reference: means managed WebP; a concrete image extension means the original.
Managed image references
The .img suffix is an instruction in Markdown, not a file that needs to be created.
The suffix is an instruction in Markdown, not a file that needs to be created.
Managed image references



These references produce role-based WebP files such as
, , and . The generated filename does not expose the source extension or quality.
| Reference | Source selection | Delivery |
|---|---|---|
| First matching source by the configured precedence. | Original locally; WebP variant from R2 when deployed. | |
| First matching source, with lossy quality 95. | Original locally; quality-specific WebP when deployed. | |
| Exactly , with lossy quality 95. | Original locally; quality-specific WebP when deployed. | |
| Exactly , using the normal managed policy. | Original locally; WebP variant from R2 when deployed. |
Source selection
These rules matter when a content folder contains more than one possible source image.
For a bare reference such as , the converter looks for a matching source in this order:
An explicit extension selects that source instead. For example, uses and does not fall back to .
If two managed references would produce the same logical output name, the converter stops with a filename-collision error. For example, do not request both and for the same source, or explicitly manage both and in one generated output scope. Choose one source/profile combination for that logical image name.
Variants and quality
The role selects the intended display size; the converter never enlarges the source.
| Role | Intended use | Default target |
|---|---|---|
| Listings and thumbnails | 800 px | |
| Normal page content | 2000 px | |
| Full-screen or stage media | 4096 px |
The optional integer before is a quality override from 1 to 100. It forces lossy WebP encoding. Without an override, the configurable image conversion policy chooses the encoding mode for the source; the current default lossy quality starts at 85.
The target sizes and quality settings are configurable at the top of . When a source is smaller than a target role, the source is reused at its native size.
Originals, galleries, and thumbnails
Use concrete references for originals, and let each gallery member choose its own delivery behavior.
Use concrete references for originals, and let each gallery member choose its own delivery behavior.
A concrete image reference such as , , , or remains an original reference:
locally, it uses the original file;
for deployment, the referenced original is uploaded unchanged to R2;
the same rule applies when the concrete image is the target of a Markdown link.
Managed references request generated WebP delivery and do not expose an
original-download URL by default.
A gallery is only a group of authored image references. Each member is processed independently:
managed members receive their own WebP variants;
concrete members remain original files;
there is no separate gallery conversion format to maintain.
Image hover tint
Add to an image's modifier group for a subtle overlay in the current page background color. It starts at 10% opacity and fades to 0% on hover or keyboard-visible focus: 150ms entering, 300ms returning. Use the modifier only on images where you want this effect.
The existing modifier remains for captions; does not need it.
Cover, Listing, and feed images
Shared fields
Posts, Tutorials, and Presentations are current Listing items. Each requires , , and . Templates use , , and until an item has its own values.
is metadata. It does not add an image to a Post or Tutorial body. Presentation also appears on its generated Cover. Image references follow the same local, CDN, external, and managed-delivery rules described above.
By content type
Post: is the default Listing image and Blog feed thumbnail. overrides it in Listings. overrides it in feed items.
Tutorial: is the default Listing image. Tutorials have no feed.
Presentation: is the default Listing image and generated Cover image. changes the Listing image only. Listing media is separate.
Page: Pages are not current Listing items. Page Listings are future work. Page images remain authored in Markdown.
Showing images in Listings
Optional , , and override , , and . The , , and displays can show Listing images. The display never shows images.
No body image or Presentation filename is selected automatically. Use the shared placeholder until an item has a chosen image.
Blog feed
The Blog is the only feed. , , and override a Post's title, summary, and image. Body images stay in the article and do not become feed thumbnails. The Blog View's and set the channel title and description.
Videos
A video.mov is a local source convention; authored Markdown references the generated video.mp4 name.
A is a local source convention; authored Markdown references the generated name.
Authored video reference
| Filename | Meaning |
|---|---|
| Source-only input for the video pipeline. Do not reference it from authored Markdown. | |
| Authored playback reference for the managed source video. | |
| Any other or | Ordinary content media; not managed by this convention. |
The video pipeline detects changed sources and generates a local MP4 plus an adaptive HLS package. Local Homepage preview uses the generated MP4. Deployed Homepage playback uses the canonical HLS package from R2, where the player can select an appropriate rendition for the available bandwidth. Only the HLS package is uploaded; the standalone MP4 stays local.
The image and video pipelines remain independent. runs both, but changing an image does not trigger video conversion and changing a video does not trigger image conversion.
Make commands
Run these commands from the work/ directory to generate, inspect, publish, or clean up media.
Run these commands from the directory to generate, inspect, publish, or clean up media.
Generate and validate
Use these commands while authoring or before deployment.
Use these commands while authoring or before deployment.
| Command | Purpose |
|---|---|
| Process all managed images. | |
| Process managed images in one content path. | |
| Process all managed videos. | |
| Process managed videos in one content path. | |
| Process images and videos. | |
| Regenerate and validate the Homepage, including media references. |
Terminal: Generate one content area
make image path=presentations/example
make video path=presentations/examplePublish and clean up
Deployment publishes current media first; cleanup removes media that content no longer uses.
| Command | Purpose |
|---|---|
| Process media, validate and package the Homepage, publish it, then clean up retired media. | |
| Show R2 cleanup candidates without changing R2 or local state. | |
| Remove stale and retired R2 media and update local cleanup state. |
is destructive: it deletes objects from R2. Use the dry run first when the candidate list matters. Cleanup does not commit, push, or deploy.
R2 lifecycle
R2 should contain media still required by authored content and the canonical representation of that media.
R2 should contain media still required by authored content and the canonical representation of that media.
The normal deployed lifecycle is:
The converter walks the content tree and skips unchanged sources.
Converted output and source hashes are recorded in the media manifest.
The uploader publishes current canonical image variants, referenced image originals, or the HLS video package to R2.
Deployment validation requires every referenced managed asset to have a current upload record before the Homepage can be published.
Cleanup removes stale canonical objects and retires old legacy objects only after the canonical replacement is known to be uploaded.
The manifest is the source of truth for active and stale generated media. R2 is not scanned as a substitute for the manifest, which lets cleanup distinguish currently referenced objects from obsolete conversion output.
The migration switches and
are temporary rollback controls. Normal processing uses the canonical generated-media paths and does not publish both path families.
Troubleshooting
Start with the authored reference, then the local source, then the manifest and R2 upload record.
Start with the authored reference, then the local source, then the manifest and R2 upload record.
A managed image is missing
The converter could not resolve the virtual reference to a source image.
Check the spelling and extension in , then check for a matching source beside that file. Bare references use PNG-first precedence; explicit source extensions do not fall back to another format.
Two managed references collide
Two profiles are trying to write the same logical WebP filename.
Choose one managed source/profile combination for that logical image name. Do not combine the default and quality-specific form for the same source, and do not explicitly manage both matching PNG and JPG sources under the same logical basename.
The original image is not available as a download
Managed delivery intentionally does not expose the original by default.
Use a concrete image reference such as when the original file must be delivered. A concrete image reference in a Markdown link also causes the original to be uploaded to R2.
A deployed image or video is missing
Deployment validation is designed to catch missing managed uploads before publication.
Run the relevant processing command, inspect the manifest, and run before deployment. Deployment validation fails when a referenced managed asset has no current R2 upload record.
R2 contains old media
Cleanup is intentionally separate so its candidates can be reviewed first.
Run . If the candidates are correct, run . The cleanup command updates local state after each successful source cleanup, so it can be safely rerun after an interrupted run.