ai-image-generator
Live · sign-in requiredMy own interface and API for image generation over BFL FLUX and GPT Image. The only project here you can look inside.
Generating images through two providers means two consoles, two archives and no search across them. Something made six months ago can only be found again if you still remember which provider made it — and the prompt that led to the image is somewhere in between.
The ai-image-generator puts both behind the same interface and the same API. Pick a model, set the aspect ratio and the quality level, optionally hand over reference images for the result to follow; the library is then searchable by prompt and filterable by model, ratio and favourite. What an image cost is something both providers report themselves — that is kept rather than thrown away, and costs can be broken down afterwards by month and model.
What stands out is how much of this project comes from running it rather than from designing it. That a job needs an id of its own, because the phone cuts the connection the moment the page goes into the background. That a tile may only disappear after the server has answered, because a failed delete otherwise comes back on the next reload. That a single file only holds a library if it is written atomically. Every one of these changes has a failure as its cause, one that only showed up in use; not one of them was in the design.
Decisions
A job id instead of an open connection
Put the installed web app into the background on a phone and the operating system freezes the running request, then drops it. The server carried on regardless and filed the finished image, but the interface reported an error all the same. Every job therefore gets an id assigned up front, and the page asks after it again as soon as it is visible.
No field for the seed
The same seed and the same prompt give only roughly the same image: two runs came out 0.2 per cent apart in mean pixel deviation — the same to look at, but not identical. An input field would have promised a repeatability that is not there. The value is in the metadata and in the detail view; anyone who really wants to repeat a run uses the API.
One file for the whole library, but written atomically
The whole library, prompts and all, lives in a single JSON file. Stop the container mid-write — which is every deployment that lands during a running generation — and half a file was left behind, and with it everything was gone. Writing therefore goes to a file alongside first and is then renamed; an unreadable file is set aside at startup instead of overwritten, so the application does not end up in a restart loop.
Credits and dollars stay apart
One provider bills in credits of its own, the other in dollars. Pulling both into a single sum would mean inventing an exchange rate and showing a number nobody can trust. Costs are therefore kept per provider, and anything made before this change is shown as “unknown” rather than estimated.