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.

""" anyplot.ai
wordcloud-basic: Basic Word Cloud
Library: letsplot 4.9.0 | Python 3.13.13
Quality: 94/100 | Updated: 2026-05-06
"""
import math
import os
import numpy as np
import pandas as pd
from lets_plot import (
LetsPlot,
aes,
element_blank,
element_rect,
element_text,
geom_text,
ggplot,
ggsize,
labs,
scale_color_manual,
scale_size_identity,
theme,
theme_void,
xlim,
ylim,
)
from lets_plot.export import ggsave
LetsPlot.setup_html()
# Theme tokens
THEME = os.getenv("ANYPLOT_THEME", "light")
PAGE_BG = "#FAF8F1" if THEME == "light" else "#1A1A17"
INK = "#1A1A17" if THEME == "light" else "#F0EFE8"
INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0"
# Okabe-Ito palette (first series always #009E73)
IMPRINT = ["#009E73", "#C475FD", "#4467A3", "#BD8233", "#AE3030", "#2ABCCD", "#954477"]
# Data - Programming language popularity
np.random.seed(42)
words_data = {
"Python": 100,
"JavaScript": 92,
"Java": 85,
"TypeScript": 78,
"SQL": 75,
"HTML": 70,
"CSS": 68,
"Rust": 62,
"Go": 58,
"C++": 55,
"Kotlin": 52,
"Swift": 50,
"Shell": 48,
"Ruby": 45,
"PHP": 42,
"Scala": 38,
"R": 35,
"Perl": 32,
"Dart": 30,
"Julia": 28,
"MATLAB": 26,
"Haskell": 24,
"Lua": 22,
"Clojure": 20,
"Elixir": 18,
"GraphQL": 35,
}
words = list(words_data.keys())
frequencies = list(words_data.values())
# Sort by frequency (largest first for better placement)
sorted_indices = np.argsort(frequencies)[::-1]
words = [words[i] for i in sorted_indices]
frequencies = [frequencies[i] for i in sorted_indices]
# Canvas dimensions
canvas_width = 280
canvas_height = 140
# Scale font sizes for readability
min_freq, max_freq = min(frequencies), max(frequencies)
min_size, max_size = 9, 26
sizes = []
for freq in frequencies:
normalized = (freq - min_freq) / (max_freq - min_freq)
size = min_size + (normalized**0.6) * (max_size - min_size)
sizes.append(size)
# Spiral word placement with collision detection
placed = []
positions_x = []
positions_y = []
char_width_ratio = 0.52
for word, size in zip(words, sizes, strict=True):
word_width = len(word) * size * char_width_ratio
word_height = size * 1.05
t = 0
step = 0.08
max_iterations = 4000
placed_word = False
while t < max_iterations and not placed_word:
r = 0.1 + t * 0.08
angle = t * 0.35
x = canvas_width / 2 + r * math.cos(angle) * 0.95
y = canvas_height / 2 + r * math.sin(angle)
margin = 3
if (
x - word_width / 2 < margin
or x + word_width / 2 > canvas_width - margin
or y - word_height / 2 < margin
or y + word_height / 2 > canvas_height - margin
):
t += step
continue
collision = False
padding = 0.6
for px, py, pw, ph in placed:
if abs(x - px) < (word_width / 2 + pw / 2 + padding) and abs(y - py) < (word_height / 2 + ph / 2 + padding):
collision = True
break
if not collision:
placed.append((x, y, word_width, word_height))
positions_x.append(x)
positions_y.append(y)
placed_word = True
else:
t += step
if not placed_word:
positions_x.append(None)
positions_y.append(None)
# Build dataframe with placed words
df_data = []
for word, freq, size, x, y in zip(words, frequencies, sizes, positions_x, positions_y, strict=True):
if x is not None and y is not None:
df_data.append({"word": word, "frequency": freq, "size": size, "x": x, "y": y})
df = pd.DataFrame(df_data)
# Assign Okabe-Ito colors to words
df["color"] = [IMPRINT[i % len(IMPRINT)] for i in range(len(df))]
# Plot with theme-adaptive chrome
anyplot_theme = theme(
plot_background=element_rect(fill=PAGE_BG, color=PAGE_BG),
panel_background=element_rect(fill=PAGE_BG, color=PAGE_BG),
plot_title=element_text(size=24, color=INK, hjust=0.5),
legend_position="none",
axis_title=element_blank(),
axis_text=element_blank(),
)
plot = (
ggplot(df, aes(x="x", y="y", label="word", size="size", color="color"))
+ geom_text(fontface="bold")
+ scale_size_identity()
+ scale_color_manual(values=df["color"].unique(), guide="none")
+ xlim(0, canvas_width)
+ ylim(0, canvas_height)
+ labs(title="wordcloud-basic · letsplot · anyplot.ai")
+ theme_void()
+ anyplot_theme
+ ggsize(1600, 900)
)
# Save
ggsave(plot, f"plot-{THEME}.png", path=".", scale=3)
ggsave(plot, f"plot-{THEME}.html", path=".")
Part of Basic Word Cloud on anyplot.ai.