diff --git a/src/sbbs3/atcodes.cpp b/src/sbbs3/atcodes.cpp
index 24f8f9416411070264adbeb9fa6fc1f93d3801fa..24bee8c1f331a8fec4639707eeb9aebe6710bbc0 100644
--- a/src/sbbs3/atcodes.cpp
+++ b/src/sbbs3/atcodes.cpp
@@ -166,6 +166,11 @@ const char* sbbs_t::atcode(char* sp, char* str, size_t maxlen)
 		return nulstr;
 	}
 
+	if(strncmp(sp, "WIDE:", 5) == 0) {
+		wide(sp + 5);
+		return(nulstr);
+	}
+
 	if(!strcmp(sp,"VER"))
 		return(VERSION);
 
diff --git a/src/sbbs3/con_out.cpp b/src/sbbs3/con_out.cpp
index 54544c2f99ce4039e5ee7e5a67750c96cade48fa..12a3e14197cf33e8b11d382791412a44c2676e41 100644
--- a/src/sbbs3/con_out.cpp
+++ b/src/sbbs3/con_out.cpp
@@ -603,6 +603,21 @@ void sbbs_t::center(char *instr)
 	newline();
 }
 
+void sbbs_t::wide(const char* str)
+{
+	long term = term_supports();
+	while(*str != '\0') {
+		if((term&UTF8) && *str >= '!' && *str <= '~')
+			outchar((enum unicode_codepoint)(UNICODE_FULLWIDTH_EXCLAMATION_MARK + (*str - '!')));
+		else {
+			outchar(*str);
+			outchar(' ');
+		}
+		str++;
+	}
+}
+
+
 // Send a bare carriage return, hopefully moving the cursor to the far left, current row
 void sbbs_t::carriage_return(void)
 {
diff --git a/src/sbbs3/js_console.cpp b/src/sbbs3/js_console.cpp
index ff21588a54968c4661bdbc9a756163a8a204b783..e4a53a92e041d7571e1fa5340e3e4459f045d4e6 100644
--- a/src/sbbs3/js_console.cpp
+++ b/src/sbbs3/js_console.cpp
@@ -1420,6 +1420,34 @@ js_center(JSContext *cx, uintN argc, jsval *arglist)
     return(JS_TRUE);
 }
 
+static JSBool
+js_wide(JSContext *cx, uintN argc, jsval *arglist)
+{
+	jsval *argv=JS_ARGV(cx, arglist);
+    JSString*	str;
+	sbbs_t*		sbbs;
+	char*		cstr;
+	jsrefcount	rc;
+
+	if((sbbs=(sbbs_t*)js_GetClassPrivate(cx, JS_THIS_OBJECT(cx, arglist), &js_console_class))==NULL)
+		return(JS_FALSE);
+
+	JS_SET_RVAL(cx, arglist, JSVAL_VOID);
+
+	str = JS_ValueToString(cx, argv[0]);
+	if (str == NULL)
+		return(JS_FALSE);
+
+	JSSTRING_TO_MSTRING(cx, str, cstr, NULL);
+	if(cstr==NULL)
+		return JS_FALSE;
+	rc=JS_SUSPENDREQUEST(cx);
+	sbbs->wide(cstr);
+	free(cstr);
+	JS_RESUMEREQUEST(cx, rc);
+    return(JS_TRUE);
+}
+
 static JSBool
 js_saveline(JSContext *cx, uintN argc, jsval *arglist)
 {
@@ -1953,6 +1981,10 @@ static jsSyncMethodSpec js_console_functions[] = {
 	,JSDOCSTR("display a string centered on the screen")
 	,310
 	},
+	{"wide",			js_wide,			1, JSTYPE_VOID,		JSDOCSTR("text")
+	,JSDOCSTR("display a string double-wide on the screen (sending \"fullwidth\" Unicode characters when possible)")
+	,0x317c
+	},
 	{"strlen",			js_strlen,			1, JSTYPE_NUMBER,	JSDOCSTR("text")
 	,JSDOCSTR("returns the number of characters in text, excluding Ctrl-A codes")
 	,310
diff --git a/src/sbbs3/sbbs.h b/src/sbbs3/sbbs.h
index f2efc2a181be99b57d2e6ffe41a118f567c47a17..10a40661492ef75be59e8ae15f449fa4b97c2b48 100644
--- a/src/sbbs3/sbbs.h
+++ b/src/sbbs3/sbbs.h
@@ -718,6 +718,7 @@ public:
 	int		outchar(enum unicode_codepoint, char cp437_fallback = 0);
 	void	inc_column(int count);
 	void	center(char *str);
+	void	wide(const char*);
 	void	clearline(void);
 	void	cleartoeol(void);
 	void	cleartoeos(void);