A cross-platform C tool that converts image files into base64 data URLs for embedding directly in HTML, CSS, or JSON. Large images are automatically optimized to portfolio-appropriate sizes before encoding, using platform-native tools with zero extra dependencies. The encoded output is fully self-contained, so it works on GitHub Pages, any static host, or when opened as a local file -- no image hosting, no external URLs, no CORS issues.
Yes. Base64 data URLs embed the entire image payload inside the HTML/CSS/JSON source. GitHub Pages serves these files as-is. The browser decodes the base64 string and renders the image without making any additional HTTP requests. This means:
- No separate image files to upload or manage
- No broken images from wrong paths or missing assets
- The Go build tool already inlines everything into self-contained HTML
- A single HTML file contains the full page including all images
- Works in every modern browser and every version of Safari the project supports
The tradeoff is file size. Base64 encoding adds roughly 33% overhead compared to the raw binary. For portfolio images (avatars, project screenshots), this is negligible. The tool automatically resizes large images to 512px max before encoding, so you do not need to manually resize anything.
The converter automatically optimizes raster images before encoding. When you run ./convert photo.png, the tool:
- Reads the image dimensions using platform-native tools
- If either dimension exceeds 512px (configurable with
--max), resizes the image to fit within that box while preserving aspect ratio - Encodes the optimized version as base64
- Cleans up any temporary files
This happens transparently. A 4000x3000 photo becomes a 512x384 image before encoding, cutting the base64 output by 95% or more.
| Platform | Tool | Notes |
|---|---|---|
| macOS | sips |
Built in to every Mac, no install needed |
| Linux | magick or convert |
ImageMagick, available in most distros |
| Windows | PowerShell System.Drawing |
Built in, no install needed |
./convert photo.png # auto-optimize to 512px max
./convert --max 256 avatar.png # resize to 256px max dimension
./convert --max 800 screenshot.png # resize to 800px max dimension
./convert --no-optimize photo.png # skip optimization, encode raw fileAll raster formats: PNG, JPEG, GIF, WebP, BMP, TIFF, AVIF.
SVG and ICO are never resized. SVG is vector (already tiny). ICO has multi-resolution internal structure that should not be modified.
No dependencies. Compiles with any C compiler.
macOS / Linux:
cd img_convert
gcc -O2 -o convert convert.cmacOS (if headers are not found):
cc -O2 -o convert convert.c --sysroot="$(xcrun --show-sdk-path)"Windows (MSVC):
cl convert.c /Fe:convert.exe
Windows (MinGW):
gcc -O2 -o convert.exe convert.c
./convert photo.pngOutputs:
data:image/png;base64,iVBORw0KGgo...
Copy and paste this value anywhere a URL is accepted -- the src attribute of an <img> tag, a CSS background-image, or the site.image field in crissy-data.json.
./convert --json avatar.jpgOutputs:
{"file": "avatar.jpg", "mime": "image/jpeg", "size": 24310, "dataUrl": "data:image/jpeg;base64,..."}Multiple files produce a JSON array:
./convert --json avatar.jpg logo.svg screenshot.png./convert --field site.image avatar.pngOutputs:
"site.image": "data:image/png;base64,..."
./convert --css background.webpOutputs:
url(data:image/webp;base64,...)
./convert --html photo.pngOutputs:
<img src="data:image/png;base64,..." alt="photo.png">| Flag | Description |
|---|---|
--max N |
Max pixel dimension for optimization (default 512). Images larger than NxN are resized to fit, keeping aspect ratio |
--no-optimize |
Skip automatic optimization, encode the raw file as-is |
--json |
Output each image as a JSON object (or array for multiple files) |
--field KEY |
Output as a "KEY": "data:..." pair for pasting into JSON |
--css |
Output as a CSS url() value |
--html |
Output as an <img> tag |
--wrap N |
Line-wrap the base64 at N characters (0 = no wrap, which is the default) |
--quiet |
Suppress the info line printed to stderr |
--help |
Print usage information |
PNG, JPEG, GIF, SVG, WebP, ICO, BMP, TIFF, AVIF.
The tool detects the MIME type from the file extension. The actual file contents are encoded as-is regardless of format.
The manage page (manage.html) already has a file picker under Site > Portfolio Image that converts images to base64 in the browser. Use it when you are working in the manager.
Use this tool when you want to convert images from the command line, batch-convert multiple files, or script the process.
Set the portfolio image:
./convert photo.pngCopy the output and paste it as the value of "image" in the "site" section of crissy-data.json.
Or use --field to get the exact JSON line:
./convert --field image avatar.png >> snippet.jsonPipe into the JSON with jq (if available):
DATA_URL=$(./convert --quiet avatar.png)
jq --arg img "$DATA_URL" '.site.image = $img' crissy-data.json > tmp.json && mv tmp.json crissy-data.jsonAfter updating the JSON, run the build:
go run build.go .The built HTML files in build/ will contain the base64 image inline. Push to GitHub and the page is live with the image embedded.
| Image type | Recommended max size | Notes |
|---|---|---|
| Portfolio avatar | 100-200 KB | 256x256 or 512x512 is plenty |
| Project screenshot | 200-500 KB | Resize to ~800px wide |
| Favicon source | Under 50 KB | 64x64 or 128x128 |
| SVG icons | Under 20 KB | Already small, encodes well |
Base64 adds ~33% to file size. A 150 KB JPEG becomes ~200 KB of base64 text in the HTML. GitHub Pages has no issue serving files up to several megabytes.
MIT