Как-то вечером я поймал себя на том, что трачу по 20 минут на поиск цветовой палитры для каждого нового проекта. Coolors, Adobe Color, случайные пины в Pinterest — и всё равно ощущение «не то». Тогда я задал себе вопрос: а что если вместо колеса оттенков начинать со слова? «Рассвет», «шторм», «лакшери» — у каждого слова есть интуитивный цвет.
Так появился Колорит — инструмент, который превращает слово или фотографию в цветовую палитру с помощью ИИ. В этой статье расскажу про технические решения, prompt engineering для DeepSeek и пару неочевидных браузерных API.

Стек и архитектура
Принципиально не хотел тащить тяжёлый фреймворк. Итоговый стек:
Бэкенд: Node.js + Express, ~150 строк
Фронтенд: чистый HTML/CSS/JS без сборщиков
ИИ: DeepSeek API (модель
deepseek-chat)Деплой: Linux VPS, pm2, nginx reverse proxy
Никакой базы данных — история запросов живёт в sessionStorage браузера. Поднялось за один вечер.
Режим «Слово → Цвет»: prompt engineering
Главная задача — получить от LLM строго структурированный ответ, а не поток текста. Я потратил время на правильный промпт и в итоге пришёл к такому подходу:
async function generatePalette(query, count) { const res = await fetch('https://api.deepseek.com/chat/completions', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${process.env.DEEPSEEK_API_KEY}` }, body: JSON.stringify({ model: 'deepseek-chat', temperature: 0.8, messages: [{ role: 'system', content: `Ты — эксперт по цвету и дизайну. Возвращай ТОЛЬКО валидный JSON-массив, без markdown, без пояснений. Формат каждого элемента: {"hex":"#RRGGBB","name_ru":"Название","name_en":"Name"}` }, { role: 'user', content: `Создай палитру из ${count} цветов для слова/фразы: "${query}". Цвета должны передавать настроение, ассоциации и смысл слова. Избегай слишком похожих оттенков.` }] }) }); const data = await res.json(); return JSON.parse(data.choices[0].message.content); }
Ключевые детали промпта:
System prompt жёстко задаёт формат — только JSON, никакого markdown
temperature: 0.8 — достаточно креативно, но предсказуемо по формату
Явная просьба «избегать похожих оттенков» — без неё модель иногда возвращала 5 оттенков одного цвета
Режим «Фото → Цвет»: кластеризация в браузере
Здесь я принципиально не хотел отправлять фото на сервер — и для приватности, и для скорости. Весь алгоритм работает через Canvas API:
async function generatePalette(query, count) { const res = await fetch('https://api.deepseek.com/chat/completions', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${process.env.DEEPSEEK_API_KEY}` }, body: JSON.stringify({ model: 'deepseek-chat', temperature: 0.8, messages: [{ role: 'system', content: `Ты — эксперт по цвету и дизайну. Возвращай ТОЛЬКО валидный JSON-массив, без markdown, без пояснений. Формат каждого элемента: {"hex":"#RRGGBB","name_ru":"Название","name_en":"Name"}` }, { role: 'user', content: `Создай палитру из ${count} цветов для слова/фразы: "${query}". Цвета должны передавать настроение, ассоциации и смысл слова. Избегай слишком похожих оттенков.` }] }) }); const data = await res.json(); return JSON.parse(data.choices[0].message.content); }
Для кластеризации использовал k-means в HSL-пространстве — оно лучше отражает перцептивное расстояние между цветами, чем RGB. После нескольких итераций центроиды стабилизируются и дают доминирующие цвета изображения.

Неочевидные детали
iOS: сохранение в Фото, а не в Загрузки
Стандартный download-атрибут на iPhone сохраняет файл в «Загрузки» — пользователи этого не ожидают. Решение — Web Share API:
async function saveOrShare(canvas) { canvas.toBlob(async (blob) => { const file = new File([blob], 'palette.png', { type: 'image/png' }); // iOS поддерживает share файлов — открывает нативный шаринг if (navigator.canShare?.({ files: [file] })) { await navigator.share({ files: [file], title: 'Палитра' }); } else { // Fallback для десктопа const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = 'palette.png'; a.click(); URL.revokeObjectURL(url); } }); }
На iOS появляется нативный Share Sheet — можно сохранить в Фото, отправить в Telegram, скопировать и т.д.
Тёмная тема и восприятие цвета
Неожиданная проблема: одна и та же палитра выглядит иначе на тёмном и светлом фоне из-за симультанного контраста. Оттенок, который кажется тёплым на тёмном фоне, на светлом может казаться холоднее. Решил это через CSS-переменные с отдельными значениями для каждой темы, плавный переход transition: background .35s сглаживает переключение.
История без базы данных
Последние 10 запросов хранятся в sessionStorage — исчезают при закрытии вкладки, никакой слежки, никакого сервера. Отображаются как кликабельные чипы под строкой поиска.
Что получилось
Инструмент работает на https://konstmax.ru/colorit/. Два режима — «Слово→Цвет» и «Фото→Цвет», переключатель RU/EN, тёмная и светлая темы, история запросов, экспорт PNG с нативным шарингом на iOS.
Основные цифры после запуска:
Время ответа DeepSeek: ~1.5–3 сек на запрос
Размер фронтенда без зависимостей: ~18 KB CSS + ~22 KB JS
Canvas-кластеризация 200×200px: ~40 мс в браузере
Что дальше: «Слово-система»
Колорит — первый модуль в задуманной экосистеме «Слово-система»: набора инструментов, где точкой входа является обычное слово или образ. Слово→Цвет уже есть. Следующие модули в разработке — Слово→Шрифт, Слово→Форма. Идея в том, чтобы ИИ помогал дизайнеру и разработчику на самом раннем этапе — когда есть только ощущение, но нет ещё ни одного пикселя.
