→AI Text-to-Colour

→AI Text-to-Colour

Introduction

I set out with a simple goal, to programatically generate colour palettes from text in a way that’s explainable. I’m determined that the colour palette should be unconstrained across the full colour space.

Create a Clean Text Dataset

I start in Collab to create a text dataset for development and testing purposes. I come across a government website containing 146 of Aesop’s fables. They each have their own page, with incremental URL numbers, making them easy to scrape into markdown files.

Dataset Creation Code

Deciding on Approach

I collaborate with ChatGPT to explore several methods for converting text into colour...

  • Use high-dimensional text embeddings, reduce to three dimensions for RGB values, normalise to 0-255 range, and create additional colours through strategic offsets in colour space.
  • Develop a semantic colour wheel with custom embeddings → generate text embeddings → measure cosine similarity.
  • Deploy a compact neural network trained to map text embeddings directly to RGB or HSL colour-space coordinates.
  • Transform text embedding into a prompt → generate an image with that prompt → extract dominant colours from the resulting image.

However, these approaches seem unnecessarily complex, so I decide to start with something simpler and test its effectiveness. Here's my refined approach:

  • Conduct a thematic analysis on the text to understand its style and content.
  • Leverage this style and content analysis to generate an appropriate colour palette.

This streamlined approach feels possible with just a couple of ChatGPT API requests. The methodology also gives us the additional benefit of explainability, as I can provide the thematic analysis output to end users, helping them understand the rationale behind the colour selection.

Step 1: Thematic Analysis to Structured JSON

The prompt I settled on for thematic analysis:

Analyse the provided text and return a theme analysis in JSON format. 
Follow these exact specifications:

Output Requirements:

- Return ONLY valid JSON, no additional text or explanations
- Use exactly this structure with these key names
- Select values ONLY from the predefined options listed below
- Include exactly 3 values per array (most relevant first)

JSON Structure:
{
"type_of_text": ["value1", "value2", "value3"],
"mood_of_text": ["value1", "value2", "value3"],
"setting_environment": ["value1", "value2", "value3"],
"characters_objects": ["value1", "value2", "value3"],
"era_timeframe": ["value1", "value2", "value3"],
"cultural_influences": ["value1", "value2", "value3"],
"intended_audience": ["value1", "value2", "value3"],
"narrative_pace_tempo": ["value1", "value2", "value3"],
"dominant_themes": ["value1", "value2", "value3"]
}

Selection Rules:

1. Choose the 3 most relevant values for each category
2. Order values by relevance (most important first)
3. If fewer than 3 values apply, repeat the most relevant
4. Values must directly relate to explicit text content, not interpretations

Input Text 
{replace with markdown file text}

You can see how this approach could aid explainability. For example you can see what influenced colour selection for ‘A Raven & A Swan’:

{'type_of_text': ['fable', 'narrative', 'story'],
 'mood_of_text': ['didactic', 'reflective', 'somber'],
 'setting_environment': ['natural', 'wetland', 'forest'],
 'characters_objects': ['raven', 'swan', 'feathers'],
 'era_timeframe': ['timeless', 'mythical', 'ancient'],
 'cultural_influences': ['folklore', 'moralistic', 'traditional'],
 'intended_audience': ['children', 'general', 'educational'],
 'narrative_pace_tempo': ['steady', 'moderate', 'calm'],
 'dominant_themes': ['envy', 'identity', 'nature']}

Step 2: Generate Colour Palette From Structured JSON

Now we take that thematic analysis, and get OpenAI to respond with a colour palette.

Generate a HEX color palette in JSON format based on the provided theme analysis. Follow these exact specifications:

**Output Requirements:**
- Return ONLY valid JSON, no additional text or explanations
- Use exactly this structure with these key names
- Include exactly 1 primary, 1 secondary, 2 tertiary, and 4 quaternary colors
- All HEX codes must be uppercase 6-character format (e.g., "#2D5016")

**JSON Structure:**
{
  "primary": "#HEXCODE",
  "secondary": "#HEXCODE", 
  "tertiary": ["#HEXCODE", "#HEXCODE"],
  "quaternary": ["#HEXCODE", "#HEXCODE", "#HEXCODE", "#HEXCODE"]
}

**Color Selection Criteria:**
1. Primary: Dominant theme/setting color (highest contrast)
2. Secondary: Key symbolic element (complementary to primary)
3. Tertiary: Supporting thematic elements (harmonious with primary/secondary)
4. Quaternary: Accent colors for objects/atmosphere (complete the palette)

**Requirements:**
- Ensure sufficient contrast between primary and secondary (minimum 3:1 ratio)
- Colors must harmoniously work together (analogous, complementary, or triadic schemes)
- Reflect the dominant themes and setting from the input JSON
- Prioritize the most frequently mentioned elements in character_objects and setting_environment

**Input Theme Analysis:**
{INPUT_JSON_HERE}

Processing Confirmation / Output

Processing file: The_Hare__the_Tortoise.md
Markdown file content read successfully.
OpenAI API call for theme analysis successful.
Theme analysis JSON parsed successfully.
OpenAI API call for color palette successful.
Color palette JSON parsed successfully.
Results for The_Hare__the_Tortoise.md stored.
Generated color palette:
{'color_palette': ['#4CAF50', '#FF5722', '#8BC34A', '#FFC107', '#FFEB3B', '#9E9E9E', '#795548', '#607D8B']}
image
⚠️

I realise at this point, evaluating this is going to be challenging. So rather than look across 140 fables that are all similar, I try some very different content, and check the response for that.

A BBC news article about escalating tensions between the US and Russia and the deployment of nuclear submarines… and I’m pleasantly surprised. The results pass the gut check.

Generated color palette:
{'color_palette': ['#00274D', '#D9534F', '#AAB7B8', '#5D6D7E', '#F7F9F9','#A93226','#2874A6','#1C2833']}
image

Step 3: Computation at Scale

To check this approach scales and doesn’t take too long, I use my dataset to generate 100+ palettes in short order. Success.

Tabular Results

I initially considered creating a static webpage for each fable with its corresponding palette. However, I decided to be more ambitious and transform this concept into a simple web product.

Step 4: To Production

The vision is simple. A text box where you can paste any text, and generate a sympathetic colour palette based on the nature of that text.

Given I’ve already got some prompts working with the OpenAI API this is largely a UI/UX challenge from here on in. It took a couple of hours to get things to an acceptable standard, most of that time is the Replit agent crunching through my requests. Here’s a concise summary of that back and forth…

- Create a minimal UI app where pasted text is analysed for themes by OpenAI.
- Allow custom-defined prompts for thematic analysis in strict JSON.
- Use thematic JSON response to request a HEX colour palette from OpenAI.
- Display generated colour palette visually in proportional rectangles.
- Adjust UI to prioritise colour palette display above textual analysis.
- Add copy-on-hover functionality to colour labels.
- Add an expandable "analysis details" section with a terminal-style typing animation.
- Refine animation speed, colours, and spacing for readability.
- Insert subtle footer credit link
- Fix mobile responsiveness issues, especially clipped buttons.
- Add a thin, colourful SVG logo under the main HEXpert title.
- Adjust spacing to improve visual cohesion between logo and title.
- Resolve HTTPS/SSL issues after adding custom domain
- Add colour ramps

And just like that, we’re up and running. Why not try HEXpert? Or watch the demo if it’s not running today.

ToDo

If you share some of my personality traits, you won't feel a sense of closure once you reach this point but instead a sense of dread about the number of loose ends you've created. I'll leave this to-do list here for my sanity:

  • Iterate on the thematic analysis prompt.
  • Iterate on the palette generation prompt.
  • Explore training a neural net instead.