rsml
v3.3.5
Published
Rich Speech Markup Language annotation toolkit for rich transcription of speech.
Maintainers
Readme
RSML Annotator
RSML (Rich Speech Markup Language) annotation UI widget for transcription and speech datasets.
Install
npm install rsmlBrowser Usage
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>RSML Example</title>
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet" />
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/font/bootstrap-icons.min.css" />
</head>
<body>
<div class="container mt-4">
<!-- Textarea + Render -->
<div class="row mt-3">
<div class="col-12 col-md-6 mb-3 mb-md-0">
<textarea id="tag-textarea" class="form-control" rows="10"
placeholder="Type @ for tags, # for entities, ! for languages..."
></textarea>
</div>
<div class="col-12 col-md-6">
<div id="rendered-transcript"
class="card p-3"
style="height: 250px; overflow-y: auto; background: #f9f9f9"></div>
</div>
</div>
</div>
<script type="module">
import RSMLAnnotator from "https://cdn.jsdelivr.net/npm/rsml@latest/rsml.esm.js";
new RSMLAnnotator({
textarea: "#tag-textarea",
output: "#rendered-transcript"
});
</script>
</body>
</html>
React Usage
npm install rsmlimport { useEffect, useRef } from "react";
import RSMLAnnotator from "rsml";
export default function App() {
const textareaRef = useRef(null);
const outputRef = useRef(null);
const annotatorRef = useRef(null); // 👈 prevent multiple initializations
useEffect(() => {
if (
textareaRef.current &&
outputRef.current &&
!annotatorRef.current
) {
annotatorRef.current = new RSMLAnnotator({
textarea: textareaRef.current,
output: outputRef.current,
});
}
}, []);
return (
<div style={{ padding: "2rem" }}>
<h2>🗣️ RSML Annotator (React + Vite + CDN ESM)</h2>
<div style={{ display: "flex", gap: "2rem", flexWrap: "wrap" }}>
<textarea
ref={textareaRef}
rows="10"
style={{ flex: 1, minWidth: "300px", padding: "1rem" }}
placeholder="Type @ for tags, # for entities, ! for languages..."
/>
<div
ref={outputRef}
className="rsml-output"
style={{
flex: 1,
minWidth: "300px",
minHeight: "250px",
overflowY: "auto",
padding: "1rem",
background: "#f9f9f9",
border: "1px solid #ccc",
}}
/>
</div>
</div>
);
}Extending the vocabulary
Every configurable tag/entity/language list can be grown after construction
with add(category, value, label) / remove(category, value) — an
already-open editor picks up the change immediately, with no re-render or
retyping needed.
annotator.add("hesitations", "@meh");
annotator.add("entities", "PRODUCT", "Product Name");
annotator.add("languages", "kok", "Konkani");
annotator.remove("entities", "PRODUCT");category is one of:
| category | what value is | what label is |
|---|---|---|
| hesitations | an @tag (with or without the @) | — |
| isolatedParalinguistics | an @tag | — |
| isolatedOther | an @tag | — |
| disfluencySpans | a span base name (no @, no -start/-end) | — |
| paralinguisticSpans | a span base name | — |
| prosodySpans | a span base name | — |
| entities | a #-prefixed entity type code | display label (optional) |
| languages | a !-prefixed language code | display label (optional) |
| dialects | a $$-prefixed dialect code | display label (optional) |
| domains | a !!-prefixed domain code | display label (optional) |
add/remove return true if they changed something, false for a no-op
(e.g. removing a name that wasn't registered). They throw on an unknown
category, an invalid/empty value, or — for the @tag and span families,
which each share one namespace across their sibling categories — a name
already registered under a different category in that family.
Dialect and domain markup
Alongside ! (code-mix / language) and # (entity), two more bracket
prefixes are available, each backed by its own vocabulary registered via
add/remove above:
$$— dialect:$$TG[వస్తున్నా](వస్తున్నాను)marks a phrase as a specific dialect (e.g.add("dialects", "TG", "Telangana Telugu")).!!— domain:!!MED[BP](రక్తపోటు)marks a phrase as belonging to a specific domain/register (e.g.add("domains", "MED", "Medical")).
Like !/#/$, the type code after the prefix is optional — $$[…](…)
and ! render as "unspecified" dialect/domain.
Display settings
A settings button next to the Verbatim/Normalized switch opens a small
popup with a Hide disfluencies checkbox. It hides hesitations,
disfluencySpans, and isolatedOther tags in the rendered output —
the source text and editor are untouched, so switching it on/off never
loses anything.
A repair span written with the documented reparandum - repair
convention is a special case: only the reparandum (and the -
separator) is hidden, keeping the corrected text visible.
Source: @repair-start ఢిల్లీ కాదు - ముంబై @repair-end వెళ్ళాను.
Shown (disfluencies visible): ఢిల్లీ కాదు - ముంబై వెళ్ళాను.
Shown (disfluencies hidden): ముంబై వెళ్ళాను.A repair span with no - separator is hidden wholesale, like any
other disfluency.
