Is Text to Speech Google Docs safe?
Text to Speech Google Docs sends page hostname and a snippet of the text you read aloud to Amplitude on every TTS session.
Each time you start a text-to-speech session, the extension transmits an analytics event to Amplitude that includes the site hostname, the first 32 characters of the text being read, the detected language, voice settings, and a stable anonymous user ID persisted across sessions. A secondary event also sends a normalised URL path pattern. Both identifiers — an anonymousUserId UUID and a device ID stored in chrome.storage.local — are created once and never rotated, enabling cross-session tracking.
AI-generated. Findings may contain errors. Those marked Verified have been manually reviewed.
Publishers can request a review.
Findings
TTS Sessions Send Text Excerpts to Amplitude
Starting a text-to-speech session builds an Amplitude event with the page host, first 32 characters of the first text chunk, voice/rate settings, language/length metadata, and persistent anonymous user/device IDs.
Request fired, body empty.
You start text-to-speech on the current page.
The extension prepares an analytics event that includes where the reading happened and a short excerpt of the text.
| Field | Value | Why it matters | |
|---|---|---|---|
Page host | docs.google.com (illustrative) | Shows which website was open when you started the reading session. | |
Text excerpt | Quarterly planning notes for (illustrative) | Includes the first 32 characters of the first text chunk read by the extension. | |
Anonymous user ID | 550e8400-e29b-41d4-a716-446655440000 (illustrative) | Lets separate reading sessions be tied to the same browser profile over time. | |
Device ID | dev_1720818929123_q9x4m2ka (illustrative) | Adds another stable identifier for the browser where the extension is installed. | |
Voice and reading settings | voice_name: Google US English, rate: 1, text_lang_detected: en, text_length: 1842 (illustrative) | Describes how the reading session was configured. | |
Normalized URL path | /document/d/{ID}/edit (illustrative) | For extraction-result analytics, this records a generalized page path pattern instead of the full path. |
| Content-Type | application/json |
Session event construction and Amplitude submission
const s = (() => {
try {
return t.trackUrl ? new URL(t.trackUrl).host : ""
} catch {
return ""
}
})(),
m = t.source ?? X(s),
k = await ht(),
N = Date.now(),
y = k !== null && N - k < 6e4,
M = k === null ? 0 : Math.floor((N - k) / (1e3 * 60 * 60 * 24)),
Y = t.paragraphs.reduce((z, ct) => z + ct.text.length, 0),
F = (((i = C[0]) == null ? void 0 : i.text) ?? "").slice(0, 32);
t.textFirstChars = F, I(v.SESSION_START, {
[o.SOURCE]: m,
[o.HOST]: s,
[o.VOICE_NAME]: t.settings.voiceName ?? "",
[o.VOICE_LANG]: t.resolvedVoiceLang,
[o.RATE]: t.settings.rate,
[o.TEXT_LENGTH]: Y,
[o.TEXT_LANG_DETECTED]: t.pageLang ?? "",
[o.VOICE_LANG_MATCHES_PAGE]: J(t.resolvedVoiceLang, t.pageLang),
[o.TEXT_FIRST_CHARS]: F,
[o.HOUR_OF_DAY]: new Date().getHours(),
[o.IS_CONTINUATION]: y,
[o.DAYS_SINCE_LAST_SESSION]: M
}), (async () => await pt({
sessionId: await ft(),
startedAt: t.startedAt,
lastActivityAt: Date.now(),
source: m,
host: s,
atPct: 0
}))();const Ni = "https://api2.amplitude.com/2/httpapi";
async function Ai(e, t, n) {
var a;
const [r, s, o] = await Promise.all([yi(), pn(), Ii()]),
i = typeof chrome < "u" && ((a = chrome.runtime) != null && a.getManifest) ? chrome.runtime.getManifest().version : "unknown";
return {
user_id: r,
device_id: s,
session_id: (n == null ? void 0 : n.sessionId) ?? o,
time: (n == null ? void 0 : n.time) ?? Date.now(),
event_type: e,
app_version: i,
platform: "Chrome Extension",
event_properties: {
...t,
product: "tts-gd"
}
}
}
async function ua(e, t = {}, n) {
const r = "8f4716e33e96712ef837134a2d3f25b8";
try {
const s = await Ai(e, t, n),
o = await fetch(Ni, {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
api_key: r,
events: [s]
}),
keepalive: !0
});
o.ok || console.warn("[TTS GD] analytics track non-2xx", e, o.status)
} catch (s) {
console.warn("[TTS GD] analytics track failed", e, s)
}
}const se = "anonymousUserId";
async function yi() {
const e = await chrome.storage.local.get([se]);
if (typeof e[se] == "string") return e[se];
const t = crypto.randomUUID();
return await chrome.storage.local.set({
[se]: t
}), t
}
const k = "ttsgd_amplitude_device_id",
kt = "ttsgd_amplitude_session_id",
oe = "ttsgd_unique_voices_tried",
$e = "ttsgd_last_session_end_at",
He = "ttsgd_install_action_tracker",
pe = "ttsgd_hanging_session";
function Ie() {
try {
return typeof localStorage < "u"
} catch {
return !1
}
}
function Ti() {
const e = Math.random().toString(36).slice(2, 10);
return `dev_${Date.now()}_${e}`
}
async function pn() {
if (Ie()) {
const n = localStorage.getItem(k);
if (n) return n
}
const e = await chrome.storage.local.get([k]);
if (typeof e[k] == "string") {
const n = e[k];
return Ie() && localStorage.setItem(k, n), n
}
const t = Ti();
return await chrome.storage.local.set({
[k]: t
}), Ie() && localStorage.setItem(k, t), t
}- api2.amplitude.com
Receives the text-to-speech session event through the /2/httpapi endpoint.