For AI agents: Find a documentation index at https://talkjs.com/llms.txt (full content at https://talkjs.com/llms-full.txt). Get a markdown version of any page by appending .md to its URL path.
Skip to content

List of profanity words, with a filter tester

Here's a free list of 333 English profanity words and phrases you can reuse under CC BY 4.0. View it, copy it or download it as TXT, JSON or CSV from the bar under the tester. But a list is only half a filter. What it catches depends on how it's matched, so paste a real chat message into the tester and see what gets masked and what gets through. It matches whole words, the same way TalkJS's forbidden word list does.

How other users see it

The seller shipped the wrong size again, what a (hidden information) move. Classic. My assessment goes in the review, so don't be an (hidden information) about the refund. The last two sellers were asses too.

2 words masked

  • bullshit
  • ass

Word list

333 words and phrases · Source: LDNOOBW, license CC BY 4.0

Reading your result

The tester masks a word only when the whole word or phrase is on the list, whatever its capitalization. In the example message, "bullshit" and "ass" are masked. "Classic" and "assessment" stay as they are, even though both contain "ass".

That's why TalkJS matches whole words. A filter that matches letters inside words masks innocent words too, a mistake known as the Scunthorpe problem, after the English town whose name contains a swear word.

Whole-word matching has blind spots, though. Three kinds of words get through:

  • Other forms of a listed word. "ass" is on the list and "asses" isn't, so "asses" stays visible. Add the forms your users actually write.
  • Spaced or swapped letters, like "s h i t" or "sh1t". Spaced spellings work as list entries: add "s h i t" as a phrase. Swapped letters need a custom suppression pattern, a regular expression such as /sh[i1!]t/i, which masks "sh1t" and "sh!t".
  • Entries that start or end with an accented letter or a symbol, like "café" or "a$$". These don't match as standalone words, even when they're on the list. The next section explains why, and a custom pattern handles these too.
What the list does to five wordsbullshit: masked, it's on the list. Classic: kept, ass is only part of the word. assessment: kept, for the same reason. ass: masked, it's on the list. asses: kept, this form isn't on the list.What the list does to five wordsbullshitMaskedon the listClassicKept"ass" is only part of the wordassessmentKept"ass" is only part of the wordassMaskedon the listassesKeptthis form isn't on the list
Whole-word matching keeps "Classic" and "assessment" and misses "asses".

How the matching works

The tester runs the same steps as a TalkJS chat. We copied them from our own source code:

  1. Split the list into lines and trim the spaces around each one.
  2. Lowercase every line, and skip empty lines and lines that start with #.
  3. Escape characters that mean something in a regular expression, like . or *, so each entry is matched as plain text.
  4. Join all entries into one pattern, \b(entry1|entry2|...)\b, with the flags g (every match) and i (ignore case).
  5. Replace each match with the replacement text and keep the rest of the message.
From word list to masked messageYour list has the lines Ass, bullshit with spaces around it, and a comment line. Cleaned up, it becomes ass and bullshit; the comment is skipped. The two entries become one pattern, backslash b, ass or bullshit, backslash b, with the flags g and i. In the message, bullshit is replaced with (hidden information), while Classic and assessment are kept.From word list to masked message1. YOUR LISTAss bullshit # words for the help chat2. TRIMMED, LOWERCASED, COMMENT SKIPPEDassbullshit3. ONE WHOLE-WORD PATTERN\b(ass|bullshit)\bflags: g, i4. THE MESSAGE AS USERS SEE IT…what a (hidden information) move.Classic. My assessment goes in…"Classic" and "assessment" don't match a whole word.
The same steps on a two-word list.

Two details of that pattern decide what your list catches:

  • Word edges. \b marks the edge of a word. In a JavaScript pattern like this one, only the letters A to Z, the digits and the underscore count as word characters. Spaces, punctuation, hyphens and apostrophes count as edges, so the "shit" in "bull-shit" and in "Shit!" is masked. An entry that starts or ends with any other character, like "café", doesn't match as a standalone word. Write a custom pattern for those.
  • Overlapping phrases. When one entry is the start of a longer one, like "fuck" and "fuck buttons", the entry listed first wins. If "fuck" comes first, the phrase shows up as "(hidden information) buttons". Our list puts every phrase before the shorter entries it starts with. Single words that share a start, like "ass" and "asshole", are masked in full whatever the order.

Where the list comes from

The list is the English file of the List of Dirty, Naughty, Obscene, and Otherwise Bad Words (LDNOOBW). Shutterstock made it to keep bad words out of its search suggestions and recommendations, and published it under the CC BY 4.0 license. The English file has 403 entries and last changed on July 13, 2020.

We made three changes. We removed a middle-finger emoji, because a whole-word filter can never match it. We removed 69 entries that would mask normal messages: everyday words and phrases like "sucks", "tied up" and "snowballing", and medical terms like "rectum". And we moved longer phrases ahead of the words they start with. That leaves 333 entries, 108 of them phrases of two or more words.

Two other free lists show up in the same searches:

ListEntriesLicenseWhat to know
LDNOOBW English (this page)403 (we use 333)CC BY 4.0Built to filter search suggestions. Last changed July 2020.
Carnegie Mellon bad-words.txt1,383None statedFrom 2009. Includes words like "black", "jesus" and "muslim".
profane-words2,725WTFPLCompiled from several lists. Its README warns of false positives.

A longer list isn't a better one. Every extra entry is one more way to mask a normal message, so start with a short list and add words when your users need them.

Add the list to your TalkJS chat

TalkJS includes a forbidden word list on every plan, and you set it up without code:

  1. Download the TXT file. Its license lines start with #, so TalkJS ignores them.
  2. In the TalkJS dashboard, open the Chat UI tab and go to Mask unwanted content.
  3. Choose a mode. The default is No suppression, and the word list field appears once you pick another mode. Masked words show as "(hidden information)" in each user's language, or as your own replacement text.
  4. Paste the list under Forbidden words, one word or phrase per line. The TXT file is 3,678 characters and the field takes 100,000, so there's room for the forms you add.

We suggest Selective suppression. Other users see the replacement text, but senders don't see their own words masked, which makes the filter harder to work around. These settings are per role, so a marketplace can give buyers and sellers different lists.

If the list masks a word your users need, delete that line. Suppression exceptions work too: TalkJS lets a match through when an exception pattern matches it, including matches from the word list. The content suppression guide covers custom patterns and exceptions, and chat moderation shows the rest of TalkJS's moderation tools.

FAQs

Yes. The list is licensed CC BY 4.0, which allows any use, including commercial use, as long as you credit the source, link to the license and say what you changed. The TXT and JSON downloads include those lines.

No. TalkJS replaces each masked word or phrase with the replacement text, leaves the rest of the message intact, and still sends it.

This tester and the word list are free to use. In your own app, TalkJS's forbidden word list is included on every plan, and every plan has a free development mode, so you can set it up and test it before you go live.