diff --git a/CactbotOverlay/CactbotOverlay.cs b/CactbotOverlay/CactbotOverlay.cs
index bc0a4bb3139..ad9692b91d6 100644
--- a/CactbotOverlay/CactbotOverlay.cs
+++ b/CactbotOverlay/CactbotOverlay.cs
@@ -47,6 +47,7 @@ public class CactbotOverlay : OverlayBase<CactbotOverlayConfig>, ILogger {
     private StringBuilder dispatch_string_builder_ = new StringBuilder(1000);
     JsonTextWriter dispatch_json_writer_;
     JsonSerializer dispatch_serializer_;
+    JsonSerializer message_serializer_;
 
     private System.Timers.Timer fast_update_timer_;
     // Held while the |fast_update_timer_| is running.
@@ -107,6 +108,7 @@ public CactbotOverlay(CactbotOverlayConfig config)
       wipe_detector_ = new WipeDetector(this);
       dispatch_json_writer_ = new JsonTextWriter(new System.IO.StringWriter(dispatch_string_builder_));
       dispatch_serializer_ = JsonSerializer.CreateDefault();
+      message_serializer_ = JsonSerializer.CreateDefault();
 
 
       // Our own timer with a higher frequency than OverlayPlugin since we want to see
@@ -641,6 +643,16 @@ public void LogInfo(string format, params object[] args) {
         null);
     }
 
+    // This is an overlayMessage() function call from javascript. We accept a json object of
+    // (command, argument) pairs. Commands are:
+    // - say: The argument is a string which is read as text-to-speech.
+    public override void OverlayMessage(string message) {
+      var reader = new JsonTextReader(new StringReader(message));
+      var obj = message_serializer_.Deserialize<Dictionary<string, string>>(reader);
+      if (obj.ContainsKey("say"))
+        Advanced_Combat_Tracker.ActGlobals.oFormActMain.TTS(obj["say"]);
+    }
+
     // State that is tracked and sent to JS when it changes.
     private class NotifyState {
       public bool sent_data_dir = false;
diff --git a/ui/test/cactbot_test.html b/ui/test/cactbot_test.html
index ef712729a97..e9d10ba26cb 100644
--- a/ui/test/cactbot_test.html
+++ b/ui/test/cactbot_test.html
@@ -111,6 +111,14 @@
     document.addEventListener("onBossFightEnd", function(e) {
       document.getElementById('event').innerText = "BossFight End";
     });
+    document.addEventListener("onLogEvent", function(e) {
+      for (var i = 0; i < e.detail.logs.length; i++) {
+        // Match "/echo tts:<stuff>"
+        var r = e.detail.logs[i].match('00:0038:tts:(.*)');
+        if (r)
+          OverlayPluginApi.overlayMessage(OverlayPluginApi.overlayName, JSON.stringify({ 'say': r[1] }));
+      }
+    });
   </script>
 </head>
 <body>