Creative tool · self-built · v0.3.6
Generic image models do not know your brand. Getting on-brand output normally means hand-writing prompts, watching results, and throwing away anything off-palette, off-tone, or carrying a competitor's logo. VIOLET automates that judgement: it researches a brand from its name, compiles the research into prompts, scores every generated image against the brand with a vision model, and rewrites the prompt when the image fails.
The interesting part is not that it generates images. It is that the system holds an opinion about whether its own output is on-brand, and acts on it. A vision model scores each image against the brand profile; failures get a root-cause pass that rewrites the prompt and regenerates. That is an agentic loop, built before the word was fashionable.
1 · Research — a brand name becomes a structured profile: colour palette, typography, photography style, audience, positioning and a do/don’t list, in the Adobe Intelligence panel.
2 · Influence dial — one control for how hard that profile leans on every prompt; here at 100%, “strong brand emphasis”.
3 · Generate — on-brand stills across Story, Landscape, Square, Portrait, Classic and Cinematic framings, images or video, from one prompt box.
4 · History — every take is kept and provenance-stamped with the brand and influence it was made at, so a run is auditable afterward.
The generation archive still holds a complete session, and it reads like the influence dial from bottom to top. Same brand, same evening as the screenshot above — each step's prompt is quoted from the archive verbatim.
composeBrandPrompt at full influence, hex codes and all.kling-video image-to-video — “quick punch in camera move” plus the
same brand text. Hover to play. Stills to motion, one profile the whole way.// promptBrandFilter.ts — the exclusion list is
// researched, never hardcoded
competitors.forEach(competitor => {
const competitorRegex =
new RegExp(`\\b${competitor}\\b`, 'gi');
if (competitorRegex.test(prompt)) {
conflictsDetected.push(
`Mentioned competitor: ${competitor}`);
filteredPrompt = filteredPrompt
.replace(competitorRegex, targetBrand);
}
});
// RealTimeBrandComplianceValidator.ts —
// four judgements per image, then a verdict
const logoCompliance = await this
.validateLogoCompliance(imageUrl, intel, brand);
const colorCompliance = await this
.validateColorCompliance(imageUrl, intel);
const styleCompliance = await this
.validateStyleCompliance(imageUrl, intel);
const competitorCheck = await this
.checkForCompetitorBrands(imageUrl, intel);
// → overallScore gates approve / regenerate
The closed loop. Research (517 lines) → prompt compilation (470) → compliance scoring
against the brand with gpt-4-vision-preview (566) → root-cause regeneration (479). Each
stage is its own service; the failure path is real code, not a diagram.
Competitor exclusion is generated, not hardcoded. promptBrandFilter.ts builds a
regex per competitor from the researched rival list and strips them from prompts — so the exclusion
list is as current as the research, and no brand data is baked into the source.
26 image models behind one interface, across Fal and OpenAI — Flux Pro, SD 3.5, Recraft v3, Ideogram, Bria, SDXL, plus video models (Kling, Runway Gen-3).
The schema-driven form is half-built. The parameter form renders from a schema object rather
than hardcoded JSX — but fetchFalModels.ts does not actually fetch it. It configures the
Fal client, then returns a predefined schema branched on whether the model id contains
"flux", "bria" or "recraft", with a comment saying so. A genuinely new model gets a generic fallback,
not its own parameters. Live schema fetch is the obvious next commit.
Voice input is partly a stub. services/whisperService.ts is a 7-line
placeholder. The working transcription path is utils/googleSpeechService.ts (75 lines,
hitting Google's speech:recognize), with a 59-line Whisper response-shape helper beside it.
It cannot ship publicly as-is. Eight VITE_-prefixed keys means every one is
bundled client-side. A public demo needs a keyless replay build or a server proxy — the constraint
that shaped the whole showcase plan.
| artifact | count | what it is |
|---|---|---|
| source | 28,762 lines | 226 TypeScript / TSX files |
| services | 79 | research, generation, validation, regeneration, export, share, version |
| components | 94 | React 18 · Vite · Zustand · Supabase |
| image + video models | 26 | Fal ids referenced across the source, plus DALL·E via OpenAI |
| GPT-4 variants used | 5 | gpt-4 · 4o · 4-turbo · 4-vision-preview · 4o-mini-realtime |
| research service | 517 + 635 | brandResearch + EnhancedBrandIntelligenceService |
| compliance validator | 566 | RealTimeBrandComplianceValidator |
| regeneration | 479 | IntelligentBrandRegeneration |
Built solo — design, architecture and engineering. The version in
this write-up is 0.3.6, read from package.json; the snapshot folder is named v0.3.8, so
treat the folder name as a label rather than a version. The class of problem it answers: brand fidelity at
a volume where a person cannot review every frame, and where "on-brand" has to become something a system
can score rather than something a human eyeballs.