The news is currently very bleak. Just the pandemic alone is enough to make you want to never read the news again. Thankfully there’s a solution: “Bweaking NuwuS” (@BBCBweaking), a Twitter account that posts news stories passed through an “UwU” kaomoji filter to make them “cutesy”. The result is some serious dark comedy, with posts about corruption, sickness, and death translated into something exceedingly saccharine.

After reading a bit about the automation behind the news account, I thought it would be fun to do something similar and make a browser extension to convert Twitch chat into “OwO”-speak on the fly.

I’m not sorry.

Okay, I’m a little sorry.

OwO what’s this?

First a little background. Both “OwO” and “UwU” are emoticons, “used to express warm, happy, or affectionate feelings.” According to

In the case of uwu, the u is supposed to represent eyes and w, the mouth. The overall effect is meant to look cute, depicting a face of someone feeling all warm and fuzzy—as if the eyes are closed and the nose and mouth are scrunched up in a playful, smiling “Aww!” As for owo, the o‘s depict eyes wide open with surprise.

Uwu is connected to kaomoji, a type of Japanese emoticon which incorporates special characters used in Japanese writing. Kaomoji are considered kawaii, or “cute,” and often draw on anime and manga. Uwu is likely a simplified form of such kaomoji as (o・ω・o).

“OwO”, “UwU”, and other kaomoji are often used to make text seem ‘cute’ (◕‿◕). Although since it’s adoption by the furry community, particularly in regards to sexual roleplay, these emoticons are more often than not used ironically.

The Extension

Here it is in all its glory: Twitch Chat OwO! It’s a Chrome extension that converts all chat messages to cutesy “OwO”-speak. For better or worse.

The extension is loaded with the chat window and translates all messages as they are received. Usernames, mentions (@ pings), and emotes in each message are not translated, and neither are chat-adjacent things such as subscription alerts, raids, and point redemptions.

Usernames in this screenshot are hidden for their own protection. (⌒‿⌒)

How It Works

The ‘@zuzak/owo’ package for Node.js

There are three parts to this: the OwO translation, the Twitch chat parsing, and the Chrome extension that ties it all together.

I’m not a web developer by trade and this was thrown together in an evening, so forgive me if anything is out-of-sorts.

OwO Translation

This uses the JavaScript owo translator module from the Bweaking NuwuS documentation to handle the OwO translation. This Node.js module also imports the random-item and replace-string modules and can be downloaded, with dependencies, via the Node package manager (NPM):

npm i @zuzak/owo

Strings can then be translated into “OwO” by requiring the module and calling the owo function:

const owo = require('@zuzak/owo')

console.log(owo('I have no mouth and I must scream'))
// HIIII! I haz nu mouth and I must scweam XDDD

console.log(owo.translate('I have no mouth and I must scream'))
// I haz nu mouth and I must scweam

As Node.js modules are not made to be used client-side, I used browserify to pack the Twitch parsing code and the owo module into a deployable script:

browserify main.js -o twitchowo.js

Twitch Parsing

The Twitch message parsing is handled in vanilla JavaScript. After the extension imports the owo module, it then creates a MutationObserver object to track changes to the page (document). The mutation observer reports when nodes are added to the page, and the script searches through these to find the chat messages.

All message containers have a data-a-target="chat-line-message" field. This parent element contains all of the message data including the timestamp, username, message, and emotes. The child text components of the message, potentially separated by emotes, all contain the data-a-target="chat-message-text" field. Within the added node list provided by the mutator the script searches for these text components by the data field and “OwO”‘s them:

let fragments = node.querySelectorAll("[data-a-target='chat-message-text']");
for(let msg of fragments) {
	if(msg.innerText.length === 0 || msg.innerText === ' ') continue;  // empty, no translation
	msg.innerText = owo(msg.innerText);

Chrome Extension

Last but not least, the Chrome extension provides a convenient way to inject this into Twitch pages on the fly. It’s just a simple manifest.json file which tells the browser which JavaScript file to load and on which websites. In this case, all Twitch pages:

	"manifest_version": 2,
	"name": "Twitch Chat OwO",
	"version": "1.0.0",
	"content_scripts": [
			"matches": ["*"],
			"js": ["twitchowo.js"]

In the Chrome extensions settings, “Developer mode” needs to be enabled and then the directory containing the manifest and packed JavaScript can be loaded as an “unpacked” extension.

Potential Improvements

I just threw this together quickly as a proof of concept, so it’s very unpolished. It’s also missing a number of “basic” features, such as:

  • Being able to turn it on and off without reloading the page
  • Compatibility with BetterTTV and other chat extensions
  • Parsing existing messages when the extension is first enabled
  • Translating messages back to their unaltered state
  • Being able to translate subscriber, raid, and redemption alerts
  • Being listed for download from the Chrome web store
  • Not existing in the first place

This was more of a fun experiment than anything else, so I have no plans to develop it further or add additional features. You’re more than welcome to take on any of these tasks yourself – everything is licensed under MIT.


If you want to play with this yourself, the source code is all available on GitHub, as is a download to the deployable version of the extension. Follow the instructions on the README to set it up, and have fun!


Leave a Reply

Avatar placeholder

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Would you like to know more?