Problem
A blank prompt asks users to describe both the image they want and how to get a model to produce it. Phosphene originally led with a graph workspace, which exposed those decisions before a user could make their first image.
My responsibility
I own product direction, interface design, frontend and backend implementation, provider integration, payments, deployment, and support. That lets me follow a problem across the whole flow, from the first screen to the generation job and saved result.
Key decision: templates first
I changed the entry point from the advanced workspace to a catalog of visual outcomes. Users choose a template, supply the requested references, adjust guided fields, and generate. Graph and workflow tools remain available when they want more control.
The tradeoff is curation: each template needs examples, input requirements, useful defaults, and compatible model settings. In return, the first interaction has a defined path instead of an open-ended prompt.
What changed
Before: users started with the power-user workspace and its prompt structure. Now: a guided form turns their references and choices into a generation request, with the deeper workspace available afterward.
Engineering behind the flow
A shared prompt compiler translates template fields, references, and graph state into provider requests. Adapters keep provider-specific inputs and failures behind a consistent application boundary.
an armored guardian, wielding a glowing arcane sword, in a neon cyberpunk alley, over rain-slicked reflective ground, with atmospheric volumetric lighting, rendered as polished digital art
- 01compile
Build a stable natural-language prompt from selected template inputs.
- 02route
Pick Nano Banana 2 for fast iteration and square output.
- 03persist
Store graph, prompt tags, model params, and generated frames.
Generated images retain their prompt, model, history, and lineage. Users can revisit results and expand into graph tools without losing the context of the original generation.
Technical decisions
- Nuxt and Vue: a client-heavy editor with shared reactive state; server routes support the product around it.
- tRPC internally, explicit server endpoints externally: typed application contracts, with separate boundaries for webhooks, authentication, and provider requests.
- Prisma and Postgres: a relational core for users, payments, generation metadata, saved state, and image lineage.
- Provider adapters: external models can change while the product keeps stable concepts for inputs, jobs, results, and errors.
- D3 and Vue Flow: separate surfaces for freeform graph exploration and connected workflow modules.
Operating the product
I also maintain subscriptions and credits, storage, rate limits, monitoring, and deployment. Sentry and PostHog support investigation across the interface and generation flow; GitHub Actions deploys the application to a self-managed Hetzner/PM2 setup.
Provider timeouts, rejected inputs, and failed jobs need understandable next steps. Those operational details are part of the interface work, alongside the successful generation path.