diff --git a/app.py b/app.py
index 7d0d491..d4e8f83 100644
--- a/app.py
+++ b/app.py
@@ -1089,10 +1089,27 @@ def _ranked(words, exclude: set[str], limit: int) -> list[dict]:
out = []
for z, w in scored[:limit]:
ph = phones_for(w)
- out.append({"word": w, "syl": pronouncing.syllable_count(ph) if ph else 0})
+ out.append({"word": w, "z": round(z, 1),
+ "syl": pronouncing.syllable_count(ph) if ph else 0})
return out
+_homophone_index: dict[str, list[str]] | None = None
+
+
+def get_homophones(w: str, phones: str) -> list[str]:
+ global _homophone_index
+ if _homophone_index is None:
+ pronouncing.init_cmu()
+ idx: dict[str, list[str]] = defaultdict(list)
+ for word, ph in pronouncing.pronunciations:
+ if word.isalpha() and zipf_frequency(word, "en") >= 3.0:
+ idx[DIGITS.sub("", _norm_r(ph))].append(word)
+ _homophone_index = idx
+ return [h for h in _homophone_index.get(DIGITS.sub("", phones), [])
+ if h != w][:6]
+
+
@app.get("/api/word")
def word_info(word: str):
"""Phonetic anatomy of a word: phones, syllables, stress, rime."""
@@ -1107,6 +1124,7 @@ def word_info(word: str):
return {"word": w, "known": True,
"phones": DIGITS.sub("", phones), "syl": len(stress),
"stress": stress, "rime": rime,
+ "homophones": get_homophones(w, phones),
"zipf": round(zipf_frequency(w, "en"), 1)}
diff --git a/static/index.html b/static/index.html
index 970e1f0..7ad7979 100644
--- a/static/index.html
+++ b/static/index.html
@@ -4,7 +4,7 @@
RhymePad — a scratchpad for poets & rappers
-
+
@@ -198,18 +198,11 @@
border-radius: 10px; padding: 12px 14px; margin: 10px 0 4px;
}
.defhead { display: flex; align-items: baseline; gap: 8px; font-size: 14px; }
- .defhead .phon { color: var(--ink-dim); font-size: 12px; }
.defx { margin-left: auto; cursor: pointer; color: var(--ink-dim); font-size: 15px; }
.defx:hover { color: var(--r6); }
- .defs { margin: 8px 0 10px; padding-left: 18px; font-size: 13px; line-height: 1.55; }
- .defs li { margin: 4px 0; }
- .defs i {
- color: var(--accent-2); font-style: normal; font-size: 11px;
- text-transform: uppercase; letter-spacing: .06em; margin-right: 4px;
- }
.btn.small { padding: 5px 10px; font-size: 12px; }
.def-actions { display: flex; gap: 8px; }
- .defphon { font-size: 12px; margin: 0 0 10px; line-height: 1.6; }
+ .defphon { font-size: 12px; margin: 8px 0 10px; line-height: 1.6; }
.defphon i { color: var(--accent-2); font-style: normal; }
.chip {
font-size: 13px; background: var(--panel-2); border: 1px solid var(--line);
@@ -217,6 +210,8 @@
transition: all .12s;
}
.chip:hover { border-color: var(--accent); color: var(--accent); transform: translateY(-1px); }
+ .chip.common { font-weight: 600; }
+ .chip.rare { color: var(--ink-dim); opacity: .75; }
.chip.near { border-style: dashed; color: var(--ink-dim); }
.chip.near:hover { color: var(--accent); }
.muted { color: var(--ink-dim); font-size: 13px; line-height: 1.6; }
@@ -286,7 +281,7 @@ Double-click any word to look it up on the right.">