Inspiration

While coordinating interns for my local historical society, I started noticing a strange problem in the messages I sent. I would write things like, “Whenever you get a chance, could you look this over before the event? No rush!” because I did not want to sound demanding. But sometimes there actually was a rush: another person was waiting on the document, an event was approaching, or I needed the work finished before I could move on to something else.

What interested me was that neither the literal words nor a traditional “tone detector” really captured what was happening. The request sounded optional, yet the surrounding context made action increasingly expected. As someone interested in computational linguistics, I became fascinated by whether that mismatch could actually be measured. That became UnderTone: a tool for making the gap between a request’s wording and its observable communicative pressure visible.

What it does

UnderTone analyzes workplace-style requests along two independent dimensions. Surface strength measures how forcefully the request is phrased. The requests can be an imperative, a question, hedged with words like “maybe,” or softened with phrases such as “no rush.” Communicative force, on the other hand, looks at observable context like deadlines and dependencies to determine the actual forcefulness of the request.

The difference between the two is the pragmatic gap. A large gap indicates the surrounding language contains stronger signals that action is expected than the request’s phrasing immediately communicates. Users can analyze full threads to watch this divergence develop over time, or use Compose Mode to examine a message before sending it.

UnderTone helps make indirect communication more transparent. In workplaces, classrooms, and group projects, people often soften requests to sound polite, even when deadlines, dependencies, or consequences make the task important. That mismatch can lead to missed deadlines, unnecessary stress, and conflict.

How we built it

Because the original problem was about the difference between phrasing and context, I designed the architecture so those two concepts could not simply collapse into one another. UnderTone first identifies a reproducible request and scores its surface wording using a CCSARP-inspired directness system alongside downgraders and upgraders. Those surface-visible spans are then masked before the second scorer runs.

The Communicative Force scorer therefore receives the masked message and prior masked thread context rather than the Surface score or directness strategy. It independently searches for temporal pressure, consequences, dependencies, accountability, and escalation. Every contribution retains its exact character span, category, and weight, allowing the website to underline the precise phrases responsible for a result. The entire diagnostic engine is deterministic TypeScript and runs locally in the browser, with a Next.js interface built around Analyze, Compose, and Methodology pages.

Challenges we ran into

The hardest problem was realizing how easy it is to accidentally turn a pragmatics tool into an intent detector. Early on, many features that seemed like evidence of “urgency” were actually just stronger wording. For example, “urgent” changes how a request sounds, while “Legal is blocked on this” describes an external dependency. If both went into the same score, UnderTone would simply rediscover tone under another name.

That forced me to solve the problem architecturally rather than continuously patching individual examples. I introduced masking, strict evidence partitioning, event deduplication, and tests that change surface phrasing while requiring force to remain invariant. I also had to resist making every difficult example pass: several unsupported request forms remain explicitly documented as limitations instead of being hidden behind one-off rules.

Accomplishments that we're proud of

I’m most proud that UnderTone can explain itself. If it reports increased Communicative Force because of “by Thursday” or “blocked on Legal,” the interface highlights those exact words. A user can trace a score all the way back to the language that produced it instead of trusting an opaque model output.

I also built much more evaluation infrastructure than I originally expected for a hackathon project. UnderTone passes thousands of automated tests, all 120 of its core minimal-pair gap-direction checks, deterministic evidence reconstruction and scorer-partition tests, and a formal internal release suite. Importantly, that suite reports 12 PASS, 1 PARTIAL, 1 NOT MEASURABLE, and 0 FAIL rather than pretending that internal specification tests constitute human validation.

What we learned

Building UnderTone changed how I thought about computational linguistics. I initially approached the problem as one of finding the right phrases to detect, but the more interesting question became: what information should a system be allowed to use to make a particular claim? Once we required Surface and Force to remain mechanically separable, the architecture itself became part of the linguistic argument.

I also learned that useful language technology does not always require a larger language model. For this problem, making every assumption inspectable was more important than generating the most flexible prediction possible. The constraints of a deterministic system actually forced me to define the phenomenon more carefully.

What's next for Undertone

The next major step is human validation. UnderTone’s current evaluation tells the user whether the implementation follows its own specification. However, it doesn't tell that a +3 pragmatic gap corresponds to exactly three points of perceived pressure for a human reader. I would like to collect independent judgments of request phrasing and contextual pressure and compare them against UnderTone without modifying the benchmark around the results.

Beyond that, I want to broaden the scope of Undertone to casual conversations instead of only workplace requests and investigate how pragmatic expectations vary across linguistic and cultural backgrounds. That last question especially interests me because the same degree of directness can be interpreted very differently across languages and communities. In the long term, I hope to make the linguistic signals surrounding a request easier to see.

Built With

Share this project:

Updates