diff --git a/pd/nw/dialog_gatom.html b/pd/nw/dialog_gatom.html
new file mode 100644
index 0000000000000000000000000000000000000000..2f1501985e214960fc948013b5ac46777f04c7d0
--- /dev/null
+++ b/pd/nw/dialog_gatom.html
@@ -0,0 +1,407 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <link rel="stylesheet" type="text/css" href="dialog_iemgui.css">
+  </head>
+  <body id="iemgui_dialog_body">
+    <div class="container">
+    <form> 
+      <fieldset> 
+        <legend data-i18n="gatom.prop.gatom"></legend> 
+
+        <table class="pairs">
+          <tr class="width prop hidden">
+            <td>
+              <label data-i18n="[title]iem.prop.width_tt"> 
+                <span data-i18n="iem.prop.width"></span>
+              </label>
+            </td>
+            <td data-i18n="[title]iem.prop.width_tt">
+              <input type="text" name="width">
+            </td>
+            <td>
+              <label data-i18n="[title]iem.prop.height_tt">
+                <span data-i18n="iem.prop.height"></span>
+              </label>
+            </td>
+            <td data-i18n="[title]iem.prop.height_tt">
+              <input type="text" name="height">
+            </td>
+          </tr>
+          <tr class="draglo prop pair hidden">
+            <td>
+              <label data-i18n="[title]iem.prop.minimum_tt">
+                <span data-i18n="iem.prop.minimum"></span>
+              </label>
+            </td>
+            <td data-i18n="[title]iem.prop.minimum_tt">
+              <input type="text" name="minimum-range">
+            </td>
+            <td>
+              <label data-i18n="[title]iem.prop.maximum_tt">
+                <span data-i18n="iem.prop.maximum"></span>
+              </label>
+            </td>
+            <td data-i18n="[title]iem.prop.maximum_tt">
+              <input type="text" name="maximum-range">
+            </td>
+          </tr>
+          <tr class="send-symbol prop hidden">
+            <td>
+              <label data-i18n="[title]iem.prop.send_tt">
+                <span data-i18n="iem.prop.send"></span>
+              </label>
+            </td>
+            <td data-i18n="[title]iem.prop.send_tt">
+              <input type="text" name="send-symbol">
+            </td>
+            <td>
+          <tr class="receive-symbol prop hidden">
+            <td>
+              <label data-i18n="[title]iem.prop.receive_tt">
+                <span data-i18n="iem.prop.receive"></span>
+              </label>
+            </td>
+            <td data-i18n="[title]iem.prop.receive_tt">
+              <input type="text" name="receive-symbol">
+            </td>
+            <td>
+          </tr>
+          <tr class="label prop hidden">
+            <td>
+              <label data-i18n="[title]iem.prop.label_tt">
+                <span data-i18n="iem.prop.label"></span>
+              </label>
+            </td>
+            <td data-i18n="[title]iem.prop.label_tt">
+              <input type="text" name="label">
+            </td>
+            <td>
+              <label data-i18n="[title]iem.prop.labelpos">
+                <span data-i18n="iem.prop.labelpos"></span>
+              </label>
+            </td>
+          </tr>
+        </table>
+      </fieldset> 
+
+    <div class="submit_buttons">
+      <button type="button" onClick="ok()" data-i18n="[title]iem.prop.ok_tt">
+        <span data-i18n="iem.prop.ok"></span>
+      </button>
+      <button type="button" onClick="apply()" data-i18n="[title]iem.prop.apply_tt">
+        <span data-i18n="iem.prop.apply"></span>
+      </button>
+      <button type="button" onClick="cancel()" data-i18n="[title]iem.prop.cancel_tt">
+        <span data-i18n="iem.prop.cancel"></span>
+      </button>
+    </div>
+
+  </form> 
+  </div>      
+
+  <script>
+    'use strict';
+    var nw = require('nw.gui'); 
+    var pdgui = require('./pdgui.js');
+
+    // For translations
+    var l = pdgui.get_local_string;
+
+    console.log("my working dire is " + pdgui.get_pwd());
+
+    var pd_object_callback;
+
+    function ok() {
+        apply();
+        cancel();
+    }
+
+//    function toggler(evt) {
+//        evt.value = evt.checked ? 1 : 0; 
+//    }
+
+    function substitute_space(arg) {
+        var fake_space = String.fromCharCode(11);
+        return arg.split(' ').join(fake_space);
+    }
+
+    function strip_problem_chars(arg) {
+        var problem_chars = [';', ',', '{', '}', '\\'];
+        var ret = arg;
+        for(var i = 0; i < problem_chars.length; i++) {
+            ret = ret.split(';').join('');
+        }
+        return ret;
+    }
+
+    function apply() {
+        pdgui.gui_post("we're applying shits!");
+
+        /* Not sure what these are...
+            iemgui_clip_dim $id
+            iemgui_clip_num $id
+            iemgui_sched_rng $id
+            iemgui_verify_rng $id
+            iemgui_sched_rng $id
+            iemgui_clip_fontsize $id
+        */
+
+
+
+        var send_symbol = document.getElementsByName('send-symbol')[0].value;
+        var receive_symbol = document.getElementsByName('receive-symbol')[0].value;
+        var label =  document.getElementsByName('label')[0].value;
+        if (send_symbol === null || send_symbol === '') { send_symbol = 'empty'; }
+        if (receive_symbol === null || receive_symbol === '') { receive_symbol = 'empty'; }
+        if (label === null || label === '') { label = 'empty'; }
+
+        console.log("send_symbol is " + send_symbol);
+
+        if (send_symbol.charAt(0) === '$') {
+            send_symbol = '#' + send_symbol.slice(1);
+        }
+        if (receive_symbol.charAt(0) === '$') {
+            receive_symbol = '#' + receive_symbol.slice(1);
+        }
+        if (label.charAt(0) === '$') {
+            label = '#' + label.slice(1);
+        }
+
+        send_symbol = substitute_space(send_symbol);
+        receive_symbol = substitute_space(receive_symbol);
+        label = substitute_space(label);
+
+        send_symbol = strip_problem_chars(send_symbol);
+        receive_symbol = strip_problem_chars(receive_symbol);
+        label = strip_problem_chars(label);
+
+        var label_x_offset =  document.getElementsByName('x-offset')[0].value;
+        var label_y_offset =  document.getElementsByName('y-offset')[0].value;
+
+	// make sure the offset boxes have a value
+        if (label_x_offset === null) { label_x_offset = 0; }
+        if (label_y_offset === null) { label_y_offset = 0; }
+
+        var height, width;
+        var size = document.getElementsByName('size')[0].value;
+        if (size === '') {
+            var size = document.getElementsByName('selection-size')[0].value;
+        }
+
+        if (size !== '') {
+            width = size;
+            height = size;
+        } else {
+            width = document.getElementsByName('width')[0].value;
+            height = document.getElementsByName('height')[0].value;
+        }
+
+        var slot3 = document.getElementsByName('minimum-range')[0].value;
+        var slot4 = document.getElementsByName('maximum-range')[0].value;
+
+        if (slot3 === '') {
+            slot3 = document.getElementsByName('flash-interrupt')[0].value;
+            slot4 = document.getElementsByName('flash-hold')[0].value;
+        }
+
+        if (slot3 === '') {
+            slot3 = document.getElementsByName('visible-width')[0].value;
+            slot4 = document.getElementsByName('visible-height')[0].value;
+        }
+
+        if (slot3 === '') { // toggle
+            slot3 = document.getElementsByName('nonzero-value')[0].value;
+            if (slot3 === '') {
+                slot3 = 0;
+            }
+            slot4 = 0;
+        }
+
+        var slot5 = +document.getElementsByName('log-scaling')[0].checked;
+        // Hack to accomodate the vu-scale property, which exists in the same
+        // slot as this one
+        var log_scaling_spanner = document.getElementsByClassName('log-scaling')[0];
+        var log_display = log_scaling_spanner.style.getPropertyValue('display');
+
+        if (log_display === null) {
+            slot5 = +document.getElementsByName('vu-scale')[0].checked;
+            pdgui.gui_post('slot five is ' + slot5);
+        }
+        pdgui.gui_post('slot five is ' + slot5);
+
+        var init = +document.getElementsByName('init')[0].checked;
+        if (init === '') { init = 0; }
+
+        var slot7 = document.getElementsByName('log-height')[0].value;
+        if (slot7 === '') {
+            slot7 = document.getElementsByName('number')[0].value;
+        }
+        if (slot7 === '') {
+            slot7 = 0;
+        }
+
+        var font_style = document.getElementsByName('font-style')[0].value;
+        if (font_style !== null) { font_style = 0; }
+
+        var font_size = document.getElementsByName('font-size')[0].value;
+        if (font_size === '') { font_size = 0; }
+
+        var foreground_color = parseInt(document.getElementsByName('foreground-color')[0].value.slice(1), 16);
+        var background_color = parseInt(document.getElementsByName('background-color')[0].value.slice(1), 16);
+        var label_color = parseInt(document.getElementsByName('label-color')[0].value.slice(1), 16);
+
+        var slot18 = +document.getElementsByName('steady-on-click')[0].checked;
+
+        pdgui.pdsend([pd_object_callback, 'dialog',
+            width, height,
+            slot3, // bng: flash-interrupt
+                   // slider: min-range
+                   // toggle: nonzero-value
+                   // my_canvas: visible_width
+            slot4, // bng: flash-hold
+                   // slider: max-range
+                   // my_canvas: visible_height
+            slot5, // slider: lin/log thingy
+                   // nbx: lin/log
+                   // vu: vu-scale
+            init,
+            slot7, // log-height or vradio/hradio number
+            send_symbol, receive_symbol, label,
+            label_x_offset, label_y_offset,
+            font_style, font_size,
+            background_color, foreground_color,
+            label_color,
+            slot18, // steady on click
+            0].join(' '));
+/*
+	pd [concat $id dialog \
+		$::dialog($vid:wdt) $::dialog($vid:hgt) \
+		$::dialog($vid:min_rng) $::dialog($vid:max_rng) \
+		$::dialog($vid:lin0_log1) $::dialog($vid:loadbang) \
+		$::dialog($vid:num) \
+		$hhhsnd $hhhrcv $hhhgui_nam \
+		$::dialog($vid:gn_dx) $::dialog($vid:gn_dy) \
+		$::dialog($vid:gn_f) $::dialog($vid:gn_fs) \
+		$::dialog($vid:bcol) $::dialog($vid:fcol) \
+		$::dialog($vid:lcol) \
+		$::dialog($vid:steady) $::dialog($vid:hide) \;]
+*/
+
+    }
+
+    function cancel() {
+        pdgui.gui_post("closing the window at this point");
+//        window.close(true);
+        pdgui.pdsend(pd_object_callback + " cancel");
+    }
+
+    // This gets called from the nw_create_window function in index.html
+    // It provides us with our window id from the C side.  Once we have it
+    // we can create the menu and register event callbacks
+    function register_canvas_id(gfxstub, attr_array) {
+        pd_object_callback = gfxstub;
+
+        console.log('attr array is ' + attr_array.toString());
+        for (var i = 0; i < attr_array.length; i+=2) {
+            console.log(attr_array[i] + ": " + attr_array[i+1]);
+        }
+        add_events(gfxstub);
+        // not sure that we need this for properties windows
+//        pdgui.canvas_map(gfxstub);
+        translate_form();
+        populate_form(attr_array);
+        // We don't turn on rendering of the "container" div until
+        // We've finished displaying all the spans and populating the
+        // labels and form elements.  That makes it more efficient and
+        // snappier, at least on older machines.
+        document.getElementsByClassName('container')[0].style.setProperty('display', 'inline');
+//        document.getElementsByClass("fumbles")[0].setAttribute('style', 'display: inline;');
+    }
+
+function tr_text(id) {
+    var elem = document.getElementById('iem.prop.' + id);
+    elem.textContent = l('iem.prop.' + id);
+}
+
+// Stop-gap translator
+function translate_form() {
+    var i
+    var elements = document.querySelectorAll('[data-i18n]');
+    for (i = 0; i < elements.length; i++) {
+        var data = elements[i].dataset.i18n;
+        if (data.slice(0,7) === '[title]') {
+            elements[i].title = l(data.slice(7));
+        } else {
+            elements[i].textContent = l(data);
+        }
+    }
+}
+
+function populate_form(attr_array) {
+
+    // First, let's put the translated text for the form labels:
+
+//    tr_text('heading.size');
+//    tr_text('heading.messages');
+//    tr_text('heading.label');
+//    tr_text('heading.colors');
+//    tr_prop('width');
+//    tr_tooltip('width');
+
+//    var headings = ["size", "messages", "label", "colors"];
+//    for (var i = 0; i < headings.length; i++) {
+//        var str = "iem.prop.heading." + headings[i];
+//        var heading = document.getElementById(str);
+//        heading.textContent = l(str);
+//    }
+
+    for(var i = 0; i < attr_array.length; i+=2) {
+        // Unhide the span with the class with the same name as the id
+        var prop_group = document.getElementsByClassName(attr_array[i])[0];
+        if (prop_group !== undefined) {
+            console.log("the thing here is " + attr_array[i]);
+            prop_group.classList.remove('hidden');
+        } else {
+            pdgui.gui_post("Error: couldn't find iemgui prop group for " + attr_array[i]);
+        }
+        // iemguis use the string 'empty' for null because of
+        // the limitations of Pd's state-saving API.  So we have
+        // to filter that one out
+        if(attr_array[i+1] !== 'empty') {
+            var elem = document.getElementsByName(attr_array[i]);
+            if (elem.length > 0) {
+                if(attr_array[i].slice(-5) === 'color') {
+                    var hex_string = Number(attr_array[i+1]).toString(16);
+                    var color_string = "#" + (hex_string === '0' ? '000000' : hex_string);
+                    pdgui.gui_post("color is " + color_string);
+                    elem[0].value = color_string;
+                } else if (elem[0].type === 'checkbox') {
+                    // The attr here is a string, so we need to
+                    // force it to number, hence the "+" below
+                    elem[0].checked = +attr_array[i+1];
+                } else {
+                    elem[0].value = attr_array[i+1];
+                }
+            }
+        }
+    }
+}
+
+function add_events(name) {
+    // let's handle some events for this window...
+
+    // closing the Window
+    nw.Window.get().on("close", function() {
+        // this needs to do whatever the "cancel" button does
+//        pdgui.pdsend(name + " menuclose 0");
+//        cancel();
+        pdgui.remove_dialogwin(pd_object_callback);
+        this.close(true);
+    });
+
+}
+
+  </script>
+  </body>
+</html>
diff --git a/pd/nw/locales/en/translation.json b/pd/nw/locales/en/translation.json
index 0818f4087a002341187ffda334312ee6d2fe4a2e..744a0ff2be89449ffda05f76bacfce443eb5ecad 100644
--- a/pd/nw/locales/en/translation.json
+++ b/pd/nw/locales/en/translation.json
@@ -70,6 +70,11 @@
       "cancel_tt":    "Close the dialog window"
     }
   },
+  "gatom": {
+    "prop": {
+      "gatom": "atom box"
+    }
+  },
   "menu": {
     "file": "File",
     "new": "New",
diff --git a/pd/nw/pdgui.js b/pd/nw/pdgui.js
index 2d2c14f8b80b7734818c7184b157541485d269f4..c14d57b918c3fc0cca05b2ae5f3e67085344c959 100644
--- a/pd/nw/pdgui.js
+++ b/pd/nw/pdgui.js
@@ -1873,6 +1873,13 @@ function gui_text_create_gobj(cid, tag, type, xpos, ypos, is_toplevel) {
             class: type + (is_toplevel !== 0 ? '' : ' gop')
     });
     svg.appendChild(g);
+    var bluh = svg.getBBox();
+    var bbox_rect = svg.getElementById('bbox_rect');
+    bbox_rect.setAttributeNS(null, 'width', bluh.width);
+    bbox_rect.setAttributeNS(null, 'height', bluh.height);
+    bbox_rect.setAttributeNS(null, 'fill', 'none');
+    bbox_rect.setAttributeNS(null, 'stroke', 'black');
+    
 // hm... why returning g and not the return value of appendChild?
 //    console.log("create gobj tag is " + tag + " and ret is " + g);
     return g;
@@ -3214,6 +3221,12 @@ exports.file_dialog_callback = function(file_string) {
     pdsend(file_dialog_target + " callback " + enquote(file_string));
 }
 
+function gui_gatom_dialog(did, attr_array) {
+    gui_post("fuck tits");
+    dialogwin[did] = nw_create_window(did, 'gatom', 265, 540, 20, 20, 0,
+        0, 1, 'white', 'Properties', '', 0, null, attr_array);
+}
+
 function gui_iemgui_dialog(did, attr_array) {
     gui_post("got a gfxstub " + did + "!!!");
    
diff --git a/pd/src/g_text.c b/pd/src/g_text.c
index a7982b5049b70dde51d92b740a13167a8b79cfe8..2379e2d51388a36476a82d2b83e4bef512922cd4 100644
--- a/pd/src/g_text.c
+++ b/pd/src/g_text.c
@@ -1334,16 +1334,27 @@ static void gatom_free(t_gatom *x)
 static void gatom_properties(t_gobj *z, t_glist *owner)
 {
     t_gatom *x = (t_gatom *)z;
-    char buf[200];
-    sprintf(buf, "pdtk_gatom_dialog %%s %d %g %g %d {%s} {%s} {%s}\n",
-        x->a_text.te_width, x->a_draglo, x->a_draghi,
-            x->a_wherelabel, gatom_escapit(x->a_label)->s_name,
-                gatom_escapit(x->a_symfrom)->s_name,
-                    gatom_escapit(x->a_symto)->s_name);
-    gfxstub_new(&x->a_text.te_pd, x, buf);
+    //char buf[200];
+    //sprintf(buf, "pdtk_gatom_dialog %%s %d %g %g %d {%s} {%s} {%s}\n",
+    //    x->a_text.te_width, x->a_draglo, x->a_draghi,
+    //        x->a_wherelabel, gatom_escapit(x->a_label)->s_name,
+    //            gatom_escapit(x->a_symfrom)->s_name,
+    //                gatom_escapit(x->a_symto)->s_name);
+    //gfxstub_new(&x->a_text.te_pd, x, buf);
+    gui_start_vmess("gui_gatom_dialog", "s",
+        gfxstub_new2(&x->a_text.te_pd, x));
+    gui_start_array();
+    gui_s("width");    gui_i(x->a_text.te_width);
+    gui_s("draglo");   gui_f(x->a_draglo);
+    gui_s("draghi");   gui_f(x->a_draghi);
+    gui_s("labelpos"); gui_i(x->a_wherelabel);
+    gui_s("label");    gui_s(gatom_escapit(x->a_label)->s_name);
+    gui_s("receive");  gui_s(gatom_escapit(x->a_symfrom)->s_name);
+    gui_s("send");     gui_s(gatom_escapit(x->a_symto)->s_name);
+    gui_end_array();
+    gui_end_vmess();
 }
 
-
 /* -------------------- widget behavior for text objects ------------ */
 
 /* variant of the glist_findrtext found in g_rtext.c