Basic Word Cloud — Altair

A word cloud displays text data where word size represents frequency or importance. Words are arranged to fill available space, creating a visual summary of text content that highlights prominent terms and patterns. This visualization is ideal for quickly identifying the most common themes or keywords in a body of text.

Basic Word Cloud rendered with Altair

Renders

Python source (Altair)

""" anyplot.ai
wordcloud-basic: Basic Word Cloud
Library: altair 6.2.2 | Python 3.13.14
Quality: 85/100 | Updated: 2026-08-04
"""

import os
import sys


# Prevent local file from shadowing the altair package
script_dir = os.path.dirname(os.path.abspath(__file__)) if __file__ else os.getcwd()
if script_dir in sys.path:
    sys.path.remove(script_dir)

import altair as alt
import numpy as np
import pandas as pd
from PIL import Image


# Theme tokens (see prompts/default-style-guide.md "Background" + "Theme-adaptive Chrome")
THEME = os.getenv("ANYPLOT_THEME", "light")
PAGE_BG = "#FAF8F1" if THEME == "light" else "#1A1A17"
ELEVATED_BG = "#FFFDF6" if THEME == "light" else "#242420"
INK = "#1A1A17" if THEME == "light" else "#F0EFE8"
INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0"

# Imprint palette — color encodes topic, not ordinal position (see style guide)
CATEGORY_COLORS = {"Languages": "#009E73", "Data & AI": "#C475FD", "Cloud & Infra": "#4467A3", "Practices": "#BD8233"}

# Data: terms mined from developer conference talk titles, grouped by topic
word_data = [
    ("Python", 100, "Languages"),
    ("Analytics", 92, "Data & AI"),
    ("Kubernetes", 86, "Cloud & Infra"),
    ("JavaScript", 80, "Languages"),
    ("DevOps", 74, "Practices"),
    ("Docker", 70, "Cloud & Infra"),
    ("Security", 66, "Practices"),
    ("AWS", 62, "Cloud & Infra"),
    ("Database", 58, "Data & AI"),
    ("API", 56, "Practices"),
    ("Machine Learning", 54, "Data & AI"),
    ("TypeScript", 50, "Languages"),
    ("AI", 48, "Data & AI"),
    ("Agile", 46, "Practices"),
    ("Testing", 44, "Practices"),
    ("Terraform", 42, "Cloud & Infra"),
    ("Microservices", 38, "Practices"),
    ("Rust", 36, "Languages"),
    ("Git", 34, "Practices"),
    ("Azure", 32, "Cloud & Infra"),
    ("GraphQL", 30, "Data & AI"),
    ("Go", 28, "Languages"),
    ("Scalability", 26, "Practices"),
    ("Java", 25, "Languages"),
    ("GCP", 24, "Cloud & Infra"),
    ("NoSQL", 23, "Data & AI"),
    ("Serverless", 22, "Cloud & Infra"),
    ("CI/CD", 21, "Practices"),
    ("Data Pipeline", 20, "Data & AI"),
    ("Swift", 19, "Languages"),
    ("Monitoring", 18, "Practices"),
    ("Redis", 17, "Data & AI"),
    ("Networking", 16, "Cloud & Infra"),
    ("Kotlin", 15, "Languages"),
    ("Automation", 15, "Practices"),
    ("PostgreSQL", 14, "Data & AI"),
    ("Load Balancing", 13, "Cloud & Infra"),
    ("Refactoring", 13, "Practices"),
    ("C++", 12, "Languages"),
    ("Observability", 11, "Practices"),
    ("Caching", 11, "Cloud & Infra"),
    ("Neural Networks", 10, "Data & AI"),
    ("Deployment", 10, "Practices"),
    ("Elixir", 9, "Languages"),
    ("Firewall", 9, "Cloud & Infra"),
]

# Canvas — altair inner-view dims tuned within the library prompt's ±20px
# allowance to leave more spiral-packing room (prompts/library/altair.md "Canvas")
canvas_w = 640
canvas_h = 340

# Scale frequencies to font sizes
frequencies = [freq for _, freq, _ in word_data]
min_freq, max_freq = min(frequencies), max(frequencies)
min_size, max_size = 9, 32

# Build data with spiral positioning; a handful of lower-frequency words
# rotate 90° (a classic word-cloud technique) to fill the vertical gaps a
# purely horizontal layout leaves behind
words_list = []
x_positions = []
y_positions = []
font_sizes = []
categories = []
angles = []
placed_boxes = []

sorted_words = sorted(word_data, key=lambda w: w[1], reverse=True)
aspect = canvas_w / canvas_h

for i, (word, freq, category) in enumerate(sorted_words):
    size = min_size + (freq - min_freq) / (max_freq - min_freq) * (max_size - min_size)
    rotate = i >= 3 and i % 4 == 0
    angle = 90 if rotate else 0

    text_w = len(word) * size * 0.6
    text_h = size * 1.3
    box_w, box_h = (text_h, text_w) if rotate else (text_w, text_h)
    padding = 6

    cx, cy = canvas_w / 2, canvas_h / 2
    theta = 0.0
    radius = 0.0
    found_x, found_y = cx, cy
    found_box = (cx - box_w / 2, cy - box_h / 2, box_w, box_h)

    for _ in range(8000):
        # Elliptical spiral matching the inner-view aspect ratio
        x = cx + radius * aspect * np.cos(theta) - box_w / 2
        y = cy + radius * np.sin(theta) - box_h / 2

        if 15 < x < canvas_w - box_w - 15 and 15 < y < canvas_h - box_h - 15:
            box = (x, y, box_w, box_h)

            has_overlap = False
            for px, py, pw, ph in placed_boxes:
                if not (
                    x + box_w + padding < px
                    or px + pw + padding < x
                    or y + box_h + padding < py
                    or py + ph + padding < y
                ):
                    has_overlap = True
                    break

            if not has_overlap:
                found_x = x + box_w / 2
                found_y = y + box_h / 2
                found_box = box
                break

        theta += 0.22
        radius += 0.9

    placed_boxes.append(found_box)
    words_list.append(word)
    x_positions.append(found_x)
    y_positions.append(found_y)
    font_sizes.append(size)
    categories.append(category)
    angles.append(angle)

# Assemble data
df = pd.DataFrame(
    {
        "word": words_list,
        "x": x_positions,
        "y": y_positions,
        "size": font_sizes,
        "category": categories,
        "angle": angles,
    }
)
category_order = list(CATEGORY_COLORS.keys())
category_range = list(CATEGORY_COLORS.values())

# Title — mandated format, fontsize scaled off the 67-char baseline
title_text = "wordcloud-basic · python · altair · anyplot.ai"
title_ratio = 67 / len(title_text) if len(title_text) > 67 else 1.0
title_fontsize = max(11, round(16 * title_ratio))

# Chart — text marks sized by frequency, colored by topic, some rotated
chart = (
    alt.Chart(df)
    .mark_text(fontWeight="bold", align="center", baseline="middle")
    .encode(
        x=alt.X("x:Q", scale=alt.Scale(domain=[0, canvas_w]), axis=None),
        y=alt.Y("y:Q", scale=alt.Scale(domain=[0, canvas_h]), axis=None),
        text="word:N",
        size=alt.Size("size:Q", scale=None, legend=None),
        angle=alt.Angle("angle:Q", scale=None),
        color=alt.Color(
            "category:N", scale=alt.Scale(domain=category_order, range=category_range), legend=alt.Legend(title="Topic")
        ),
        tooltip=["word:N", "category:N", alt.Tooltip("size:Q", title="Font size (freq-scaled)")],
    )
    .properties(
        width=canvas_w,
        height=canvas_h,
        padding={"left": 0, "right": 0, "top": 0, "bottom": 0},
        background=PAGE_BG,
        title=alt.Title(title_text, fontSize=title_fontsize, anchor="middle", color=INK),
    )
    .configure_view(fill=PAGE_BG, strokeWidth=0, continuousWidth=canvas_w, continuousHeight=canvas_h)
    .configure_title(color=INK)
    .configure_legend(
        fillColor=ELEVATED_BG,
        strokeColor=INK_SOFT,
        labelColor=INK_SOFT,
        titleColor=INK,
        labelFontSize=10,
        titleFontSize=10,
    )
)

# Save — pad (never crop) up to the exact canonical target (prompts/library/altair.md "Canvas")
chart.save(f"plot-{THEME}.png", scale_factor=4.0)

TARGET_W, TARGET_H = 3200, 1800
img = Image.open(f"plot-{THEME}.png").convert("RGB")
w, h = img.size
if w > TARGET_W or h > TARGET_H:
    raise SystemExit(
        f"altair vl-convert produced {w}x{h}, exceeds target {TARGET_W}x{TARGET_H}. "
        f"Shrink chart .properties(width=, height=) values and re-render."
    )
if w < TARGET_W or h < TARGET_H:
    canvas = Image.new("RGB", (TARGET_W, TARGET_H), PAGE_BG)
    canvas.paste(img, ((TARGET_W - w) // 2, (TARGET_H - h) // 2))
    canvas.save(f"plot-{THEME}.png")

chart.save(f"plot-{THEME}.html")

Retrieve this implementation

Runnable source as JSON, for any HTTP client: https://api.anyplot.ai/specs/wordcloud-basic/altair/code. Any spec id and library id listed in llms-full.txt fit the same URL shape; every URL below is complete and callable.

{
  "spec_id": "wordcloud-basic",
  "language": "python",
  "library": "altair",
  "page": "https://anyplot.ai/wordcloud-basic/python/altair",
  "hub": "https://anyplot.ai/wordcloud-basic",
  "code_json": "https://api.anyplot.ai/specs/wordcloud-basic/altair/code",
  "spec_json": "https://api.anyplot.ai/specs/wordcloud-basic",
  "render_light_png": "https://storage.googleapis.com/anyplot-images/plots/wordcloud-basic/python/altair/plot-light.png",
  "render_dark_png": "https://storage.googleapis.com/anyplot-images/plots/wordcloud-basic/python/altair/plot-dark.png",
  "interactive_light_html": "https://storage.googleapis.com/anyplot-images/plots/wordcloud-basic/python/altair/plot-light.html",
  "interactive_dark_html": "https://storage.googleapis.com/anyplot-images/plots/wordcloud-basic/python/altair/plot-dark.html",
  "quality_score": 85.0,
  "license": "MIT",
  "guide": "https://anyplot.ai/llms.txt"
}

Part of Basic Word Cloud on anyplot.ai.

Other implementations