Skip to content

Add voiced-dialogue#13127

Merged
runelite-github-app[bot] merged 6 commits into
runelite:masterfrom
grabartley:voiced-dialogue
Jul 1, 2026
Merged

Add voiced-dialogue#13127
runelite-github-app[bot] merged 6 commits into
runelite:masterfrom
grabartley:voiced-dialogue

Conversation

@grabartley

@grabartley grabartley commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Voiced Dialogue

Gielinor, out loud. Voiced Dialogue gives every NPC and your own adventurer a real, real-time AI voice, turning silent text boxes into a living world you can actually hear. Walk up, talk, listen, that's the whole setup.

What makes it special

  • 13,700+ NPCs already voiced. The plugin knows who's speaking and picks the right voice from a bundled race/gender table, so there are no lookups or lag mid-conversation. Meet someone added in a later game update and an optional auto-learn quietly looks them up on the wiki once and remembers them.
  • Accents with real craft. 12 races and 13 regional origins each map to their own accent, following the real-world cultures the lands are based on: Scottish dwarves, South London trolls, Irish leprechauns, Dracula-esque vampyres, Norse Fremennik raiders, the gothic dread of Morytania, and more.
  • 6,400+ hand-written personalities. On top of the accents, thousands of NPCs get a bespoke persona with its own delivery style and speaking pace, so the icons of Gielinor sound like themselves.
  • Real emotion. Each line's delivery, happy, sad, angry, scared, or neutral, is read from the speaker's chat-head expression, so a furious dwarf actually sounds furious.
  • You star in it too. Set your own hero's accent, persona, and pace: the dashing knight, the gruff mercenary, or the chaos goblin of your dreams.
  • Any language, any vibe. Pipe dialogue through another language, or layer a speaking style over it (Gen Z slang, pirate speak, Shakespearean, and more).
  • Atmosphere on tap. Lines spoken underground pick up a cave echo, so dungeons and sewers feel enclosed.
  • Friendly by default. Built-in profanity filtering needs no setup.

Everything runs off the game thread and replays already-heard lines from a local cache, so the client stays snappy even when you mash through dialogue.

Setup & privacy

Speech is synthesized by OpenRouter, so the plugin needs a free OpenRouter API key. Until one is set it stays silent with a one-time notice pointing you to the setting. While active, the dialogue text being voiced is sent to OpenRouter over HTTPS using your key (disclosed in the descriptor warning= and in-plugin before it does anything); nothing else leaves your machine. The API key is stored as a RuneLite secret, never logged, and sent only to OpenRouter.

For reviewers

  • All HTTP/JSON goes through RuneLite's injected OkHttpClient / Gson (no new OkHttpClient() / new Gson()).
  • No third-party runtime dependencies are bundled; all bundled resources load via getResourceAsStream.
  • Audio uses javax.sound.sampled.SourceDataLine to stream synthesized PCM at low latency and cut the current line off when the player advances dialogue, the same pattern as the merged NaturalSpeech plugin; confirmed acceptable with the team for this use case.
  • MIT licensed, public repo, README documents every feature.

@runelite-github-app

runelite-github-app Bot commented Jul 1, 2026

Copy link
Copy Markdown

@grabartley grabartley changed the title add voiced-dialogue Add voiced-dialogue Jul 1, 2026
@grabartley
grabartley marked this pull request as draft July 1, 2026 13:21
@runelite-github-app

runelite-github-app Bot commented Jul 1, 2026

Copy link
Copy Markdown

Feedback:

  • javax.sound.sampled.AudioSystem.getSourceDataLine: Use of javax.sound, use net.runelite.client.audio.AudioPlayer instead
  • javax.sound.sampled.SourceDataLine.open: Use of javax.sound, use net.runelite.client.audio.AudioPlayer instead
  • javax.sound.sampled.SourceDataLine.close: Use of javax.sound, use net.runelite.client.audio.AudioPlayer instead
  • javax.sound.sampled.FloatControl$Type.MASTER_GAIN: Use of javax.sound, use net.runelite.client.audio.AudioPlayer instead
  • javax.sound.sampled.SourceDataLine.drain: Use of javax.sound, use net.runelite.client.audio.AudioPlayer instead
  • javax.sound.sampled.FloatControl.getMaximum: Use of javax.sound, use net.runelite.client.audio.AudioPlayer instead
  • javax.sound.sampled.AudioSystem: Use of javax.sound, use net.runelite.client.audio.AudioPlayer instead
  • javax.sound.sampled.Control: Use of javax.sound, use net.runelite.client.audio.AudioPlayer instead
  • javax.sound.sampled.SourceDataLine.stop: Use of javax.sound, use net.runelite.client.audio.AudioPlayer instead
  • javax.sound.sampled.SourceDataLine: Use of javax.sound, use net.runelite.client.audio.AudioPlayer instead
  • javax.sound.sampled.SourceDataLine.write: Use of javax.sound, use net.runelite.client.audio.AudioPlayer instead
  • javax.sound.sampled.FloatControl: Use of javax.sound, use net.runelite.client.audio.AudioPlayer instead
  • javax.sound.sampled.AudioFormat.<init>: Use of javax.sound, use net.runelite.client.audio.AudioPlayer instead
  • javax.sound.sampled.SourceDataLine.flush: Use of javax.sound, use net.runelite.client.audio.AudioPlayer instead
  • javax.sound.sampled.FloatControl.setValue: Use of javax.sound, use net.runelite.client.audio.AudioPlayer instead
  • javax.sound.sampled.FloatControl.getMinimum: Use of javax.sound, use net.runelite.client.audio.AudioPlayer instead
  • javax.sound.sampled.AudioFormat: Use of javax.sound, use net.runelite.client.audio.AudioPlayer instead
  • javax.sound.sampled.SourceDataLine.isControlSupported: Use of javax.sound, use net.runelite.client.audio.AudioPlayer instead
  • javax.sound.sampled.SourceDataLine.getControl: Use of javax.sound, use net.runelite.client.audio.AudioPlayer instead
  • javax.sound.sampled.FloatControl$Type: Use of javax.sound, use net.runelite.client.audio.AudioPlayer instead
  • javax.sound.sampled.LineUnavailableException: Use of javax.sound, use net.runelite.client.audio.AudioPlayer instead
  • javax.sound.sampled.SourceDataLine.start: Use of javax.sound, use net.runelite.client.audio.AudioPlayer instead
  • javax.sound.sampled.Control$Type: Use of javax.sound, use net.runelite.client.audio.AudioPlayer instead

A new scan will be automatically performed on the next push to the PR branch.


Internal use only: Reviewer details Maintainer details

@grabartley
grabartley marked this pull request as ready for review July 1, 2026 13:58
@grabartley
grabartley marked this pull request as draft July 1, 2026 14:23
@grabartley
grabartley marked this pull request as ready for review July 1, 2026 15:09
@grabartley

Copy link
Copy Markdown
Contributor Author

Note on the RuneLite Plugin Hub Checks flag: it's the expected javax.sound usage.

This plugin streams synthesized PCM at low latency and interrupts the current line the moment the player advances dialogue. net.runelite.client.audio.AudioPlayer plays finite Clips with no way to stop playback early, so it can't express that; the plugin uses javax.sound.sampled.SourceDataLine directly instead, the same pattern as the merged NaturalSpeech plugin. A maintainer confirmed javax.sound is acceptable for this use case.

@riktenx

riktenx commented Jul 1, 2026

Copy link
Copy Markdown
Member
  • use of subprocess execution is not allowed
  • use of thread sleep is not allowed; use a scheduled executor etc. instead
  • use of thread interrupt is not allowed

@runelite-github-app runelite-github-app Bot removed the waiting for author waiting for the pr author to make changes or respond to questions label Jul 1, 2026
@grabartley

Copy link
Copy Markdown
Contributor Author
  • use of subprocess execution is not allowed

    • use of thread sleep is not allowed; use a scheduled executor etc. instead

    • use of thread interrupt is not allowed

Hi @riktenx! Thanks for the review. I have removed the local backend entirely so there is no more subprocess execution. I also removed use of Thread.sleep() and Thread.interrupt() in favour of a scheduled executor.

Comment thread plugins/voiced-dialogue Outdated
@runelite-github-app
runelite-github-app Bot merged commit 6e3ae28 into runelite:master Jul 1, 2026
2 of 3 checks passed
@grabartley
grabartley deleted the voiced-dialogue branch July 1, 2026 21:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants