JSPM

tiny-mood

0.3.1
  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 6
  • Score
    100M100P100Q78852F
  • License MIT

Tiny, zero-dependency text-to-mood-to-color utility. Consumes a pre-built mood table JSON — use tiny-mood-generate to build one from your content.

Package Exports

  • tiny-mood
  • tiny-mood/browser

Readme

tiny-mood

Generate deterministic, mood-driven background colors from text — no images, no LLM calls, no network requests at runtime. Just a word table and OKLCH color math.

Feed it a blog post's title and description, get back a CSS background gradient that reflects the tone of the text — calmer posts render softer and cooler, energetic posts render brighter and warmer — optionally clamped to your own brand colors so it never looks off-brand.

What it is

tiny-mood looks up each word in your text against a small mood table (word → weight/energy/warmth), averages the matches into a single mood vector, then maps that vector onto an OKLCH color palette. The result is rendered as a soft, blurred gradient background — entirely deterministic, so the same text and seed always produce the same output.

The mood table itself is just JSON. You can use the small built-in default, or generate your own from your actual content using tiny-mood-generate, a separate companion CLI that builds a table from your real vocabulary using GloVe word embeddings.

Install

npm install tiny-mood

Quick start

import { getMoodBackground } from 'tiny-mood'
import moodTable from './mood-table.json' // generated by tiny-mood-generate, or your own

const post = {
  title: 'Announcing our new release',
  description: 'A fast, exciting launch into something new',
  slug: 'announcing-release' // used as the seed for stable, repeatable layout
}

const { background, filter, mood } = getMoodBackground(
  `${post.title} ${post.description}`,
  moodTable,
  post.slug
)

// background and filter are ready-to-use CSS values:
// <div style={{ background, filter }} />

Examples

Basic usage, no brand constraint

const { background, filter } = getMoodBackground(text, moodTable, seed)

Hue varies freely based on the text's warmth — calm/cold text trends toward blues and greens, warm/energetic text trends toward oranges and reds.

Clamped to your brand colors

import { createBrandPalette, getMoodVector, composeBackground } from 'tiny-mood'

const brand = createBrandPalette({
  colors: ['#0f380f', '#306230', '#8bac0f', '#9bbc0f'] // any hex/rgb()/hsl() strings
})

const mood = getMoodVector(text, moodTable)
const palette = brand.paletteFor(mood)
const { background, filter } = composeBackground(palette, seed)

Mood now picks a position along the real spread of hues in your brand colors, rather than inventing a color outside your palette. A calm post and an energetic post will land in genuinely different parts of your brand's color range, not just vary in lightness.

One-liner with brand colors

const { background, filter } = getMoodBackground(text, moodTable, seed, {
  colors: ['#0f380f', '#306230', '#8bac0f', '#9bbc0f']
})

Same effect as above, without the intermediate steps — pass colors directly and getMoodBackground derives the brand palette internally.

Controlling the visual shape

const { background, filter } = getMoodBackground(text, moodTable, seed, {
  colors: brandColors,
  blendShape: 'linear',      // 'round' | 'linear' | 'spiral'
  blendIntensity: 0.7,        // 0–1, how strongly the shape expresses
  blendAngleRange: [100, 160] // direction guardrail, degrees
})

round (the default) produces soft, organic blooms. linear produces diagonal color streaks. spiral produces sharper conic wedge-rays — a different visual register from the other two, intentionally less blurred so its structure stays visible.

Functions

getMoodBackground(text, table, seed?, options?)

The main convenience function. Extracts a mood vector from text using table, generates a palette (brand-clamped if options.colors is provided, otherwise free-hue), and composes it into CSS.

Returns { background, filter, mood }.

getMoodVector(text, table)

Extracts just the mood vector, if you want it without rendering anything.

Returns { weight, energy, warmth }, each roughly in the range -1 to 1.

createBrandPalette(options)

Builds a reusable palette generator from your brand colors.

const brand = createBrandPalette({ colors: [...] })
brand.paletteFor(mood) // -> Oklch[]
brand.sortedColors      // your colors, sorted by hue, for inspection

composeBackground(palette, seed, options?)

Lower-level: takes an array of OKLCH colors and a seed, returns the final { background, filter } CSS strings. Use this directly if you're building your own palette logic instead of createBrandPalette.

Parameters reference

Parameter Where Type Default What it does
colors getMoodBackground options, createBrandPalette string[] Your brand colors (hex, rgb()/rgba(), hsl()/hsla()). Omit for free-hue output.
blendShape getMoodBackground options, composeBackground options 'round' | 'linear' | 'spiral' 'round' The visual form of each color region.
blendIntensity same number (0–1) 0 How strongly the selected shape expresses. 0 always looks like round regardless of blendShape.
blendAngleRange same [number, number] [100, 160] Degree range streak/spiral direction is randomly drawn from per post. Has no effect when blendShape is 'round'.
blobCount composeBackground options number 5 Number of color regions composited together.
blurPx composeBackground options number 70 Base blur radius. Actual blur is reduced automatically as blendIntensity rises, more aggressively for spiral than linear.
blobAlpha composeBackground options number 0.7 Opacity of each color region before blending.
moodToPosition createBrandPalette options (mood) => number warmth-weighted blend Override how the 3-axis mood vector becomes a single position along your sorted brand colors.

Where the mood table comes from

tiny-mood itself ships with a small default table, but the better option for a real project is generating one from your actual content with tiny-mood-generate:

npx tiny-mood-generate ./content ./mood-table.json

This reads your blog posts, looks up each word's real semantic position using GloVe embeddings, and writes a small JSON table containing only the words your content actually uses. tiny-mood's runtime has no dependency on GloVe or any generation tooling — it only ever reads the resulting JSON.

Try it live

StackBlitz demo — tabbed examples with brand presets, blend shapes, and an interactive Blend Lab.

License

MIT