From f02bc04c98f48d82e96d7dbb8b74d68b7cbd4c51 Mon Sep 17 00:00:00 2001 From: teahubxyz Date: Wed, 21 Oct 2026 19:01:51 +0000 Subject: [PATCH] asmr: show cost per clip and per minute of audio on every voice --- asmr/index.html | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/asmr/index.html b/asmr/index.html index 9609cbe..5879f4b 100644 --- a/asmr/index.html +++ b/asmr/index.html @@ -28,6 +28,7 @@ summary{cursor:pointer;color:var(--ink)} .v .bar{height:3px;background:var(--line);border-radius:2px;margin-top:6px;overflow:hidden} .v .bar span{display:block;height:100%;width:0;background:var(--acc)} .v.bad{opacity:.4;cursor:default} +.v .n b{font-weight:500;color:var(--acc);font-size:12px;margin-left:6px} .stars{display:flex;gap:2px;flex:none} .stars button{background:none;border:0;color:#3a3a45;font-size:18px;padding:2px;cursor:pointer} .stars button.y{color:#ffd36b} @@ -43,6 +44,7 @@ summary{cursor:pointer;color:var(--ink)}

g*ndora asmr

Voice bake-off. Same 30-second goodnight script through every whisper-capable voice on the Venice worker. Tap to play, star the ones that land. Headphones.

+
Costs

Venice bills TTS per input character, so a clip's cost is the script length × the model's rate. Per minute / hour is that cost spread over the clip's actual length, which is how long ASMR would run. Rates from Venice's model list, Oct 21 2026.

The script

Style direction (Qwen3 only):

@@ -123,7 +125,18 @@ fetch("voices.json").then((r) => r.json()).then((cfg) => { } const el = document.createElement("div"); el.className = "v"; el.innerHTML = `
▶
`; - el.querySelector(".n > span").textContent = v.voice; el.querySelector(".n i").textContent = v.model; + el.querySelector(".n > span").textContent = v.voice; + const rate = (cfg.pricing || {})[v.model]; + const text = (v.lead || "") + (v.tags ? cfg.script.replace("Hey...", "Hey... ") : cfg.script) + (v.tail || ""); + const clip = rate ? rate * text.length / 1e6 : null; + const usd = (x) => "$" + (x < 0.01 ? x.toFixed(4) : x < 1 ? x.toFixed(3) : x.toFixed(2)); + const info = el.querySelector(".n i"); + info.textContent = v.model + (rate ? ` · $${rate}/1M chars` : ""); + if (clip != null) { + const tag = document.createElement("b"); tag.textContent = usd(clip) + "/clip"; el.querySelector(".n > span").after(tag); + const a = new Audio(); a.preload = "metadata"; a.src = "audio/" + v.id + ".mp3"; + a.onloadedmetadata = () => { if (a.duration > 0) { const pm = clip / a.duration * 60; tag.textContent = `${usd(clip)}/clip · ${usd(pm)}/min · ${usd(pm * 60)}/hr`; } }; + } const st = el.querySelector(".stars"); for (let i = 1; i <= 3; i++) { const s = document.createElement("button"); s.textContent = "★"; s.ariaLabel = i + " stars";