diff --git a/pd/nw/DejaVuSansMono.ttf b/pd/nw/DejaVuSansMono.ttf
new file mode 100644
index 0000000000000000000000000000000000000000..8b7bb2a4e1b2c27786398c320e9020bcc24c3af3
Binary files /dev/null and b/pd/nw/DejaVuSansMono.ttf differ
diff --git a/pd/nw/index.html b/pd/nw/index.html
new file mode 100644
index 0000000000000000000000000000000000000000..b8f71616f4addcecd22240fb5702e1af4bf852fa
--- /dev/null
+++ b/pd/nw/index.html
@@ -0,0 +1,489 @@
+<!DOCTYPE html>
+<html>
+  <head>
+  </head>
+  <body>
+    <input style="display:none;" id="fileDialog" type="file" nwworkingdir multiple />
+    <script>
+        'use strict';
+        var nw = require('nw.gui');
+        console.log(nw.App.argv);
+        var pdgui = require('./pdgui.js');
+        var port_no = nw.App.argv[0]; // fed to us by the Pd process
+        var pwd = process.env.PWD;
+        pdgui.set_port(port_no);
+        pdgui.set_pwd(pwd);
+        pdgui.set_pd_window(this);
+        pdgui.set_app_quitfn(app_quit);
+
+        console.log("Hello from " + pwd);
+
+        function app_quit () {
+            console.log("quitting for reals");
+            nw.App.quit();
+        }
+
+        var chooser = document.querySelector("#fileDialog");
+        chooser.addEventListener("change", function(evt) {
+            var file_array = this.value;
+            // reset value so that we can open the same file twice
+            this.value = null;
+            pdgui.menu_open(file_array);
+            console.log("tried to open something");
+        }, false);
+
+
+
+//        prompt("hey", "you");
+
+        // Invoke some functions to create main menus, etc.
+
+        nw.Window.get().on("close", function() {
+            pdgui.menu_quit();
+// stop-gap to shutdown properly
+//            this.close(true);
+        });
+        console.log(nw.App.argv); 
+
+        document.getElementById("fileDialog").setAttribute("nwworkingdir", pwd);
+        document.getElementById("fileDialog").setAttribute("accept",
+            Object.keys(pdgui.pd_filetypes).toString());
+
+        nw_create_pd_window_menus();
+
+        pdgui.connect();
+        pdgui.init_socket_events();
+
+        // nw context callbacks (mostly just creating/destroying windows)
+        pdgui.set_new_window_fn(nw_create_window);
+        pdgui.set_close_window_fn(nw_close_window);
+
+
+        function pdmenu_copy () {
+            alert("Please implement pdmenu_copy"); 
+        }
+
+        function pdmenu_selectall () {
+            alert("Please implement pdmenu_selectall"); 
+        }
+
+        function pdmenu_preferences () {
+            alert("Please implement pdmenu_preferences"); 
+        }
+
+        function pdmenu_next_win () {
+            alert("Please implement pdmenu_preferences"); 
+        }
+
+        function pdmenu_previous_win () {
+            alert("Please implement pdmenu_preferences"); 
+        }
+
+        function pdmenu_parent_win () {
+            alert("Please implement pdmenu_preferences"); 
+        }
+
+        function pdmenu_console_win () {
+            alert("Please implement pdmenu_preferences"); 
+        }
+
+        function pdmenu_audio_on () {
+            alert("Please implement pdmenu_preferences"); 
+        }
+
+        function pdmenu_audio_off () {
+            alert("Please implement pdmenu_preferences"); 
+        }
+
+        function pdmenu_test_audio () {
+            alert("Please implement pdmenu_preferences"); 
+        }
+
+        function pdmenu_load_meter () {
+            alert("Please implement pdmenu_preferences"); 
+        }
+
+        function pdmenu_about_pd () {
+            alert("Please implement pdmenu_preferences"); 
+        }
+
+        function pdmenu_manual () {
+            alert("Please implement pdmenu_preferences"); 
+        }
+
+        function pdmenu_help_browser () {
+            alert("Please implement pdmenu_preferences"); 
+        }
+
+        function pdmenu_l2ork_mailinglist () {
+            alert("Please implement pdmenu_preferences"); 
+        }
+
+        function pdmenu_pd_mailinglists () {
+            alert("Please implement pdmenu_preferences"); 
+        }
+
+        function pdmenu_forums () {
+            alert("Please implement pdmenu_preferences"); 
+        }
+
+        function pdmenu_irc () {
+            alert("Please implement pdmenu_preferences"); 
+        }
+
+// Menus for the main Pd window
+function nw_create_pd_window_menus () {
+    // Window menu
+    var windowMenu = new nw.Menu({
+        type: 'menubar'
+    });
+
+    // File menu
+    var fileMenu = new nw.Menu();
+
+    // Add to window menu
+    windowMenu.append(new nw.MenuItem({
+        label: 'File',
+        submenu: fileMenu
+    }));
+
+    // File sub-entries
+    fileMenu.append(new nw.MenuItem({
+        label: 'New',
+        click: pdgui.menu_new,
+        key: 'n',
+        modifiers: "ctrl"
+    }));
+
+    fileMenu.append(new nw.MenuItem({
+        label: 'Open',
+        key: 'o',
+        modifiers: "ctrl",
+        click: function (){
+            var chooser = document.querySelector('#fileDialog');
+            chooser.click();
+        }
+    }));
+
+    if (pdgui.k12_mode == 1) {
+        fileMenu.append(new nw.MenuItem({
+        label: 'K12 Demos',
+        click: pdgui.menu_k12_open_demos
+        }));
+    }
+
+    fileMenu.append(new nw.MenuItem({
+        type: 'separator'
+    }));
+
+    // Note: this must be different for the main Pd window
+    fileMenu.append(new nw.MenuItem({
+        label: 'Save',
+            click: function () {},
+            enabled: false,
+        key: 's',
+        modifiers: "ctrl"
+    }));
+
+    fileMenu.append(new nw.MenuItem({
+        label: 'Save as...',
+        click: function (){},
+        enabled: false,
+        key: 'S',
+        modifiers: "ctrl"
+    }));
+
+    if (pdgui.k12_mode == 0) {
+        fileMenu.append(new nw.MenuItem({
+            type: 'separator'
+        }));
+    }
+
+    fileMenu.append(new nw.MenuItem({
+        label: 'Message...',
+        click: pdgui.menu_send,
+        key: 'm',
+        modifiers: "ctrl"
+    }));
+
+    if (pdgui.k12_mode == 0) {
+        fileMenu.append(new nw.MenuItem({
+            type: 'separator'
+        }));
+    }
+
+    // recent files go here
+
+    // anther separator goes here if there are any recent files
+
+    // Note: there's no good reason to have this here
+    fileMenu.append(new nw.MenuItem({
+        label: 'Close',
+        click: function () {},
+        enabled: false,
+    }));
+
+    // Quit Pd
+    fileMenu.append(new nw.MenuItem({
+        label: 'Quit',
+        click: pdgui.menu_quit,
+        key: 'q',
+        modifiers: "ctrl"
+    }));
+
+
+    // Edit menu
+    var editMenu = new nw.Menu();
+
+    // Add to window menu
+    windowMenu.append(new nw.MenuItem({
+    label: 'Edit',
+    submenu: editMenu
+    }));
+
+    // Edit sub-entries
+    editMenu.append(new nw.MenuItem({
+        label: 'Copy',
+        click: pdmenu_copy,
+        key: 'c',
+        modifiers: "ctrl"
+    }));
+
+    editMenu.append(new nw.MenuItem({
+        label: 'Select All',
+        click: function () {
+            document.selectAllChildren(document);
+        },
+        key: 'a',
+        modifiers: "ctrl"
+    }));
+
+    editMenu.append(new nw.MenuItem({
+        type: 'separator'
+    }));
+
+    editMenu.append(new nw.MenuItem({
+        label: 'Zoom In',
+        click: function () {
+            nw.Window.get().zoomLevel += 1;
+            pdgui.gui_post("zoom level is " + nw.Window.get().zoomLevel);
+        },
+        key: '=',
+        modifiers: "ctrl"
+    }));
+
+    editMenu.append(new nw.MenuItem({
+        label: 'Zoom Out',
+        click: function () {
+            nw.Window.get().zoomLevel -= 1;
+            pdgui.gui_post("zoom level is " + nw.Window.get().zoomLevel);
+        },
+        key: '-',
+        modifiers: "ctrl"
+    }));
+
+    editMenu.append(new nw.MenuItem({
+        label: 'Preferences...',
+        click: pdmenu_preferences,
+        key: 'a',
+        modifiers: "ctrl"
+    }));
+
+
+    // Windows menu... call it "winman" (i.e., window management) to avoid confusion
+    var winmanMenu = new nw.Menu();
+
+    // Add to windows menu
+    windowMenu.append(new nw.MenuItem({
+    label: 'Windows',
+    submenu: winmanMenu
+    }));
+
+    // Winman sub-entries
+    winmanMenu.append(new nw.MenuItem({
+        label: 'Next Window',
+        click: pdmenu_next_win,
+        key: 'c',
+        modifiers: "ctrl"
+    }));
+
+    winmanMenu.append(new nw.MenuItem({
+        label: 'Previous Window',
+        click: pdmenu_previous_win,
+        key: 'a',
+        modifiers: "ctrl"
+    }));
+
+    winmanMenu.append(new nw.MenuItem({
+        type: 'separator'
+    }));
+
+    winmanMenu.append(new nw.MenuItem({
+        label: 'Parent Window',
+        click: pdmenu_parent_win,
+        key: 'a',
+        modifiers: "ctrl"
+    }));
+
+    winmanMenu.append(new nw.MenuItem({
+        label: 'Pd & Console',
+        click: pdmenu_console_win,
+        key: 'a',
+        modifiers: "ctrl"
+    }));
+
+    // Media menu
+    var mediaMenu = new nw.Menu();
+
+    // Add to window menu
+    windowMenu.append(new nw.MenuItem({
+    label: 'Media',
+    submenu: mediaMenu
+    }));
+
+    // Media sub-entries
+    mediaMenu.append(new nw.MenuItem({
+        label: 'Audio On',
+        click: function() {
+            pdgui.pdsend("pd dsp 1");
+        },
+        key: 'c',
+        modifiers: "ctrl"
+    }));
+
+    mediaMenu.append(new nw.MenuItem({
+        label: 'Audio Off',
+        click: function() {
+            pdgui.pdsend("pd dsp 0");
+        },
+        key: 'a',
+        modifiers: "ctrl"
+    }));
+
+    mediaMenu.append(new nw.MenuItem({
+        type: 'separator'
+    }));
+
+    mediaMenu.append(new nw.MenuItem({
+        label: 'Test Audio and Midi',
+        click: pdmenu_test_audio,
+        key: 'a',
+        modifiers: "ctrl"
+    }));
+
+    mediaMenu.append(new nw.MenuItem({
+        label: 'Load Meter',
+        click: pdmenu_load_meter,
+        key: 'a',
+        modifiers: "ctrl"
+    }));
+
+    // Help menu
+    var helpMenu = new nw.Menu();
+
+    // Add to window menu
+    windowMenu.append(new nw.MenuItem({
+    label: 'Help',
+    submenu: helpMenu
+    }));
+
+    // Help sub-entries
+    helpMenu.append(new nw.MenuItem({
+        label: 'About Pd-L2ork',
+        click: pdmenu_about_pd,
+        key: 'c',
+        modifiers: "ctrl"
+    }));
+
+    helpMenu.append(new nw.MenuItem({
+        label: 'Manual',
+        click: pdmenu_manual,
+        key: 'a',
+        modifiers: "ctrl"
+    }));
+
+    helpMenu.append(new nw.MenuItem({
+        label: 'Help Browser',
+        click: pdmenu_help_browser,
+        key: 'a',
+        modifiers: "ctrl"
+    }));
+
+    helpMenu.append(new nw.MenuItem({
+        type: 'separator'
+    }));
+
+    helpMenu.append(new nw.MenuItem({
+        label: 'Pd-L2ork Mailing List',
+        click: pdmenu_l2ork_mailinglist,
+        key: 'a',
+        modifiers: "ctrl"
+    }));
+
+    helpMenu.append(new nw.MenuItem({
+        label: 'Pure Data Mailing Lists',
+        click: pdmenu_pd_mailinglists,
+        key: 'a',
+        modifiers: "ctrl"
+    }));
+
+    helpMenu.append(new nw.MenuItem({
+        label: 'Forums',
+        click: pdmenu_forums,
+        key: 'a',
+        modifiers: "ctrl"
+    }));
+
+    helpMenu.append(new nw.MenuItem({
+        label: 'IRC Chat',
+        click: pdmenu_irc,
+        key: 'a',
+        modifiers: "ctrl"
+    }));
+
+    // Assign to window
+    nw.Window.get().menu = windowMenu;
+}
+
+function nw_close_window(window) {
+    window.close(true);
+}
+
+function nw_create_window(cid, width, height, xpos, ypos, menu_flag, resize, topmost, cnv_color,
+    canvas_string, dir, dirty_flag, cargs) {
+        // todo: make a separate way to format the title for OSX
+    var my_title =  pdgui.format_window_title(canvas_string, dirty_flag, cargs, dir);
+
+    var new_win = nw.Window.open('pdcanvas.html', {
+        title: my_title,
+        position: "center",
+        toolbar: true,
+        focus: true,
+        width: width,
+        height: height,
+        x: xpos,
+        y: ypos
+    });
+    return new_win;
+}
+
+function canvasNew(args) {
+    console.log(args);
+}
+
+
+
+
+
+    </script>
+    <input style="display:none;" id="saveDialog" type="file" nwsaveas />
+
+    <pre id="p1" style="white-space: pre-wrap;">Welcome to Pd GUI using Node-Webkit
+      <script>document.write(process.versions['node-webkit'])</script><br/></pre> 
+
+    <script>
+
+
+    </script>
+  </body>
+</html>
diff --git a/pd/nw/package.json b/pd/nw/package.json
new file mode 100644
index 0000000000000000000000000000000000000000..de61b251c46c28907a3f4646afa2f2511de9b79e
--- /dev/null
+++ b/pd/nw/package.json
@@ -0,0 +1,8 @@
+{
+  "name": "my-app",
+  "version": "0.1.0",
+  "main": "index.html",
+  "window": {
+      "toolbar": true
+  }
+}
diff --git a/pd/nw/pdcanvas.css b/pd/nw/pdcanvas.css
new file mode 100644
index 0000000000000000000000000000000000000000..88baa317e9d4a9598f82761739c0cbe90abfa5d3
--- /dev/null
+++ b/pd/nw/pdcanvas.css
@@ -0,0 +1,50 @@
+@font-face {
+    font-family: "DejaVu Sans Mono";
+    src: url("DejaVuSansMono.ttf");
+}
+
+body {
+    margin: 0px;
+    font-family: "DejaVu Sans Mono";
+}
+
+.noselect {
+    -webkit-touch-callout: none;
+    -webkit-user-select: none;
+    -khtml-user-select: none;
+    -moz-user-select: none;
+    -ms-user-select: none;
+    user-select: none;
+}
+
+text {
+    // fill: red;
+    cursor: default;
+}
+
+.selected_border {
+    stroke: blue;
+    stroke-dasharray: none;
+    stroke-width: 1;
+}
+
+.selected_line {
+    stroke: blue;
+}
+
+.broken_border {
+    fill: #f7f7f7;
+    stroke: red;
+    stroke-dasharray: 3 2;
+}
+
+.xlet {
+    stroke: black;
+    fill: gray;
+    stroke-width: 1;
+    shape-rendering: optimizeSpeed;
+}
+
+//.xlet:hover {
+//    stroke: red;
+//}
diff --git a/pd/nw/pdcanvas.html b/pd/nw/pdcanvas.html
new file mode 100644
index 0000000000000000000000000000000000000000..9bd9af314103021e2233b364dd70403d8478956f
--- /dev/null
+++ b/pd/nw/pdcanvas.html
@@ -0,0 +1,1009 @@
+<!DOCTYPE html>
+<html>
+  <head>
+  </head>
+  <link rel="stylesheet" type="text/css" href="pdcanvas.css">
+  <body>
+  <input style="display:none;" id="fileDialog" type="file" multiple />
+  <input style="display:none;" id="saveDialog" type="file" nwsaveas nwworkingdir accept=".pd" />
+  <svg xmlns="http://www.w3.org/2000/svg" version="1.1" id="patchsvg" height="1000" width="1000" class="noselect">
+  <rect/>
+  </svg>
+  <script>
+    'use strict';
+    var nw = require('nw.gui'); 
+    var pdgui = require('./pdgui.js');
+
+    var name = pdgui.last_loaded();
+    
+    console.log("my working dire is " + pdgui.get_pwd());
+    document.getElementById("saveDialog").setAttribute("nwworkingdir", pdgui.get_pwd());
+
+    document.getElementById("fileDialog").setAttribute("nwworkingdir", pdgui.get_pwd());
+    document.getElementById("fileDialog").setAttribute("accept",
+        Object.keys(pdgui.pd_filetypes).toString());
+
+
+
+    // The right-click popup menu
+    var popup_menu = new nw.Menu();
+    pdgui.add_popup(name, popup_menu);
+
+    popup_menu.append(new nw.MenuItem({
+        label: 'Properties',
+        click: function() {
+            pdgui.popup_action(name, 0);
+        }
+    }));
+    popup_menu.append(new nw.MenuItem({
+        label: 'Open',
+        click: function() {
+            pdgui.popup_action(name, 1);
+        }
+    }));
+    popup_menu.append(new nw.MenuItem({
+        label: 'Help',
+        click: function() {
+            pdgui.popup_action(name, 2);
+        }
+    }));
+
+    document.querySelector("#saveDialog").addEventListener("change", function(evt) {
+        pdgui.saveas_callback(name, this.value);
+        console.log("tried to open something");
+    }, false);
+
+
+    document.querySelector("#fileDialog").addEventListener("change", function(evt) {
+        var file_array = this.value;
+        // reset value so that we can open the same file twice
+        this.value = null;
+        pdgui.menu_open(file_array);
+        console.log("tried to open something");
+    }, false);
+
+
+    document.addEventListener("mousemove", function(evt) {
+        //pdgui.gui_post("x: " + evt.pageX + " y: " + evt.pageY +
+        //    " modifier: " + (evt.shiftKey + (evt.ctrlKey << 1)));
+        pdgui.pdsend(name + " motion " + evt.pageX + " " + evt.pageY + " " +
+            (evt.shiftKey + (evt.ctrlKey << 1)));
+        evt.stopPropagation();
+//        evt.preventDefault();
+        return false;
+    });
+
+    var last_keydown = "";
+
+
+    function add_keymods(key, evt) {
+        var shift = evt.shiftKey ? "Shift" : "";
+        var ctrl = evt.ctrlKey ? "Ctrl" : "";
+        return shift + ctrl + key;
+    }
+
+    document.addEventListener("keydown", function(evt) {
+        var key_code = evt.keyCode; 
+        var hack = null; // hack for unprintable ascii codes
+        switch(key_code) {
+            case 8:
+            case 9:
+            case 10:
+            case 27:
+//            case 32:
+            case 127: hack = key_code; break;
+
+
+            case 37: hack = add_keymods('Left', evt); break;
+            case 38: hack = add_keymods('Up', evt); break;
+            case 39: hack = add_keymods('Right', evt); break;
+            case 40: hack = add_keymods('Down', evt); break;
+            case 33: hack = add_keymods('Prior', evt); break;
+            case 34: hack = add_keymods('Next', evt); break;
+            case 35: hack = add_keymods('End', evt); break;
+            case 36: hack = add_keymods('Home', evt); break;
+
+            // Need to handle Control key, Alt
+
+            case 16: hack = 'Shift'; break;
+            case 17: hack = 'Control'; break;
+            case 18: hack = 'Alt'; break;
+        }
+        if (hack !== null) {
+            pdgui.gui_canvas_sendkey(name, 1, evt, hack);
+            pdgui.set_keymap(key_code, hack);
+        }
+        pdgui.gui_post("keydown time: keycode is " + evt.keyCode);
+        last_keydown = evt.keyCode;
+        evt.stopPropagation();
+    });
+
+    document.addEventListener("keypress", function(evt) {
+        pdgui.gui_canvas_sendkey(name, 1, evt, evt.charCode);
+        pdgui.set_keymap(last_keydown, evt.charCode);
+        pdgui.gui_post("keypress time: charcode is " + evt.charCode);
+        // Don't do things like scrolling on space, arrow keys, etc.
+        evt.stopPropagation();
+        evt.preventDefault();
+    });
+
+    document.addEventListener("keyup", function(evt) {
+        var my_char_code = pdgui.get_char_code(evt.keyCode);
+        pdgui.gui_canvas_sendkey(name, 0, evt, my_char_code);
+        pdgui.gui_post("keyup time: charcode is: " + my_char_code);
+    });
+
+    // just left-clicks for the moment
+    document.onmousedown = function(evt) {
+        //pdgui.gui_post("mousedown: x: " + evt.pageX + " y: " + evt.pageY +
+        //    " button: " + (evt.button + 1) + " modifier: " + (evt.shiftKey + (evt.ctrlKey << 1)));
+        // tk events are one greater than html5...
+        var b = evt.button + 1;
+        var mod;
+        // For some reason right-click sends a modifier value of "8", and canvas_doclick in
+        // g_editor.c depends on that value to do the right thing.  So let's hack...
+        if (b === 3) { // right-click
+            mod = 8;
+        } else {
+            mod = (evt.shiftKey + (evt.ctrlKey << 1)); 
+        }
+        pdgui.pdsend(name + " mouse " + evt.pageX + " " + evt.pageY + " " +
+            b + " " + mod);
+        evt.stopPropagation();
+        evt.preventDefault();
+    }
+
+    document.onmouseup = function(evt) {
+        //pdgui.gui_post("mouseup: x: " + evt.pageX + " y: " + evt.pageY +
+        //    " button: " + (evt.button + 1));
+        pdgui.pdsend(name + " mouseup " + evt.pageX + " " + evt.pageY + " " +
+            (evt.button + 1));
+    }
+
+
+
+
+    // let's handle some events for this window...
+
+    // closing the Window
+    // this isn't actually closing the window yet
+    nw.Window.get().on("close", function() {
+        pdgui.pdsend(name + " menuclose 0");
+    });
+    // draw the objects in the patch once we've finished loading this boilerplate page
+    // We could probably use the window.onload here, which would make it standard javascript
+    this.onload = function() {
+        pdgui.canvas_map(name);
+    };
+
+
+
+
+
+nw_create_patch_window_menus(name);
+
+        function menu_generic () {
+            alert("Please implement this");
+        }
+
+        function pdmenu_copy () {
+            alert("Please implement pdmenu_copy"); 
+        }
+
+        function pdmenu_selectall () {
+            alert("Please implement pdmenu_selectall"); 
+        }
+
+        function pdmenu_preferences () {
+            alert("Please implement pdmenu_preferences"); 
+        }
+
+        function pdmenu_next_win () {
+            alert("Please implement pdmenu_preferences"); 
+        }
+
+        function pdmenu_previous_win () {
+            alert("Please implement pdmenu_preferences"); 
+        }
+
+        function pdmenu_parent_win () {
+            alert("Please implement pdmenu_preferences"); 
+        }
+
+        function pdmenu_console_win () {
+            alert("Please implement pdmenu_preferences"); 
+        }
+
+        function pdmenu_audio_on () {
+            alert("Please implement pdmenu_preferences"); 
+        }
+
+        function pdmenu_audio_off () {
+            alert("Please implement pdmenu_preferences"); 
+        }
+
+        function pdmenu_test_audio () {
+            alert("Please implement pdmenu_preferences"); 
+        }
+
+        function pdmenu_load_meter () {
+            alert("Please implement pdmenu_preferences"); 
+        }
+
+        function pdmenu_about_pd () {
+            alert("Please implement pdmenu_preferences"); 
+        }
+
+        function pdmenu_manual () {
+            alert("Please implement pdmenu_preferences"); 
+        }
+
+        function pdmenu_help_browser () {
+            alert("Please implement pdmenu_preferences"); 
+        }
+
+        function pdmenu_l2ork_mailinglist () {
+            alert("Please implement pdmenu_preferences"); 
+        }
+
+        function pdmenu_pd_mailinglists () {
+            alert("Please implement pdmenu_preferences"); 
+        }
+
+        function pdmenu_forums () {
+            alert("Please implement pdmenu_preferences"); 
+        }
+
+        function pdmenu_irc () {
+            alert("Please implement pdmenu_preferences"); 
+        }
+
+// Menus for the Patch window
+function nw_create_patch_window_menus () {
+
+    // Window menu
+    var windowMenu = new nw.Menu({
+        type: 'menubar'
+    });
+
+    // File menu
+    var fileMenu = new nw.Menu();
+
+    // Add to window menu
+    windowMenu.append(new nw.MenuItem({
+        label: 'File',
+        submenu: fileMenu
+    }));
+
+    // File sub-entries
+    fileMenu.append(new nw.MenuItem({
+        label: 'New',
+        click: pdgui.menu_new,
+        key: 'n',
+        modifiers: "ctrl"
+    }));
+
+    fileMenu.append(new nw.MenuItem({
+        label: 'Open',
+        key: 'o',
+        modifiers: "ctrl",
+        click: function (){
+            var chooser = document.querySelector('#fileDialog');
+            chooser.click();
+        }
+    }));
+
+    if (pdgui.k12_mode == 1) {
+        fileMenu.append(new nw.MenuItem({
+        label: 'K12 Demos',
+        click: pdgui.menu_k12_open_demos
+        }));
+    }
+
+    fileMenu.append(new nw.MenuItem({
+        type: 'separator'
+    }));
+
+    // Note: this must be different for the main Pd window
+    fileMenu.append(new nw.MenuItem({
+        label: 'Save',
+        click: function () {
+            pdgui.menu_save(name);
+        },
+        key: 's',
+        modifiers: "ctrl"
+    }));
+
+    fileMenu.append(new nw.MenuItem({
+        label: 'Save as...',
+        click: function (){
+            pdgui.menu_saveas(name);
+        },
+        key: 's',
+        modifiers: "ctrl+shift"
+    }));
+
+    if (pdgui.k12_mode == 0) {
+        fileMenu.append(new nw.MenuItem({
+            type: 'separator'
+        }));
+    }
+
+    fileMenu.append(new nw.MenuItem({
+        label: 'Message...',
+        click: pdgui.menu_send,
+        key: 'm',
+        modifiers: "ctrl"
+    }));
+
+    if (pdgui.k12_mode == 0) {
+        fileMenu.append(new nw.MenuItem({
+            type: 'separator'
+        }));
+    }
+
+    // recent files go here
+
+    // anther separator goes here if there are any recent files
+
+    fileMenu.append(new nw.MenuItem({
+        label: 'Close',
+        click: function() {
+            pdgui.menu_close(name);
+        }
+    }));
+
+    // Quit Pd
+    fileMenu.append(new nw.MenuItem({
+        label: 'Quit',
+        click: pdgui.menu_quit,
+        key: 'q',
+        modifiers: "ctrl"
+    }));
+
+
+    // Edit menu
+    var editMenu = new nw.Menu();
+
+    // Add to window menu
+    windowMenu.append(new nw.MenuItem({
+    label: 'Edit',
+    submenu: editMenu
+    }));
+
+    // Edit sub-entries
+    editMenu.append(new nw.MenuItem({
+        label: 'Undo',
+        click: menu_generic,
+        key: 'c',
+        modifiers: "ctrl"
+    }));
+
+    editMenu.append(new nw.MenuItem({
+        label: 'Redo',
+        click: menu_generic,
+        key: 'a',
+        modifiers: "ctrl"
+    }));
+
+    editMenu.append(new nw.MenuItem({
+        type: 'separator'
+    }));
+
+    editMenu.append(new nw.MenuItem({
+        label: 'Cut',
+        click: menu_generic,
+        key: 'c',
+        modifiers: "ctrl"
+    }));
+
+    editMenu.append(new nw.MenuItem({
+        label: 'Copy',
+        click: menu_generic,
+        key: 'c',
+        modifiers: "ctrl"
+    }));
+
+    editMenu.append(new nw.MenuItem({
+        label: 'Paste',
+        click: menu_generic,
+        key: 'c',
+        modifiers: "ctrl"
+    }));
+
+
+    editMenu.append(new nw.MenuItem({
+        label: 'Duplicate',
+        click: menu_generic,
+        key: 'c',
+        modifiers: "ctrl"
+    }));
+
+    editMenu.append(new nw.MenuItem({
+        label: 'Select All',
+        click: menu_generic,
+        key: 'a',
+        modifiers: "ctrl"
+    }));
+
+    editMenu.append(new nw.MenuItem({
+        label: 'Reselect',
+        click: menu_generic,
+        key: 'a',
+        modifiers: "ctrl"
+    }));
+
+    editMenu.append(new nw.MenuItem({
+        type: 'separator'
+    }));
+
+    editMenu.append(new nw.MenuItem({
+        label: 'Zoom In',
+        click: function () {
+            nw.Window.get().zoomLevel += 1;
+            pdgui.gui_post("zoom level is " + nw.Window.get().zoomLevel);
+        },
+        key: '=',
+        modifiers: "ctrl"
+    }));
+
+    editMenu.append(new nw.MenuItem({
+        label: 'Zoom Out',
+        click: function () {
+            nw.Window.get().zoomLevel -= 1;
+            pdgui.gui_post("zoom level is " + nw.Window.get().zoomLevel);
+        },
+        key: '-',
+        modifiers: "ctrl"
+    }));
+
+
+
+
+    editMenu.append(new nw.MenuItem({
+        type: 'separator'
+    }));
+
+    editMenu.append(new nw.MenuItem({
+        label: 'Tidy Up',
+        click: menu_generic,
+        key: 'a',
+        modifiers: "ctrl"
+    }));
+
+    editMenu.append(new nw.MenuItem({
+        label: 'Bring to Front',
+        click: menu_generic,
+        key: 'a',
+        modifiers: "ctrl"
+    }));
+
+    editMenu.append(new nw.MenuItem({
+        label: 'Send to Back',
+        click: menu_generic,
+        key: 'a',
+        modifiers: "ctrl"
+    }));
+
+    editMenu.append(new nw.MenuItem({
+        type: 'separator'
+    }));
+
+    editMenu.append(new nw.MenuItem({
+        label: 'Font',
+        click: menu_generic,
+        key: 'a',
+        modifiers: "ctrl"
+    }));
+
+    editMenu.append(new nw.MenuItem({
+        label: 'Cord Inspector',
+        click: function() {
+            pdgui.pdsend(name + " magicglass 0");
+        },
+        key: 'a',
+        modifiers: "ctrl"
+    }));
+
+    editMenu.append(new nw.MenuItem({
+        type: 'separator'
+    }));
+
+    editMenu.append(new nw.MenuItem({
+        label: 'Find',
+        click: menu_generic,
+        key: 'a',
+        modifiers: "ctrl"
+    }));
+
+    editMenu.append(new nw.MenuItem({
+        label: 'Find Again',
+        click: menu_generic,
+        key: 'a',
+        modifiers: "ctrl"
+    }));
+
+    editMenu.append(new nw.MenuItem({
+        label: 'Find Last Error',
+        click: menu_generic,
+        key: 'a',
+        modifiers: "ctrl"
+    }));
+
+    editMenu.append(new nw.MenuItem({
+        type: 'separator'
+    }));
+
+    editMenu.append(new nw.MenuItem({
+        label: 'Autotips',
+        click: menu_generic,
+        key: 'a',
+        modifiers: "ctrl"
+    }));
+
+    editMenu.append(new nw.MenuItem({
+        label: 'Editmode',
+        click: function() { pdgui.pdsend(name + " editmode 0"); },
+        key: 'e',
+        modifiers: "ctrl"
+    }));
+
+    editMenu.append(new nw.MenuItem({
+        type: 'separator'
+    }));
+
+    editMenu.append(new nw.MenuItem({
+        label: 'Preferences...',
+        click: menu_generic,
+        key: 'a',
+        modifiers: "ctrl"
+    }));
+
+    // Put menu
+    var putMenu = new nw.Menu();
+
+    // Add to window menu
+    windowMenu.append(new nw.MenuItem({
+    label: 'Put',
+    submenu: putMenu
+    }));
+
+    // Put menu sub-entries
+    putMenu.append(new nw.MenuItem({
+        label: 'Object',
+        click: function() {
+                   pdgui.pdsend(name + " dirty 1");
+                   pdgui.pdsend(name + " obj 0");
+        },
+        key: '1',
+        modifiers: "ctrl"
+    }));
+
+/*
+
+proc menu_floatatom {name accel} {
+	pd [concat $name dirty 1 \;]
+    pd [concat $name floatatom $accel \;]
+}
+
+proc menu_symbolatom {name accel} {
+	pd [concat $name dirty 1 \;]
+    pd [concat $name symbolatom $accel \;]
+}
+
+proc menu_comment {name accel} {
+	pd [concat $name dirty 1 \;]
+    pd [concat $name text $accel \;]
+}
+
+proc menu_graph {name} {
+	pd [concat $name dirty 1 \;]
+	set xdraw [expr int([$name.c canvasx 0])]
+	set ydraw [expr int([$name.c canvasy 0])]
+    pd [concat $name graph NULL 0 0 0 0 [expr $xdraw+30] [expr $ydraw+30] 0 [expr $ydraw+30]\;]
+    #pd [concat $name graph \;]
+}
+
+proc menu_array {name} {
+	pd [concat $name dirty 1 \;]
+    pd [concat $name menuarray \;]
+}
+
+
+*/
+
+    putMenu.append(new nw.MenuItem({
+        label: 'Message',
+        click: function() {
+                   pdgui.pdsend(name + " dirty 1");
+                   pdgui.pdsend(name + " msg 0");
+        },
+        key: '2',
+        modifiers: "ctrl"
+    }));
+
+    putMenu.append(new nw.MenuItem({
+        label: 'Number',
+        click: function() { 
+                   pdgui.pdsend(name + " dirty 1");
+                   pdgui.pdsend(name + " floatatom 0");
+        },
+        key: '3',
+        modifiers: "ctrl"
+    }));
+
+    putMenu.append(new nw.MenuItem({
+        label: 'Symbol',
+        click: function() {
+                   pdgui.pdsend(name + " dirty 1");
+                   pdgui.pdsend(name + " symbolatom 0");
+        },
+        key: '4',
+        modifiers: "ctrl"
+    }));
+
+    putMenu.append(new nw.MenuItem({
+        label: 'Comment',
+        click: function() {
+                   pdgui.pdsend(name + " dirty 1");
+                   pdgui.pdsend(name + " text 0");
+        },
+        key: '5',
+        modifiers: "ctrl"
+    }));
+
+    putMenu.append(new nw.MenuItem({
+        type: 'separator'
+    }));
+
+    putMenu.append(new nw.MenuItem({
+        label: 'Bang',
+        click: function(e) {
+                   pdgui.pdsend(name + " dirty 1");
+                   pdgui.pdsend(name + " bng 0");
+        },
+        key: 'b',
+        modifiers: "ctrl-shift"
+    }));
+
+    putMenu.append(new nw.MenuItem({
+        label: 'Toggle',
+        click: function() {
+                   pdgui.pdsend(name + " dirty 1");
+                   pdgui.pdsend(name + " toggle 0");
+        },
+        key: 't',
+        modifiers: "ctrl-shift"
+    }));
+
+    putMenu.append(new nw.MenuItem({
+        label: 'Number2',
+        click: function() {
+                   pdgui.pdsend(name + " dirty 1");
+                   pdgui.pdsend(name + " numbox 0");
+        },
+        key: 'n',
+        modifiers: "ctrl-shift"
+    }));
+
+    putMenu.append(new nw.MenuItem({
+        label: 'Vslider',
+        click: function() {
+                   pdgui.pdsend(name + " dirty 1");
+                   pdgui.pdsend(name + " vslider 0");
+        },
+        key: 'v',
+        modifiers: "ctrl-shift"
+    }));
+
+    putMenu.append(new nw.MenuItem({
+        label: 'Hslider',
+        click: function() {
+                   pdgui.pdsend(name + " dirty 1");
+                   pdgui.pdsend(name + " hslider 0");
+        },
+        key: 'h',
+        modifiers: "ctrl-shift"
+    }));
+
+    putMenu.append(new nw.MenuItem({
+        label: 'Vradio',
+        click: function() {
+                   pdgui.pdsend(name + " dirty 1");
+                   pdgui.pdsend(name + " vradio 0");
+        },
+        key: 'd',
+        modifiers: "ctrl"
+    }));
+
+    putMenu.append(new nw.MenuItem({
+        label: 'Hradio',
+        click: function() {
+                   pdgui.pdsend(name + " dirty 1");
+                   pdgui.pdsend(name + " hradio 0");
+        },
+        key: 'i',
+        modifiers: "ctrl"
+    }));
+
+    putMenu.append(new nw.MenuItem({
+        label: 'VU',
+        click: function() {
+                   pdgui.pdsend(name + " dirty 1");
+                   pdgui.pdsend(name + " vumeter 0");
+        },
+        key: 'u',
+        modifiers: "ctrl"
+    }));
+
+    putMenu.append(new nw.MenuItem({
+        label: 'Canvas',
+        click: function() {
+                   pdgui.pdsend(name + " dirty 1");
+                   pdgui.pdsend(name + " mycnv 0");
+        },
+        key: 'c',
+        modifiers: "ctrl"
+    }));
+
+    putMenu.append(new nw.MenuItem({
+        type: 'separator'
+    }));
+
+    putMenu.append(new nw.MenuItem({
+        label: 'Graph',
+        click: function() {
+            pdgui.pdsend(name + " dirty 1");
+            // leaving out some placement logic... see pd.tk menu_graph
+            pdgui.pdsend(name + " graph NULL 0 0 0 0 30 30 0 30");
+        },
+        key: 'a',
+        modifiers: "ctrl"
+    }));
+
+    putMenu.append(new nw.MenuItem({
+        label: 'Array',
+        click: menu_generic,
+        key: 'a',
+        modifiers: "ctrl"
+    }));
+
+
+    // Windows menu... call it "winman" (i.e., window management) to avoid confusion
+    var winmanMenu = new nw.Menu();
+
+    // Add to windows menu
+    windowMenu.append(new nw.MenuItem({
+    label: 'Windows',
+    submenu: winmanMenu
+    }));
+
+    // Winman sub-entries
+    winmanMenu.append(new nw.MenuItem({
+        label: 'Next Window',
+        click: menu_generic,
+        key: 'c',
+        modifiers: "ctrl"
+    }));
+
+    winmanMenu.append(new nw.MenuItem({
+        label: 'Previous Window',
+        click: menu_generic,
+        key: 'a',
+        modifiers: "ctrl"
+    }));
+
+    winmanMenu.append(new nw.MenuItem({
+        type: 'separator'
+    }));
+
+    winmanMenu.append(new nw.MenuItem({
+        label: 'Parent Window',
+        click: menu_generic,
+        key: 'a',
+        modifiers: "ctrl"
+    }));
+
+    winmanMenu.append(new nw.MenuItem({
+        label: 'Pd & Console',
+        click: menu_generic,
+        key: 'a',
+        modifiers: "ctrl"
+    }));
+
+    // Media menu
+    var mediaMenu = new nw.Menu();
+
+    // Add to window menu
+    windowMenu.append(new nw.MenuItem({
+    label: 'Media',
+    submenu: mediaMenu
+    }));
+
+    // Media sub-entries
+    mediaMenu.append(new nw.MenuItem({
+        label: 'Audio On',
+        click: menu_generic,
+        key: 'c',
+        modifiers: "ctrl"
+    }));
+
+    mediaMenu.append(new nw.MenuItem({
+        label: 'Audio Off',
+        click: menu_generic,
+        key: 'a',
+        modifiers: "ctrl"
+    }));
+
+    mediaMenu.append(new nw.MenuItem({
+        type: 'separator'
+    }));
+
+    mediaMenu.append(new nw.MenuItem({
+        label: 'Test Audio and Midi',
+        click: menu_generic,
+        key: 'a',
+        modifiers: "ctrl"
+    }));
+
+    mediaMenu.append(new nw.MenuItem({
+        label: 'Load Meter',
+        click: menu_generic,
+        key: 'a',
+        modifiers: "ctrl"
+    }));
+
+    // Help menu
+    var helpMenu = new nw.Menu();
+
+    // Add to window menu
+    windowMenu.append(new nw.MenuItem({
+    label: 'Help',
+    submenu: helpMenu
+    }));
+
+    // Help sub-entries
+    helpMenu.append(new nw.MenuItem({
+        label: 'About Pd-L2ork',
+        click: menu_generic,
+        key: 'c',
+        modifiers: "ctrl"
+    }));
+
+    helpMenu.append(new nw.MenuItem({
+        label: 'Manual',
+        click: menu_generic,
+        key: 'a',
+        modifiers: "ctrl"
+    }));
+
+    helpMenu.append(new nw.MenuItem({
+        label: 'Help Browser',
+        click: menu_generic,
+        key: 'a',
+        modifiers: "ctrl"
+    }));
+
+    helpMenu.append(new nw.MenuItem({
+        type: 'separator'
+    }));
+
+    helpMenu.append(new nw.MenuItem({
+        label: 'Pd-L2ork Mailing List',
+        click: menu_generic,
+        key: 'a',
+        modifiers: "ctrl"
+    }));
+
+    helpMenu.append(new nw.MenuItem({
+        label: 'Pure Data Mailing Lists',
+        click: menu_generic,
+        key: 'a',
+        modifiers: "ctrl"
+    }));
+
+    helpMenu.append(new nw.MenuItem({
+        label: 'Forums',
+        click: menu_generic,
+        key: 'a',
+        modifiers: "ctrl"
+    }));
+
+    helpMenu.append(new nw.MenuItem({
+        label: 'IRC Chat',
+        click: menu_generic,
+        key: 'a',
+        modifiers: "ctrl"
+    }));
+
+    // Assign to window
+    nw.Window.get().menu = windowMenu;
+
+}
+
+
+
+/*
+function nw_create_canvas_window_menus(name) {
+	// Window menu
+	var windowMenu = new nw.Menu;
+	var windowMenu = new nw.Menu({
+	    type: 'menubar'
+	});
+
+
+	// File menu
+	var fileMenu = new nw.Menu();
+
+	// Add to window menu
+	windowMenu.append(new nw.MenuItem({
+	    label: 'File',
+	    submenu: fileMenu
+	}));
+
+	// File sub-entries
+	fileMenu.append(new nw.MenuItem({
+	    label: 'New',
+	    click: pdgui.menu_new,
+	    key: 'n',
+	    modifiers: "ctrl"
+	}));
+
+	fileMenu.append(new nw.MenuItem({
+	    label: 'Open',
+	    key: 'o',
+	    modifiers: "ctrl",
+	    click: function (){
+		var chooser = document.querySelector('#fileDialog');
+		chooser.click();
+		chooser.addEventListener("change", function(evt) {
+		    menu_open(this.value);
+		    console.log("tried to open something");
+		}, false);
+	    }
+	}));
+
+	if (pdgui.k12_mode == 1) {
+	    fileMenu.append(new nw.MenuItem({
+		label: 'K12 Demos',
+		click: pdgui.menu_k12_open_demos
+	    }));
+	}
+
+	fileMenu.append(new nw.MenuItem({
+	    type: 'separator'
+	}));
+
+	// Note: this must be different for the main Pd window
+	fileMenu.append(new nw.MenuItem({
+	    label: 'Save',
+	    click: function (){
+		pdgui.menu_save(name);
+	    },
+	    key: 's',
+	    modifiers: "ctrl"
+	}));
+
+	fileMenu.append(new nw.MenuItem({
+	    label: 'Save as...',
+	    click: function (){
+		pdgui.menu_saveas(name);
+	    },
+	    key: 'S',
+	    modifiers: "ctrl"
+	}));
+
+	if (pdgui.k12_mode == 0) {
+	    fileMenu.append(new nw.MenuItem({
+		type: 'separator'
+	    }));
+	}
+
+	// Assign to window
+	nw.Window.get().menu = windowMenu;
+}
+*/
+
+  </script>
+  </body>
+</html>
diff --git a/pd/nw/pdgui.js b/pd/nw/pdgui.js
new file mode 100644
index 0000000000000000000000000000000000000000..825bafcca3a97700da137998c8579acad0dca7fa
--- /dev/null
+++ b/pd/nw/pdgui.js
@@ -0,0 +1,2695 @@
+'use strict';
+
+// Modules
+
+var pwd;
+
+exports.set_pwd = function(pwd_string) {
+    pwd = pwd_string;
+}
+
+exports.get_pwd = function() {
+    return pwd;
+}
+
+var fs = require('fs');     // for fs.existsSync
+var path = require('path'); // for path.dirname path.extname
+
+var pd_window; 
+exports.pd_window;
+
+exports.set_pd_window = function(win) {
+    pd_window = win;
+    exports.pd_window = win;
+}
+
+var nw_create_window;
+var nw_close_window;
+var nw_app_quit;
+
+exports.set_new_window_fn = function (nw_context_fn) {
+    nw_create_window = nw_context_fn;
+}
+
+exports.set_close_window_fn = function (nw_context_fn) {
+    nw_close_window = nw_context_fn;
+}
+
+// Global variables from tcl
+var pd_myversion,    // Pd version string
+    pd_apilist,      // Available Audio APIs (tcl list)
+    pd_midiapilist,  // MIDI APIsa (tcl list)
+    pd_nt,           // Something to do with Windows configuration
+    fontname,        // Font
+    fontweight,      //  config
+    pd_fontlist,     //   (Seems to be hard coded in Pd-l2ork)
+    pd_whichmidiapi, // MIDI API, set by pd->gui message
+    pd_whichapi,     // Audio API, set by pd->gui message
+    pd_opendir,      //
+    pd_guidir,       //
+    pd_tearoff,      //
+    put_tearoff,     //
+    tcl_version,     //
+    canvas_fill,     //
+    colors,          //
+    global_clipboard, //
+    global_selection, //
+    k12_mode = 0,         // should be set from argv ('0' is just a stopgap)
+    k12_saveas_on_new, //
+    autotips,          // tooltips
+    magicglass,        // cord inspector
+    window_prefs,      //retaining window-specific preferences
+    pdtk_canvas_mouseup_name, // not sure what this does
+    filetypes,         // valid file extensions for opening/saving (includes Max filetypes)
+    untitled_number,   // number to increment for each new patch that is opened
+    untitled_directory, // default directory where to create/save new patches
+    popup_coords,       // x/y for current popup window (global because there's only one at a time)
+    pd_colors = {};                // associative array of canvas color presets
+
+    var pd_filetypes = { ".pd": "Pd Files",
+                         ".pat":"Max Patch Files",
+                         ".mxt":"Max Text Files",
+                         ".mxb":"Max Binary Files",
+                         ".help":"Max Help Files"
+                       };
+
+    exports.k12_mode = k12_mode;
+    exports.pd_filetypes = pd_filetypes;
+
+    popup_coords = [0,0];
+
+    var startup_files = []; // Array of files to be opened at startup (from the command line)
+
+    var pd_keymap = {}; // to iteratively map keydown/keyup keys
+                        // to keypress char codes
+
+function set_keymap(keycode, charcode) {
+    pd_keymap[keycode] = charcode;
+}
+
+exports.set_keymap = set_keymap;
+
+function get_char_code(keycode) {
+    return pd_keymap[keycode];
+}
+
+exports.get_char_code = get_char_code;
+
+    // Hard-coded Pd-l2ork font metrics
+/*
+var font_fixed_metrics = "\
+8 5 11 \
+9 6 12 \
+10 6 13 \
+12 7 16 \
+14 8 17 \
+16 10 19 \
+18 11 22 \
+24 14 29 \
+30 18 37 \
+36 22 44";
+*/
+
+// Let's try to get some metrics specific to Node-webkit...
+    // Hard-coded Pd-l2ork font metrics
+var font_fixed_metrics = "\
+8 5 11 \
+9 6 12 \
+10 6 13 \
+12 7 16 \
+14 8 17 \
+16 10 19 \
+18 11 22 \
+24 14 29 \
+30 18 37 \
+36 22 44";
+
+
+// Utility Functions
+
+// originally used to enquote a string to send it to a tcl function
+function enquote (x) {
+    var foo = x.replace(/,/g, "");
+    foo = foo.replace(/;/g, "");
+    foo = foo.replace(/"/g, "");
+    foo = foo.replace(/ /g, "\\ ");
+    foo = foo.trim();
+    return foo;
+}
+
+// from stackoverflow.com/questions/21698906/how-to-check-if-a-path-is-absolute-or-relative
+// this doesn't seem to be needed atm
+function path_is_absolute(myPath) {
+    var ret = (path.resolve(myPath) === path.normalize(myPath).replace(/(.+)([\/]\\])$/, '$1'));
+    return ret;
+}
+
+function set_midiapi(val) {
+    pd_whichmidiapi = val;
+}
+
+function set_audioapi(val) {
+    pd_whichapi = val;
+}
+
+function gui_post(string, color) {
+    if (color === undefined) { color = "black" };
+    var myp = pd_window.document.getElementById('p1');
+    var text;
+    var span = pd_window.document.createElement("span");
+    span.style.color = color;
+    var text = pd_window.document.createTextNode(string + "\n"); 
+    span.appendChild(text);
+    myp.appendChild(span);
+    pd_window.window.scrollTo(0, pd_window.document.body.scrollHeight);
+}
+
+exports.gui_post = gui_post;
+
+function pd_error_select_by_id(objectid) {
+    if (objectid !== null) {
+        pdsend("pd findinstance " + objectid);
+    }
+}
+exports.pd_error_select_by_id = pd_error_select_by_id
+
+function gui_post_error(objectid, loglevel, errormsg) {
+    var my_p = pd_window.document.getElementById('p1');
+    // if we have an object id, make a friendly link...
+    var error_title = pd_window.document.createTextNode("error");
+    if (objectid.length > 0) {
+        var my_a = pd_window.document.createElement('a');
+        my_a.href = "javascript:pdgui.pd_error_select_by_id('" + objectid + "')";
+        my_a.appendChild(error_title);
+        my_p.appendChild(my_a);
+    } else {
+        my_p.appendChild(error_title);
+    }
+    var rest = pd_window.document.createTextNode(": " + errormsg + "\n");
+    my_p.appendChild(rest);
+
+    // looks like tcl/tk tried to throttle this... maybe we should, too...
+    /*
+    after cancel .printout.frame.text yview end-2char
+    after idle .printout.frame.text yview end-2char
+        .printout.frame.text configure -state disabled
+    */
+}
+
+function menu_save(name) {
+//    gui_post(name + " menusave");
+    pdsend(name + " menusave");
+}
+
+exports.menu_save = menu_save;
+
+function gui_canvas_saveas (name, initfile, initdir) {
+    gui_post("working directory is " + pwd);
+    //global pd_nt filetypes untitled_directory
+    if (!fs.existsSync(initdir)) {
+        initdir = pwd;
+    }
+//    patchwin[name].window.document.getElementById("fileDialog").setAttribute("nwworkingdir", pwd);
+    var chooser = patchwin[name].window.document.querySelector('#saveDialog');
+    chooser.click();
+//    chooser.addEventListener("change", function(evt) {
+//        saveas_callback(name, this.value);
+//        console.log("tried to open something");
+//    }, false);
+}
+
+function saveas_callback(cid, file) {
+    gui_post("tried a saveas, and the file chosen is " + file);
+    var filename = file;
+    // It probably isn't possible to arrive at the callback with an
+    // empty string.  But I've only tested on Debian so far...
+    if (filename === null) {
+        return;
+    }
+    // We don't need to use the codepath below because node-webkit
+    // let's us specify the allowed files extensions.  Lo and behold,
+    // nw just does the "right thing" whether the user types an extension
+    // or not.  This should put us on part with Microsoft Word in the late
+    // 90s.
+    //var lc = filename.toLowerCase();
+    //if (lc.slice(-3) !== '.pd' &&
+    //    lc.slice(-4) !== '.pat' &&
+    //    lc.slice(-4) !== '.mxt') {
+        // remove any other extensions
+    //    filename = filename.slice(0,
+    //        (filename.length - path.extname(filename).length));
+        // add ".pd"
+    //    filename = filename + '.pd';
+    //}
+    // test again after downcasing and maybe adding a ".pd" on the end
+    //if (fs.existsSync(filename)) {
+    //    var reply = patchwin[cid].window.confirm(filename +
+    //        " already exists. Do you want to replace it?");
+    //    if (!reply) {
+    //        return;
+    //    }
+    //}
+    var directory = path.dirname(filename);
+    var basename = path.basename(filename);
+    pdsend(cid + " savetofile " + enquote(basename) + " " +
+        enquote(directory));
+
+    // haven't implemented these last few commands yet...
+    // set untitled_directory $directory
+    // add to recentfiles
+    //::pd_guiprefs::update_recentfiles "$filename" 1
+}
+
+exports.saveas_callback = saveas_callback;
+
+function menu_saveas(name) {
+    pdsend(name + " menusaveas");
+}
+
+exports.menu_saveas = menu_saveas;
+
+function menu_new () {
+    //if { ! [file isdirectory $untitled_directory]} {set untitled_directory $::env(HOME)}
+    untitled_directory = pwd;
+    pdsend("pd filename " + "Untitled-" + untitled_number + " " + enquote(untitled_directory));
+    if (k12_mode == 1) {
+        k12_saveas_on_new = 1;
+        pdsend("#N canvas");
+        pdsend("#X obj -30 -30 preset_hub k12 1 %hidden%");
+        pdsend("#X pop 1");
+    } else {
+        pdsend("#N canvas");
+        pdsend("#X pop 1");
+    }
+    untitled_number++;
+}
+
+exports.menu_new = menu_new;
+
+function gui_close_window(cid) {
+    nw_close_window(patchwin[cid]);
+}
+
+function menu_k12_open_demos () {
+
+}
+
+exports.menu_k12_open_demos = menu_k12_open_demos;
+
+
+function menu_open (filenames_string) {
+    gui_post("menu_open " + filenames_string);
+    var file_array = filenames_string.split(";");
+    var length = file_array.length;
+    gui_post("file_array is " + file_array);
+    for (var i = 0; i < length; i++) {
+        open_file(file_array[i]);
+    }
+}
+
+exports.menu_open = menu_open;
+
+function menu_close(name) {
+    // not handling the "text editor" yet
+    // not handling the "Window" menu yet
+    //pdtk_canvas_checkgeometry $name
+    pdsend(name + " menuclose 0");
+}
+
+exports.menu_close = menu_close;
+
+function canvas_menuclose_callback(cid_for_dialog, cid, force) {
+    // Hacky-- this should really be dir/filename here instead of
+    // filename/args/dir which is ugly
+    var title = patchwin[cid_for_dialog].window.document.title;
+    var reply = patchwin[cid_for_dialog].window.confirm("Save changes to " + title + "?");
+    if (reply) {
+        pdsend(cid_for_dialog + " menusave");
+    } else {
+        pdsend(cid_for_dialog + " dirty 0");        
+        pdsend(cid + " menuclose " + force);
+    }
+}
+
+function gui_canvas_menuclose(cid_for_dialog, cid, force) {
+    // Hack to get around a renderer bug-- not guaranteed to work
+    // for long patches
+    setTimeout(function() {
+            canvas_menuclose_callback(cid_for_dialog, cid, force);
+        }, 450);
+}
+
+function gui_pd_quit_dialog() {
+    var reply = pd_window.window.confirm("Really quit?");
+    if (reply === true) {
+        pdsend("pd quit");
+    }
+}
+
+// send a message to Pd
+function menu_send() {
+    gui_post("message...pdwindow is " + pd_window);
+    var message = pd_window.window.prompt("Type a message to send to Pd");
+    if (message != undefined && message.length) {
+        gui_post("Sending message to Pd: " + message + ";");
+        pdsend(message);
+    }
+}
+
+exports.menu_send = menu_send;
+
+function menu_quit() {
+    pdsend("pd verifyquit");
+}
+
+exports.menu_quit = menu_quit;
+
+var nw_app_quit;
+
+function app_quit() {
+    nw_app_quit();
+}
+
+exports.set_app_quitfn = function(quitfn) {
+    nw_app_quit = quitfn;
+} 
+
+function open_file(file) {
+    //from tcl...
+    //global pd_opendir pd_guidir pd_nt
+    gui_post("open_file: " + file);
+    var directory = path.dirname(file);
+    var basename = path.basename(file);
+    var cyclist;
+    if (basename.match(/\.(pat|mxb|help)$/) !=null) {
+        gui_post("warning: opening pat|mxb|help not implemented yet");
+        //gui_post("converting " + filename);
+        if (pd_nt == 0) {
+            // on GNU/Linux, cyclist is installed into /usr/bin usually
+            cyclist = "/usr/bin/cyclist";
+        } else {
+            cyclist = pd_guidir + "/bin/cyclist"
+        }
+        //gui_post(cyclist + filename);
+        //convert Max binary to text .pat
+        // The following is tcl code which needs to get converted to javascript...
+        //set binport [open "| \"$cyclist\" \"$filename\""]
+        //set convertedtext [read $binport]
+        //if { ! [catch {close $binport} err]} {
+        //    if {! [file writable $directory]} {     set directory "/tmp" }
+        //    set basename "$basename.pat"
+        //    set textpatfile [open "$directory/$basename" w]
+        //    puts $textpatfile $convertedtext
+        //    close $textpatfile
+        //    puts stderr "converted Max binary to text format: $directory/$basename"
+        //}
+    }
+    if (basename.match(/\.(pd|pat|mxt)$/i) != null) {
+        pdsend("pd open" + " " + enquote(basename) + " " + enquote(directory));
+        pd_opendir = directory;
+        //::pd_guiprefs::update_recentfiles "$filename" 1
+    }
+}
+
+// Doesn't work yet... need to figure out how to send command line args (files) to be opened by
+// the unique instance 
+function gui_open_files_via_unique(filenames)
+{
+    gui_post("pdtk_open_files_via_unique " + filenames);
+    length = filenames.length;
+    if (length != 0) {
+        for (var i = 0; i < length; i++) {
+            var file = filenames[i];
+            //gui_post("open_file " + file);
+            open_file(file);
+        }
+    }
+}
+
+function gui_build_filelist(file) {
+    startup_files.push(file);
+}
+
+// This doesn't work at the moment.  Not sure how to feed the command line filelist to a single
+// instance of node-webkit.
+function gui_check_unique (unique) {
+        // gui_post("pdtk_check_unique " + unique);
+    // global appname
+    return;
+    var final_filenames = new Array;
+    var startup_dir = pwd;
+    if (unique == 0) {
+        var filelist_length = startup_files.length;
+        for (var i = 0; i < filelist_length; i++) {
+            var file = startup_files[i];
+            var dir;
+            //gui_post (file [file dirname $file] $startup_dir"
+            if (!pathIsAbsolute(file)) {
+                file = fs.join(pwd, file);
+            }
+            final_filenames.push(file);  
+        }
+        gui_open_files_via_unique(final_filenames);
+    }
+    // old tcl follows...
+	//if path is relative
+	//then join pwd and relative path
+	//else
+	//use the absolute path
+	//(no need to check for existence here)
+
+	//            catch {cd [file dirname $file]}
+	//            set dir [pwd]
+
+	//file tail should be the filename
+
+	//            set name [file tail $file]
+	//            #puts stderr "********DIR:$dir FILE:$name COMBINED:[file join $dir $name]"
+	//            lappend final_filenames [file join $dir $name]
+	//            cd $startup_dir
+	//        }
+	//        #puts stderr "send pd-l2ork pdtk_open_files_via_unique $final_filenames"
+	//        set outcome [catch {send pd-l2ork pdtk_open_files_via_unique \{$final_filenames\}}]
+	//        #puts stderr "outcome = $outcome"
+	//        if { $outcome == 0 } {
+	//                menu_really_quit
+	//                exit
+	//        }
+	//}
+	//        tk appname $appname
+	//        #puts stderr "this is unique instance [tk appname]"
+}
+
+
+
+function gui_startup(version, apilist, midiapilist, fontname_from_pd, fontweight_from_pd) {
+    console.log("we're starting up...");
+    // # tb: user defined typefaces
+    // our args:
+//    console.log(version);
+//    console.log(apilist);
+//    console.log(fontname);
+
+    // set some global variables
+    pd_myversion = version;
+    pd_apilist =  apilist;
+    pd_midiapilist = midiapilist;
+
+    fontname = fontname_from_pd;
+    fontweight = fontweight_from_pd;
+    pd_fontlist = "";
+    untitled_number = 1; // global variable to increment for each new patch
+
+    // From tcl, not sure if needed...
+       // # on Mac OS X, lower the Pd window to the background so patches open on top
+       // if {$pd_nt == 2} { lower . }
+       // # on Windows, raise the Pd window so that it has focused when launched
+       // if {$pd_nt == 1} { raise . }
+
+//    set fontlist ""
+//        if {[info tclversion] >= 8.5} {find_default_font}
+//        set_base_font $fontname_from_pd $fontweight_from_pd
+//        fit_font_into_metrics
+
+//    # UBUNTU MONO 6 6 8 10 11 14 14 19 22 30
+//        # DEJAVU SANS MONO 6 6 8 9 10 12 14 18 22 29
+
+//#    foreach i {6 6 8 10 11 14 14 19 22 30} {
+//#        set font [format {{%s} %d %s} $fontname_from_pd $i $fontweight_from_pd]
+//#        set pd_fontlist [linsert $pd_fontlist 100000 $font]
+//#        set width0 [font measure  $font x]
+//#        set height0 [lindex [font metrics $font] 5]
+//#        set fontlist [concat $fontlist $i [font measure  $font x] \
+//#                          [lindex [font metrics $font] 5]]
+//#    }
+
+//    set tclpatch [info patchlevel]
+//    if {$tclpatch == "8.3.0" || \
+//            $tclpatch == "8.3.1" || \
+//            $tclpatch == "8.3.2" || \
+//            $tclpatch == "8.3.3" } {
+//        set oldtclversion 1
+//    } else {
+//        set oldtclversion 0
+//    }
+    pdsend("pd init " + enquote(pwd) + " 0 " + font_fixed_metrics);
+
+//    # add the audio and help menus to the Pd window.  We delayed this
+//    # so that we'd know the value of "apilist".
+//    menu_addstd .mbar
+
+//    global pd_nt
+//    if {$pd_nt == 2} {
+//        global pd_macdropped pd_macready
+//        set pd_macready 1
+//        foreach file $pd_macdropped {
+//            pd [concat pd open [pdtk_enquote [file tail $file]] \
+//                    [pdtk_enquote  [file dirname $file]] \;]
+//            menu_doc_open [file dirname $file] [file tail $file]
+//        }
+//    }
+}
+
+
+
+
+
+ 
+
+/*
+    // Some other menu
+    var fooMenu = new nw.Menu();
+
+    // Add to window menu
+    windowMenu.append(new nw.MenuItem({
+        label: 'Foo',
+        submenu: fooMenu
+    }));
+
+    // Foo sub-entry
+    fooMenu.append(new nw.MenuItem({
+        label: 'flub',
+        click: function(){
+            alert('Foo flub');
+        }
+    }));
+
+    // Another Foo sub-entry
+    fooMenu.append(new nw.MenuItem({
+        label: 'bub',
+        click: function(){
+            alert('Foo bub');
+        }
+    }));
+}
+
+
+/*
+function canvas_create_menus(name) {
+    // the "File" menu
+	
+    // The menus are instantiated here for the patch windows.
+    // For the main window, they are created on load, at the 
+    // top of this file.
+    match_linux_wm [list menu $name.m -relief flat]
+    match_linux_wm [list menu $name.m.file  -postcommand [concat pdtk_fixfilemenu $name.m.file] -tearoff $pd_tearoff]
+    $name.m add cascade -label File -menu $name.m.file
+
+    $name.m.file add command -label New -command {menu_new} \
+        -accelerator [accel_munge "Ctrl+n"]
+
+    $name.m.file add command -label Open -command {menu_open} \
+        -accelerator [accel_munge "Ctrl+o"]
+
+	if { $k12_mode == 1 } {
+		$name.m.file add command -label {K12 Demos} -command {menu_k12_open_demos}
+	}
+
+    match_linux_wm [list $name.m.file add separator]
+
+    $name.m.file add command -label Save -command [concat menu_save $name] \
+        -accelerator [accel_munge "Ctrl+s"]
+
+    $name.m.file add command -label "Save as..." \
+        -command [concat menu_saveas $name] \
+        -accelerator [accel_munge "Ctrl+S"]
+	if { $k12_mode == 0 } {
+		match_linux_wm [list $name.m.file add separator]
+
+		# arrange menus according to Apple HIG
+		if {$pd_nt != 2 } {
+			$name.m.file add command -label "Message..." -command {menu_send} \
+				-accelerator [accel_munge "Ctrl+m"]
+			# these are now part of Preferences... on Mac OS X
+		    $name.m.file add command -label Path... \
+		        -command {pd pd start-path-dialog \;} 
+		    $name.m.file add command -label Startup... \
+		        -command {pd pd start-startup-dialog \;} 
+		} else { 
+			# Cmd-m is minimize window on Mac OS X		
+			$name.m.file add command -label "Message..." -command {menu_send}
+			match_linux_wm [list $name.m.file add  separator]
+			$name.m.file add command -label "Make app from patch..." \
+				-command {menu_makeapp 0}
+			$name.m.file add command -label "Make app from folder..." \
+				-command {menu_makeapp 1}
+		}
+		match_linux_wm [list $name.m.file add separator]
+		$name.m.file add command -label "Print..." -command [concat menu_print $name] \
+		    -accelerator [accel_munge "Ctrl+p"]
+	}
+    # update recent files
+    match_linux_wm [list $name.m.file add separator]
+    $name.m.file add command -label "No Recent Files" -state disabled
+    #match_linux_wm [list $name.m.file add separator]
+    #if {[llength $::recentfiles_list] > 0} {
+    #    ::pd_menus::update_recentfiles_menu $name.m.file false
+    #}
+
+	match_linux_wm [list $name.m.file add separator]
+    $name.m.file add command -label Close \
+        -command [concat menu_close $name] \
+        -accelerator [accel_munge "Ctrl+w"]
+
+	if {$pd_nt != 2} {
+		# Mac OS X doesn't put Quit on the File menu
+		$name.m.file add command -label Quit -command {menu_quit} \
+			-accelerator [accel_munge "Ctrl+q"]
+	}
+
+    # the "Edit" menu
+    match_linux_wm [list menu $name.m.edit -postcommand [concat menu_fixeditmenu $name] -tearoff $pd_tearoff]
+    $name.m add cascade -label Edit -menu $name.m.edit
+    
+    $name.m.edit add command -label Undo -command [concat menu_undo $name] \
+        -accelerator [accel_munge "Ctrl+z"]
+
+    $name.m.edit add command -label Redo -command [concat menu_redo $name] \
+        -accelerator [accel_munge "Ctrl+Z"]
+
+    match_linux_wm [list $name.m.edit add separator]
+
+    $name.m.edit add command -label Cut -command [concat menu_cut $name] \
+        -accelerator [accel_munge "Ctrl+x"] -state disabled
+
+    $name.m.edit add command -label Copy -command [concat menu_copy $name] \
+        -accelerator [accel_munge "Ctrl+c"] -state disabled
+
+    $name.m.edit add command -label Paste \
+        -command [concat menu_paste $name] \
+        -accelerator [accel_munge "Ctrl+v"]
+
+	if {!$global_clipboard} {
+		$name.m.edit entryconfigure "Paste" -state disabled
+	} else {
+		$name.m.edit entryconfigure "Paste" -state normal
+	}
+
+    $name.m.edit add command -label Duplicate \
+        -command [concat menu_duplicate $name] \
+        -accelerator [accel_munge "Ctrl+d"]
+
+	if {!$global_selection} {
+		$name.m.edit entryconfigure "Duplicate" -state disabled
+	} else {
+		$name.m.edit entryconfigure "Duplicate" -state normal
+	}
+
+    $name.m.edit add command -label {Select all} \
+        -command [concat menu_selectall $name] \
+        -accelerator [accel_munge "Ctrl+a"]
+
+	if { $k12_mode == 0 } {
+		$name.m.edit add command -label {Reselect} \
+		    -command [concat menu_reselect $name] \
+		    -accelerator "Ctrl+Enter" -state disabled
+	}
+
+    match_linux_wm [list $name.m.edit add separator]
+
+	$name.m.edit add command -label {Tidy Up} \
+		-command [concat menu_tidyup $name] \
+	    -accelerator [accel_munge "Ctrl+y"] -state disabled
+
+	if { $k12_mode == 0 } {
+
+		$name.m.edit add command -label {Bring To Front} \
+			-command [concat popup_action $name 3] \
+		    -accelerator [accel_munge "Ctrl+Up"] -state disabled
+
+		$name.m.edit add command -label {Send To Back} \
+			-command [concat popup_action $name 4] \
+		    -accelerator [accel_munge "Ctrl+Down"] -state disabled
+	}
+
+	match_linux_wm [list $name.m.edit add separator]
+
+	if { $k12_mode == 0 } {
+
+		#if {$pd_nt == 2} { # no key command on Mac OS X, conflicts with standard
+		#	$name.m.edit add command -label {Text Editor} \
+		#		-command [concat menu_texteditor $name]
+		#} else {
+		#	$name.m.edit add command -label {Text Editor} \
+		#		-accelerator [accel_munge "Ctrl+t"] \
+		#		-command [concat menu_texteditor $name]
+		#}
+
+		$name.m.edit add command -label Font \
+		    -command [concat menu_font $name] 
+	}
+
+## jsarlo
+    $name.m.edit add checkbutton -label "Cord Inspector" \
+        -indicatoron false -selectcolor black \
+        -command [concat menu_magicglass $name] \
+        -accelerator [accel_munge "Ctrl+r"]
+
+    #if { $editable == 0 } {
+    #       $name.m.edit entryconfigure "Cord Inspector" -indicatoron false }
+  
+    match_linux_wm [list $name.m.edit add separator]
+## end jsarlo
+
+	$name.m.edit add command -label "Toggle console" \
+	    -accelerator [accel_munge "Ctrl+R"] \
+	    -command [concat .controls.switches.console invoke]
+
+	$name.m.edit add command -label "Clear console" \
+	    -accelerator [accel_munge "Ctrl+L"] \
+	    -command [concat menu_clear_console]
+
+	match_linux_wm [list $name.m.edit add separator]
+
+	if { $k12_mode == 0 } {
+    
+		# Apple, Microsoft, and others put find functions in the Edit menu.
+		$name.m.edit add command -label {Find...} \
+		    -accelerator [accel_munge "Ctrl+f"] \
+		    -command [concat menu_findobject $name] 
+		$name.m.edit add command -label {Find Again} \
+		    -accelerator [accel_munge "Ctrl+g"] \
+		    -command [concat menu_findagain $name] 
+		$name.m.edit add command -label {Find last error} \
+		    -command [concat menu_finderror] 
+
+		match_linux_wm [list $name.m.edit add separator]
+
+		############iemlib##################
+		# instead of "red = #BC3C60" we take "grey85", so there is no difference,
+		# if widget is selected or not.
+
+		$name.m.edit add checkbutton -label "Autotips" \
+		    -indicatoron false -selectcolor black \
+		    -command [concat menu_tooltips $name] \
+		    -accelerator [accel_munge "Ctrl+E"]
+	}
+
+    $name.m.edit add checkbutton -label "Edit mode" \
+        -indicatoron false -selectcolor black \
+        -command [concat menu_editmode $name] \
+        -accelerator [accel_munge "Ctrl+e"]
+    if {$k12_mode == 0} {
+    	match_linux_wm [list $name.m.edit add separator]
+        $name.m.edit add command -label {Preferences...} \
+        -command {::dialog_prefs::open_prefs_dialog .}
+    }
+
+	if { $editable == 1 } {
+    	$name.m.edit entryconfigure "Edit mode" -background "#7dd37d" -foreground black
+	}
+
+	if { $k12_mode == 0 && $autotips == 1 } {
+    	$name.m.edit entryconfigure "Autotips" -background "#7dd37d" -foreground "#dddddd"
+	}
+
+	set ::editmode($name) $editable
+
+	#if { $magicglass == 1 } {
+    #	$name.m.edit entryconfigure "Cord Inspector" -background "#7dd37d"
+	#	menu_magicglass $name
+	#}
+
+    #if { $editable == 0 } {
+    #    $name.m.edit entryconfigure "Edit mode" -indicatoron false 
+    #}
+
+*/
+
+
+
+// From what used to be canvas.js
+
+// Global canvas associative arrays (aka javascript objects)
+var scroll = {},
+    menu = {},
+    canvas_color = {},
+    topmost = {},
+    resize = {},
+    xscrollable = {},
+    yscrollable = {},
+    update_tick = {},
+    drag_tick = {},
+    undo = {},
+    redo = {},
+    font = {},
+    doscroll = {},
+    last_loaded,
+    loaded = {},
+    popup_menu = {};
+
+    var patchwin = {}; // Associative array of patch windows
+
+exports.get_patchwin = function(name) {
+    return patchwin[name];
+}
+
+// stopgap...
+pd_colors['canvas_color'] = "white";
+
+exports.last_loaded = function () {
+    return last_loaded;
+}
+
+// close a canvas window
+
+function gui_canvas_cursor(cid, pd_event_type) {
+    var patch = get_item(cid, "patchsvg");
+    var c;
+    // A quick mapping of events to pointers-- these can
+    // be revised later
+    switch(pd_event_type) {
+        case "cursor_runmode_nothing":
+            c = 'default';
+            break;
+        case "cursor_runmode_clickme":
+            c = 'pointer';
+            break;
+        case "cursor_runmode_thicken":
+            c = 'inherit';
+            break;
+        case "cursor_runmode_addpoint":
+            c = 'cell';
+            break;
+        case "cursor_editmode_nothing":
+            c = 'pointer';
+            break;
+        case "cursor_editmode_connect":
+            c = '-webkit-grabbing';
+            break;
+        case "cursor_editmode_disconnect":
+            c = 'no-drop';
+            break;
+        case "cursor_editmode_resize":
+            c = 'ew-resize';
+            break;
+        case "cursor_editmode_resize_bottom_right":
+            c = 'se-resize';
+            break;
+        case "cursor_scroll":
+            c = 'all-scroll'; 
+            break;
+    }
+    patch.style.cursor = c;
+}
+
+function gui_canvas_sendkey(cid, state, evt, char_code) {
+    pdsend(
+        cid + " key " +
+        state + " " +
+        char_code + " " +
+        (evt.shiftKey ? 1 : 0) + " " +
+        1 + " " +
+        (evt.repeat ? 1 : 0)
+    );
+}
+
+exports.gui_canvas_sendkey = gui_canvas_sendkey;
+
+function title_callback(cid, title) {
+    patchwin[cid].window.document.title = title;
+}
+
+function format_window_title(name, dirty_flag, args, dir) {
+        return name + " " + (dirty_flag ? "*" : "") + args + " - " + dir;
+}
+
+exports.format_window_title = format_window_title;
+
+// This should be used when a file is saved with the name changed (and maybe in other situations)
+function gui_canvas_set_title(cid, name, args, dir, dirty_flag) {
+    var title = format_window_title(name, dirty_flag, args, dir);
+    patchwin[cid].title = title;
+}
+
+// create a new canvas
+// todo: rename parameter "name" to "cid"
+function gui_canvas_new(cid, width, height, geometry, editable, name, dir, dirty_flag, cargs) {
+    gui_post("canvas name is " + name);
+    gui_post("canvas string is " + name);
+    gui_post("canvas dir " + dir);
+    gui_post("canvas dirty_flag is " + dirty_flag);
+    gui_post("canvas cargsis " + cargs);
+    // hack for buggy tcl popups... should go away for node-webkit
+    //reset_ctrl_on_popup_window
+
+    // local vars for window-specific behavior
+    // visibility of menu and scrollbars, plus canvas background
+    scroll[cid] = 1;
+    menu[cid] = 1;
+    // attempt at getting global presets to play
+    // well with local settings.
+    var my_canvas_color = "";
+    //canvas_color[cid] = orange;
+    my_canvas_color = pd_colors['canvas_color'];
+    topmost[cid] = 0;
+    resize[cid] = 1;
+    xscrollable[cid] = 0;
+    yscrollable[cid] = 0;
+    update_tick[cid] = 0;
+    drag_tick[cid] = 0;
+    undo[cid] = false;
+    redo[cid] = false;
+    font[cid] = 10;
+    doscroll[cid] = 0;
+    // geometry is just the x/y screen offset "+xoff+yoff"
+    geometry = geometry.slice(1);   // remove the leading "+"
+    geometry = geometry.split("+"); // x/y screen offset (in pixels)
+    // Keep patches on the visible screen
+    var xpos = Math.min(Number(geometry[0]), window.screen.height - width); 
+    var ypos = Math.min(Number(geometry[1]), window.screen.height - height); 
+    xpos = Math.max(xpos, 0);
+    ypos = Math.max(ypos, 0);
+    var menu_flag;
+    if (menu[cid] == 1) {
+        menu_flag = true;
+    } else {
+        menu_flag = false;
+    }
+    last_loaded = cid;
+    // Not sure why resize and topmost are here-- but we'll pass them on for the time being...
+    patchwin[cid] = nw_create_window(cid, width, height, xpos, ypos, menu_flag,
+        resize[cid], topmost[cid], my_canvas_color, name, dir, dirty_flag, cargs);
+     
+    // initialize variable to reflect that this window has been opened
+    loaded[cid] = 1;
+    //#pdtk_standardkeybindings $cid.c
+}
+
+function canvas_map(name) {
+    console.log("canvas mapping " + name + "...");
+    pdsend(name + " map 1");
+}
+
+exports.canvas_map = canvas_map;
+
+/*    
+    ############iemlib##################
+
+	if { $k12_mode == 0 } {
+
+		# the "Put" menu
+		match_linux_wm [list menu $name.m.put -tearoff $put_tearoff]
+		$name.m add cascade -label Put -menu $name.m.put
+
+		$name.m.put add command -label Object \
+		    -command [concat menu_object $name 0] \
+		    -accelerator [accel_munge "Ctrl+1"]
+
+		$name.m.put add command -label Message \
+		    -command [concat menu_message $name 0] \
+		    -accelerator [accel_munge "Ctrl+2"]
+
+		$name.m.put add command -label Number \
+		    -command [concat menu_floatatom $name 0] \
+		    -accelerator [accel_munge "Ctrl+3"]
+
+		$name.m.put add command -label Symbol \
+		    -command [concat menu_symbolatom $name 0] \
+		    -accelerator [accel_munge "Ctrl+4"]
+
+		$name.m.put add command -label Comment \
+		    -command [concat menu_comment $name 0] \
+		    -accelerator [accel_munge "Ctrl+5"]
+
+		match_linux_wm [list $name.m.put add separator]
+		
+		############iemlib##################
+
+		$name.m.put add command -label Bang \
+		    -command [concat menu_bng $name 0] \
+		    -accelerator [accel_munge "Shift+Ctrl+b"]
+		
+		$name.m.put add command -label Toggle \
+		    -command [concat menu_toggle $name 0] \
+		    -accelerator [accel_munge "Shift+Ctrl+t"]
+		
+		$name.m.put add command -label Number2 \
+		    -command [concat menu_numbox $name 0] \
+		    -accelerator [accel_munge "Shift+Ctrl+n"]
+		
+		$name.m.put add command -label Vslider \
+		    -command [concat menu_vslider $name 0] \
+		    -accelerator [accel_munge "Shift+Ctrl+v"]
+		
+		$name.m.put add command -label Hslider \
+		    -command [concat menu_hslider $name 0] \
+		    -accelerator [accel_munge "Shift+Ctrl+h"]
+		
+		$name.m.put add command -label Vradio \
+		    -command [concat menu_vradio $name 0] \
+		    -accelerator [accel_munge "Shift+Ctrl+d"]
+		
+		$name.m.put add command -label Hradio \
+		    -command [concat menu_hradio $name 0] \
+		    -accelerator [accel_munge "Shift+Ctrl+i"]
+		
+		$name.m.put add command -label VU \
+		    -command [concat menu_vumeter $name 0] \
+		    -accelerator [accel_munge "Shift+Ctrl+u"]
+		
+		$name.m.put add command -label Canvas \
+		    -command [concat menu_mycnv $name 0] \
+		    -accelerator [accel_munge "Shift+Ctrl+c"]
+
+		############iemlib##################
+		
+		match_linux_wm [list $name.m.put add separator]
+		
+		$name.m.put add command -label Graph \
+		    -command [concat menu_graph $name] 
+
+		$name.m.put add command -label Array \
+		    -command [concat menu_array $name] 
+
+		# the find menu
+		# Apple, Microsoft, and others put find functions in the Edit menu.
+		# But in order to move these items to the Edit menu, the Find menu
+		# handling needs to be dealt with, including this line in g_canvas.c:
+		#         sys_vgui(".mbar.find delete %d\n", i);
+		# <hans@at.or.at>
+		#match_linux_wm [list menu $name.m.find -tearoff $put_tearoff]
+		#$name.m add cascade -label Find -menu $name.m.find
+		#
+		#$name.m.find add command -label {Find...} \
+		#    -accelerator [accel_munge "Ctrl+f"] \
+		#    -command [concat menu_findobject $name] 
+		#$name.m.find add command -label {Find Again} \
+		#    -accelerator [accel_munge "Ctrl+g"] \
+		#    -command [concat menu_findagain $name] 
+		#$name.m.find add command -label {Find last error} \
+		#    -command [concat menu_finderror] 
+		
+		# the window menu
+		match_linux_wm [list menu $name.m.windows -postcommand \
+			[concat menu_fixwindowmenu $name] -tearoff $pd_tearoff]
+
+		if {$pd_nt == 2} {
+			$name.m.windows add command -label {Minimize} \
+				-command "menu_minimize $name" -accelerator [accel_munge "Ctrl+m"]
+			$name.m.windows add command -label {Zoom} -command "menu_zoom $name"
+		} else {
+			$name.m.windows add command -label "Next Window" -command {menu_raisenextwindow} \
+				-accelerator "Ctrl+PageDown"
+			$name.m.windows add command -label "Previous Window" -command {menu_raisepreviouswindow} \
+				-accelerator "Ctrl+PageUp"
+		}
+		match_linux_wm [list $name.m.windows add separator]
+		$name.m.windows add command -label {parent window}\
+		    -command [concat menu_windowparent $name] 
+		$name.m.windows add command -label {Pd & Console} -command menu_raise_console \
+			-accelerator [accel_munge "Ctrl+;"]
+		match_linux_wm [list $name.m.windows add separator]
+
+		# the audio menu
+		match_linux_wm [list menu $name.m.audio -tearoff $pd_tearoff]
+
+		if {$pd_nt != 2} {
+		    $name.m add cascade -label Windows -menu $name.m.windows
+		    $name.m add cascade -label Media -menu $name.m.audio
+		} else {
+		    $name.m add cascade -label Media -menu $name.m.audio
+		    $name.m add cascade -label Window -menu $name.m.windows
+		    # the MacOS X app menu
+		    menu $name.m.apple -tearoff $pd_tearoff
+		    $name.m add cascade -label "Apple" -menu $name.m.apple 
+		}
+
+		# the "Help" menu
+
+		match_linux_wm [list menu $name.m.help -tearoff $pd_tearoff]
+		$name.m add cascade -label Help -menu $name.m.help
+
+		menu_addstd $name.m
+	}
+
+    # the popup menu
+	match_linux_wm [list menu $name.popup -tearoff false]
+	if { $k12_mode == 0 } {
+		$name.popup add command -label {Properties} \
+		    -command [concat popup_action $name 0]
+		$name.popup add command -label {Open} \
+		    -command [concat popup_action $name 1]
+		$name.popup add command -label {Help} \
+		    -command [concat popup_action $name 2]
+		match_linux_wm [list $name.popup add separator]
+		$name.popup add command -label {To Front} \
+		    -command [concat popup_action $name 3]
+		$name.popup add command -label {To Back} \
+		    -command [concat popup_action $name 4]
+	} else {
+		$name.popup add command -label {Properties} -state disabled \
+		    -command [concat popup_action $name 0] 
+		$name.popup add command -label {Open} -state disabled \
+		    -command [concat popup_action $name 1]
+		$name.popup add command -label {Help} \
+		    -command [concat popup_action $name 2]
+		match_linux_wm [list $name.popup add separator]
+		$name.popup add command -label {To Front} -state disabled \
+		    -command [concat popup_action $name 3]
+		$name.popup add command -label {To Back} -state disabled \
+		    -command [concat popup_action $name 4]
+	}
+
+    # fix menu font size on Windows with tk scaling = 1
+    if {$pd_nt == 1} {
+        $name.m.file configure -font menuFont
+        $name.m.edit configure -font menuFont
+        $name.m.find configure -font menuFont
+        $name.m.put configure -font menuFont
+        $name.m.windows configure -font menuFont
+        $name.m.audio configure -font menuFont
+        $name.m.help configure -font menuFont
+        $name.popup configure -font menuFont
+    }
+
+    # WM protocol
+    wm protocol $name WM_DELETE_WINDOW [concat menu_close $name]
+
+    # bindings.
+    # this is idiotic -- how do you just sense what mod keys are down and
+    # pass them on? I can't find it anywhere.
+    # Here we encode shift as 1, control 2, alt 4, in agreement
+    # with definitions in g_canvas.c.  The third button gets "8" but we don't
+    # bother with modifiers there.
+    # We don't handle multiple clicks yet.
+
+    bind $name.c <Configure> {pdtk_canvas_getscroll %W}
+	#bind $name.c <Configure> {after 100 pdtk_canvas_getscroll_configure %W}
+    bind $name.c <Button> {pdtk_canvas_click %W %x %y %b 0}
+    bind $name.c <Shift-Button> {pdtk_canvas_click %W %x %y %b 1}
+    bind $name.c <Control-Shift-Button> {pdtk_canvas_click %W %x %y %b 3}
+    # Alt key is called Option on the Mac
+    if {$pd_nt == 2} {
+        bind $name.c <Option-Button> {pdtk_canvas_click %W %x %y %b 4}
+        bind $name.c <Option-Shift-Button> {pdtk_canvas_click %W %x %y %b 5}
+        bind $name.c <Option-Control-Button> {pdtk_canvas_click %W %x %y %b 6}
+        bind $name.c <Mod1-Button> {pdtk_canvas_click %W %x %y %b 6}
+        bind $name.c <Option-Control-Shift-Button> \
+            {pdtk_canvas_click %W %x %y %b 7}
+    } else {
+        bind $name.c <Alt-Button> {pdtk_canvas_click %W %x %y %b 4}
+        bind $name.c <Alt-Shift-Button> {pdtk_canvas_click %W %x %y %b 5}
+        bind $name.c <Alt-Control-Button> {pdtk_canvas_click %W %x %y %b 6}
+        bind $name.c <Alt-Control-Shift-Button> \
+            {pdtk_canvas_click %W %x %y %b 7}
+    }
+    # button 2 is the right button on Mac; on other platforms it's button 3.
+    if {$pd_nt == 2} {
+        bind $name.c <Button-2> {pdtk_canvas_rightclick %W %x %y %b}
+        bind $name.c <Control-Button> {pdtk_canvas_rightclick %W %x %y %b}
+    } else {
+        bind $name.c <Button-3> {pdtk_canvas_rightclick %W %x %y %b}
+        bind $name.c <Control-Button> {pdtk_canvas_click %W %x %y %b 2}
+    }
+    #on linux, button 2 "pastes" from the X windows clipboard
+    if {$pd_nt == 0} {
+        bind $name.c <Button-2> {pdtk_canvas_middleclick %W %x %y %b 0;}
+    }
+
+    bind $name.c <ButtonRelease> {pdtk_canvas_mouseup %W %x %y %b}
+    bind $name.c <Control-Key> {pdtk_canvas_ctrlkey %W %K 0}
+    bind $name.c <Control-Shift-Key> {pdtk_canvas_ctrlkey %W %K 1}
+    #    bind $name.c <Mod1-Key> {puts stderr [concat mod1 %W %K %A]}
+    if {$pd_nt == 2} {
+        bind $name.c <Mod1-Key> {pdtk_canvas_ctrlkey %W %K 0}
+        bind $name.c <Mod1-Shift-Key> {pdtk_canvas_ctrlkey %W %K 1}
+        bind $name.c <Mod1-BackSpace> {pdtk_canvas_sendkey %W 1 %K %A 0 1 %t}
+        bind $name.c <Mod1-quoteleft> {menu_raisenextwindow}
+    } else {
+        bind $name.c <Control-Next>   {menu_raisenextwindow}
+        bind $name.c <Control-Prior>  {menu_raisepreviouswindow} ;# needs Tcl/Tk 8.5
+	}
+    bind $name.c <Key> {pdtk_canvas_sendkey %W 1 %K %A 0 1 %t}
+    bind $name.c <Shift-Key> {pdtk_canvas_sendkey %W 1 %K %A 1 1 %t}
+    bind $name.c <KeyRelease> {pdtk_canvas_sendkey %W 0 %K %A 0 1 %t}
+    bind $name.c <Motion> {pdtk_canvas_motion %W %x %y 0}
+    bind $name.c <Shift-Motion> {pdtk_canvas_motion %W %x %y 1}
+    bind $name.c <Control-Motion> {pdtk_canvas_motion %W %x %y 2}
+    bind $name.c <Control-Shift-Motion> {pdtk_canvas_motion %W %x %y 3}
+
+    # canvas bindings ---------------------------------------------------------
+    # just for tooltips right now
+	#$name.c bind all <Enter> "puts stderr {%x %y}"
+    #$name.c bind inlet <Enter> "pdtk_canvas_enteritem %W %x %y inlet %t"
+    #$name.c bind outlet <Enter> "pdtk_canvas_enteritem %W %x %y outlet %t"
+    #$name.c bind text <Enter> "pdtk_canvas_enteritem %W %x %y text %t"
+    #$name.c bind inlet <Leave> "pdtk_canvas_leaveitem %W inlet 0"
+    #$name.c bind outlet <Leave> "pdtk_canvas_leaveitem %W outlet 0"
+    #$name.c bind text <Leave> "pdtk_canvas_leaveitem %W text 0"
+	
+    if {$pd_nt == 2} {
+        bind $name.c <Option-Motion> {pdtk_canvas_motion %W %x %y 4}
+    } else { 
+        bind $name.c <Alt-Motion> {pdtk_canvas_motion %W %x %y 4}
+    }
+    bind $name.c <Map> {pdtk_canvas_map %W}
+    bind $name.c <Unmap> {pdtk_canvas_unmap %W}
+
+    switch $pd_nt { 0 {
+        bind $name.c <Button-4>  "pdtk_canvas_scroll $name.c y -1"
+        bind $name.c <Button-5>  "pdtk_canvas_scroll $name.c y +1"
+        bind $name.c <Shift-Button-4>  "pdtk_canvas_scroll $name.c x -1"
+        bind $name.c <Shift-Button-5>  "pdtk_canvas_scroll $name.c x +1"
+		#if { $k12_mode == 0 } {
+		#    bind $name.c <Control-Button-4>  "pdtk_zoom $name 1"
+		#    bind $name.c <Control-Button-5>  "pdtk_zoom $name -1"
+		#}
+    } default {
+        bind $name.c  <MouseWheel> \
+            "pdtk_canvas_scroll $name.c y \[expr -abs(%D)/%D\]"
+        bind $name.c  <Shift-MouseWheel> \
+            "pdtk_canvas_scroll $name.c x \[expr -abs(%D)/%D\]"
+    }}
+
+    #dnd bindtarget $name.c text/uri-list <Drop> { pdtk_canvas_makeobjs $name %D %x %y }
+    after idle [list dnd bindtarget $name text/uri-list <Drop> { foreach file %D {open_file $file} }]
+
+    #    puts stderr "all done"
+    #   after 1 [concat raise $name]
+    set pdtk_canvas_mouseup_name ""
+
+	bind $name <FocusIn> "menu_fixeditmenu $name"
+	bind $name <FocusOut> "pdtk_canvas_leaveitem $name.c"
+	if { $k12_mode == 1 } { pd [concat $name tooltips 1 \;] }
+    after idle [concat focus $name.c]
+
+	if { $k12_mode == 1 && $k12_saveas_on_new == 1 } {
+		after 1000 [concat pdtk_k12_saveas_on_new $name]
+	}
+
+	set ::scroll_on($name) 0
+	set ::hit_scrollbar($name) 0
+	#set ::scroll_was_cursor($name) 0
+	set ::last_scroll_x($name) 0
+	set ::last_scroll_y($name) 0
+	set ::canvaswidth($name) 0
+	set ::canvasheight($name) 0
+
+
+/*
+	if { $k12_mode == 1 } {
+		# K-12 menu
+
+		match_linux_wm [list frame $name.k12frame]
+		pack $name.k12frame -side left -fill y
+
+		# ---------------------------------- EDIT BUTTON -----------------------------------------
+		match_linux_wm [list frame $name.k12frame.edit -relief flat]
+		if {$editable==1} {
+			match_linux_wm [list button $name.k12frame.edit.b -image i.edit \
+				-command [concat menu_editmode $name]]
+		} else {
+			match_linux_wm [list button $name.k12frame.edit.b -image i.perform \
+				-command [concat menu_editmode $name]]
+		}
+		pack $name.k12frame.edit.b -side left -expand 1 -padx 1 -pady 0
+		setTooltip $name.k12frame.edit.b "Toggle between building and playing an instrument"
+		pdtk_k12panel_standardkeybindings $name.k12frame.edit.b
+		bind $name.k12frame.edit.b <Key> [list pdtk_canvas_sendkey $name.c 1 %K %A 0 1 %t]
+    	bind $name.k12frame.edit.b <KeyRelease> [list pdtk_canvas_sendkey $name.c 0 %K %A 0 1 %t]
+
+		# ---------------------------------- DATA VS SOUND BUTTONS ----------------------------------
+		match_linux_wm [list frame $name.k12frame.datasound -relief flat]
+		match_linux_wm [list button $name.k12frame.datasound.data -text "DATA" -image i.data_on -command [concat pdtk_k12_show_data_icons $name]]
+		match_linux_wm [list button $name.k12frame.datasound.sound -text "SOUND" -image i.sound -command [concat pdtk_k12_show_sound_icons $name]]
+		pack $name.k12frame.datasound.data $name.k12frame.datasound.sound -side left -pady 1 -padx 1 -expand 0
+		setTooltip $name.k12frame.datasound.data "Show DATA objects"
+		setTooltip $name.k12frame.datasound.sound "Show SOUND objects"
+		pdtk_k12panel_standardkeybindings $name.k12frame.datasound.data
+		pdtk_k12panel_standardkeybindings $name.k12frame.datasound.sound
+
+		# ---------------------------------- MESSAGES LABEL -----------------------------------------
+
+		#match_linux_wm [list frame $name.k12frame.msgs -relief flat]
+		#match_linux_wm [list label $name.k12frame.msgs.label -relief flat -text "MESSAGES"]
+		#pack $name.k12frame.msgs.label -fill x -pady 0 -padx 1
+
+		# ---------------------------------- WII -----------------------------------------
+		match_linux_wm [list frame $name.k12frame.wii -relief flat]
+		match_linux_wm [list button $name.k12frame.wii.b_wii_connect -image i.wii_connect \
+			-command [concat put_K12_objects $name wii_connect]]
+		match_linux_wm [list button $name.k12frame.wii.b_wii_buttons -image i.wii_buttons \
+			-command [concat put_K12_objects $name wii_buttons]]
+		match_linux_wm [list button $name.k12frame.wii.b_wii_hit -image i.wii_hit \
+			-command [concat put_K12_objects $name wii_hit]]
+		match_linux_wm [list button $name.k12frame.wii.b_wii_accelerometer -image i.wii_accelerometer \
+			-command [concat put_K12_objects $name wii_accelerometer]]
+		match_linux_wm [list button $name.k12frame.wii.b_wii_speed_xry -image i.wii_speed_xry \
+			-command [concat put_K12_objects $name wii_speed_xry]]
+		pack $name.k12frame.wii.b_wii_connect $name.k12frame.wii.b_wii_buttons $name.k12frame.wii.b_wii_hit $name.k12frame.wii.b_wii_accelerometer $name.k12frame.wii.b_wii_speed_xry -side left -expand 0 -padx 1 -pady 1
+		setTooltip $name.k12frame.wii.b_wii_connect "Wiimote Connect: Use this to connect wiimote to the computer"
+		setTooltip $name.k12frame.wii.b_wii_buttons "Wiimote Buttons: Use this to select which Wiimote button should activate objects connected to this object"
+		setTooltip $name.k12frame.wii.b_wii_hit "Wiimote Hit: Use this to detect when the wiimote has been shaken like a mallet"
+		setTooltip $name.k12frame.wii.b_wii_accelerometer "Wiimote Accelerometer: Use this to monitor Wiimotes acceleration across X, Y, and Z axes"
+		setTooltip $name.k12frame.wii.b_wii_speed_xry "Wiimote Speed X, Roll, Y: Use this to detect how quickly is Wiimote moving across individual axes x, roll, and y (requires motion plus)"
+		pdtk_k12panel_standardkeybindings $name.k12frame.wii.b_wii_connect
+		pdtk_k12panel_standardkeybindings $name.k12frame.wii.b_wii_buttons
+		pdtk_k12panel_standardkeybindings $name.k12frame.wii.b_wii_hit
+		pdtk_k12panel_standardkeybindings $name.k12frame.wii.b_wii_accelerometer
+		pdtk_k12panel_standardkeybindings $name.k12frame.wii.b_wii_speed_xry
+
+		# ---------------------------------- WII2 -----------------------------------------
+		match_linux_wm [list frame $name.k12frame.wii2 -relief flat]
+		match_linux_wm [list button $name.k12frame.wii2.b_wii_speed -image i.wii_speed \
+			-command [concat put_K12_objects $name wii_speed]]
+		match_linux_wm [list button $name.k12frame.wii2.b_wii_nunchuk_buttons -image i.wii_nunchuk_buttons \
+			-command [concat put_K12_objects $name wii_nunchuk_buttons]]
+		match_linux_wm [list button $name.k12frame.wii2.b_wii_nunchuk_hit -image i.wii_nunchuk_hit \
+			-command [concat put_K12_objects $name wii_nunchuk_hit]]
+		match_linux_wm [list button $name.k12frame.wii2.b_wii_nunchuk_accelerometer -image i.wii_nunchuk_accelerometer \
+			-command [concat put_K12_objects $name wii_nunchuk_accelerometer]]
+		match_linux_wm [list button $name.k12frame.wii2.b_wii_nunchuk_stick -image i.wii_nunchuk_stick \
+			-command [concat put_K12_objects $name wii_nunchuk_stick]]
+		pack $name.k12frame.wii2.b_wii_speed $name.k12frame.wii2.b_wii_nunchuk_buttons $name.k12frame.wii2.b_wii_nunchuk_hit $name.k12frame.wii2.b_wii_nunchuk_accelerometer $name.k12frame.wii2.b_wii_nunchuk_stick -side left -expand 0 -padx 1 -pady 1
+		setTooltip $name.k12frame.wii2.b_wii_speed "Wiimote Speed: Use this to detect how quickly is Wiimote moving (requires motion plus)"
+		setTooltip $name.k12frame.wii2.b_wii_nunchuk_buttons "Wiimote Nunchuk Buttons: Use this to select which Nunchuk button should activate objects connected to this object (requires nunchuk extension)"
+		setTooltip $name.k12frame.wii2.b_wii_nunchuk_hit "Wiimote Nunchuk Hit: Use this to detect when the wiimote has been shaken like a mallet (requires nunchuk extension)"
+		setTooltip $name.k12frame.wii2.b_wii_nunchuk_accelerometer "Wiimote Nunchuk Accelerometer: Use this to monitor Nunchuk acceleration across X, Y, and Z axes (requires nunchuk extension)"
+		setTooltip $name.k12frame.wii2.b_wii_nunchuk_stick "Wiimote Nunchuk stick: Use this to monitor Nunchuk stick motion across X and Y axes (requires nunchuk extension)"
+		pdtk_k12panel_standardkeybindings $name.k12frame.wii2.b_wii_speed
+		pdtk_k12panel_standardkeybindings $name.k12frame.wii2.b_wii_nunchuk_buttons
+		pdtk_k12panel_standardkeybindings $name.k12frame.wii2.b_wii_nunchuk_hit
+		pdtk_k12panel_standardkeybindings $name.k12frame.wii2.b_wii_nunchuk_accelerometer
+		pdtk_k12panel_standardkeybindings $name.k12frame.wii2.b_wii_nunchuk_stick
+
+		# ---------------------------------- ARDUINO -----------------------------------------
+		match_linux_wm [list frame $name.k12frame.arduino -relief flat]
+		match_linux_wm [list button $name.k12frame.arduino.b_sarcduino -image i.sarcduino \
+			-command [concat put_K12_objects $name sarcduino_connect]]
+		match_linux_wm [list button $name.k12frame.arduino.b_sarcduino_digital -image i.sarcduino_digital \
+			-command [concat put_K12_objects $name sarcduino_digital]]
+		match_linux_wm [list button $name.k12frame.arduino.b_sarcduino_analog -image i.sarcduino_analog \
+			-command [concat put_K12_objects $name sarcduino_analog]]
+		match_linux_wm [list button $name.k12frame.arduino.b_sarcduino_hit -image i.sarcduino_hit \
+			-command [concat put_K12_objects $name sarcduino_hit]]
+		match_linux_wm [list button $name.k12frame.arduino.b_sarcduino_piezo -image i.sarcduino_piezo \
+			-command [concat put_K12_objects $name sarcduino_piezo]]
+		pack $name.k12frame.arduino.b_sarcduino $name.k12frame.arduino.b_sarcduino_digital $name.k12frame.arduino.b_sarcduino_analog $name.k12frame.arduino.b_sarcduino_hit $name.k12frame.arduino.b_sarcduino_piezo -side left -expand 0 -padx 1 -pady 1
+		setTooltip $name.k12frame.arduino.b_sarcduino "Arduino Connect: Use this to arduino to the computer"
+		setTooltip $name.k12frame.arduino.b_sarcduino_digital "Arduino Digital: Use this to detect on/off states of a digital sensor"
+		setTooltip $name.k12frame.arduino.b_sarcduino_analog "Arduino Analog: Use this to monitor analog sensor speed"
+		setTooltip $name.k12frame.arduino.b_sarcduino_hit "Arduino Hit: Use this to detect when the arduino analog sensor data has rapidly changed"
+		setTooltip $name.k12frame.arduino.b_sarcduino_piezo "Arduino Piezo: Use this to analyze data coming from a piezo microphone sensor"
+		pdtk_k12panel_standardkeybindings $name.k12frame.arduino.b_sarcduino
+		pdtk_k12panel_standardkeybindings $name.k12frame.arduino.b_sarcduino_digital
+		pdtk_k12panel_standardkeybindings $name.k12frame.arduino.b_sarcduino_analog
+		pdtk_k12panel_standardkeybindings $name.k12frame.arduino.b_sarcduino_hit
+		pdtk_k12panel_standardkeybindings $name.k12frame.arduino.b_sarcduino_piezo
+
+		# ---------------------------------- RPI & MATH ROW 1 -----------------------------------------
+		match_linux_wm [list frame $name.k12frame.math_1 -relief flat]
+		#match_linux_wm [list button $name.k12frame.math_1.b_sarcduino_net -image i.sarcduino_net \
+		#	-command [concat put_K12_objects $name sarcduino_net]]
+		match_linux_wm [list button $name.k12frame.math_1.b_rpi_digital -image i.raspberry_digital \
+			-command [concat put_K12_objects $name raspberrypi_digital]]
+		match_linux_wm [list button $name.k12frame.math_1.b_rpi_analog_out -image i.raspberry_analog_out \
+			-command [concat put_K12_objects $name raspberrypi_analog_out]]
+		match_linux_wm [list button $name.k12frame.math_1.b_rpi_analog_in -image i.raspberry_analog_in \
+			-command [concat put_K12_objects $name raspberrypi_analog_in]]
+		match_linux_wm [list button $name.k12frame.math_1.b_math_number -image i.math_number \
+			-command [concat put_K12_objects $name math_number]]
+		match_linux_wm [list button $name.k12frame.math_1.b_math_netsend -image i.math_netsend \
+			-command [concat put_K12_objects $name math_netsend]]
+		pack $name.k12frame.math_1.b_rpi_digital $name.k12frame.math_1.b_rpi_analog_out $name.k12frame.math_1.b_rpi_analog_in $name.k12frame.math_1.b_math_number $name.k12frame.math_1.b_math_netsend -side left -expand 0 -padx 1 -pady 1
+		#setTooltip $name.k12frame.math_1.b_sarcduino_net "Arduino Net: Use this to retrieve data from arduino devices connected via network"
+		setTooltip $name.k12frame.math_1.b_rpi_digital "RaspberryPi Digital: Use this to read from or write to GPIO pins in digital format"
+		setTooltip $name.k12frame.math_1.b_rpi_analog_out "RaspberryPi Analog Out: Use this to write to GPIO pins in analog format using PWM"
+		setTooltip $name.k12frame.math_1.b_rpi_analog_in "RaspberryPi Analog In: Use this to read from RaspberryPi LOP Shield's analog inputs"
+		setTooltip $name.k12frame.math_1.b_math_number "Number: Use this to assign a value to other objects"
+		setTooltip $name.k12frame.math_1.b_math_netsend "Netsend: Use this to send data over network to another computer"
+		pdtk_k12panel_standardkeybindings $name.k12frame.math_1.b_rpi_digital
+		pdtk_k12panel_standardkeybindings $name.k12frame.math_1.b_rpi_analog_out
+		pdtk_k12panel_standardkeybindings $name.k12frame.math_1.b_rpi_analog_in
+		pdtk_k12panel_standardkeybindings $name.k12frame.math_1.b_math_number
+		pdtk_k12panel_standardkeybindings $name.k12frame.math_1.b_math_netsend
+
+		# ---------------------------------- MATH ROW 2 -----------------------------------------
+		match_linux_wm [list frame $name.k12frame.math_2 -relief flat]
+		match_linux_wm [list button $name.k12frame.math_2.b_math_netreceive -image i.math_netreceive \
+			-command [concat put_K12_objects $name math_netreceive]]
+		match_linux_wm [list button $name.k12frame.math_2.b_math_tag -image i.math_tag \
+			-command [concat put_K12_objects $name math_tag]]
+		match_linux_wm [list button $name.k12frame.math_2.b_math_routebytag -image i.math_routebytag \
+			-command [concat put_K12_objects $name math_routebytag]]
+		match_linux_wm [list button $name.k12frame.math_2.b_math_add -image i.math_add \
+			-command [concat put_K12_objects $name math_add]]
+		match_linux_wm [list button $name.k12frame.math_2.b_math_subtract -image i.math_subtract \
+			-command [concat put_K12_objects $name math_subtract]]
+		pack $name.k12frame.math_2.b_math_netreceive $name.k12frame.math_2.b_math_tag $name.k12frame.math_2.b_math_routebytag $name.k12frame.math_2.b_math_add $name.k12frame.math_2.b_math_subtract -side left -expand 0 -padx 1 -pady 1
+		setTooltip $name.k12frame.math_2.b_math_netreceive "Netreceive: Use this to receive data from another computer over network"
+		setTooltip $name.k12frame.math_2.b_math_tag "Tag: Use this to tag data to be sent over network"
+		setTooltip $name.k12frame.math_2.b_math_routebytag "Route By Tag: Use this to filter incoming network data by tag"
+		setTooltip $name.k12frame.math_2.b_math_add "Add: Use this to add two values"
+		setTooltip $name.k12frame.math_2.b_math_subtract "Subtract: Use this to subtract two values"
+		pdtk_k12panel_standardkeybindings $name.k12frame.math_2.b_math_netreceive
+		pdtk_k12panel_standardkeybindings $name.k12frame.math_2.b_math_tag
+		pdtk_k12panel_standardkeybindings $name.k12frame.math_2.b_math_routebytag
+		pdtk_k12panel_standardkeybindings $name.k12frame.math_2.b_math_add
+		pdtk_k12panel_standardkeybindings $name.k12frame.math_2.b_math_subtract
+
+		# ---------------------------------- MATH ROW 3 -----------------------------------------
+		match_linux_wm [list frame $name.k12frame.math_3 -relief flat]
+		match_linux_wm [list button $name.k12frame.math_3.b_math_multiply -image i.math_multiply \
+			-command [concat put_K12_objects $name math_multiply]]
+		match_linux_wm [list button $name.k12frame.math_3.b_math_divide -image i.math_divide \
+			-command [concat put_K12_objects $name math_divide]]
+		match_linux_wm [list button $name.k12frame.math_3.b_math_random -image i.math_random \
+			-command [concat put_K12_objects $name math_random]]
+		match_linux_wm [list button $name.k12frame.math_3.b_math_average -image i.math_average \
+			-command [concat put_K12_objects $name math_average]]
+		match_linux_wm [list button $name.k12frame.math_3.b_math_scale -image i.math_scale \
+			-command [concat put_K12_objects $name math_scale]]
+		pack $name.k12frame.math_3.b_math_multiply $name.k12frame.math_3.b_math_divide $name.k12frame.math_3.b_math_random $name.k12frame.math_3.b_math_average $name.k12frame.math_3.b_math_scale -side left -expand 0 -padx 1 -pady 1
+		setTooltip $name.k12frame.math_3.b_math_multiply "Multiply: Use this to multiply two values"
+		setTooltip $name.k12frame.math_3.b_math_divide "Divide: Use this to divide two values"
+		setTooltip $name.k12frame.math_3.b_math_random "Random: Use this to generate random numbers"
+		setTooltip $name.k12frame.math_3.b_math_average "Average: Use this to calculate average from a stream of numbers"
+		setTooltip $name.k12frame.math_3.b_math_scale "Scale: Use this to scale incoming values to a new range and direction"
+		pdtk_k12panel_standardkeybindings $name.k12frame.math_3.b_math_multiply
+		pdtk_k12panel_standardkeybindings $name.k12frame.math_3.b_math_divide
+		pdtk_k12panel_standardkeybindings $name.k12frame.math_3.b_math_random
+		pdtk_k12panel_standardkeybindings $name.k12frame.math_3.b_math_average
+		pdtk_k12panel_standardkeybindings $name.k12frame.math_3.b_math_scale
+
+		# ---------------------------------- LOGIC -----------------------------------------
+		match_linux_wm [list frame $name.k12frame.logic -relief flat]
+		match_linux_wm [list button $name.k12frame.logic.b_logic_compare -image i.logic_compare \
+			-command [concat put_K12_objects $name logic_compare]]
+		match_linux_wm [list button $name.k12frame.logic.b_logic_mapper -image i.logic_mapper \
+			-command [concat put_K12_objects $name logic_mapper]]
+		match_linux_wm [list button $name.k12frame.logic.b_logic_metronome -image i.logic_metronome \
+			-command [concat put_K12_objects $name logic_metronome]]
+		match_linux_wm [list button $name.k12frame.logic.b_logic_counter -image i.logic_counter \
+			-command [concat put_K12_objects $name logic_counter]]
+		match_linux_wm [list button $name.k12frame.logic.b_logic_sequencer -image i.logic_sequencer \
+			-command [concat put_K12_objects $name logic_sequencer]]
+		pack $name.k12frame.logic.b_logic_compare $name.k12frame.logic.b_logic_mapper $name.k12frame.logic.b_logic_metronome $name.k12frame.logic.b_logic_counter $name.k12frame.logic.b_logic_sequencer -side left -expand 0 -padx 1 -pady 1
+		setTooltip $name.k12frame.logic.b_logic_compare "Compare: Use this to compare two values"
+		setTooltip $name.k12frame.logic.b_logic_mapper "Mapper: Use this to map one value to two different but related values"
+		setTooltip $name.k12frame.logic.b_logic_metronome "Metronome: Use this to create a steady pulse"
+		setTooltip $name.k12frame.logic.b_logic_counter "Counter: Use this to count events"
+		setTooltip $name.k12frame.logic.b_logic_sequencer "Sequencer: Use this to map values to MIDI pitches"
+		pdtk_k12panel_standardkeybindings $name.k12frame.logic.b_logic_compare
+		pdtk_k12panel_standardkeybindings $name.k12frame.logic.b_logic_mapper
+		pdtk_k12panel_standardkeybindings $name.k12frame.logic.b_logic_metronome
+		pdtk_k12panel_standardkeybindings $name.k12frame.logic.b_logic_counter
+		pdtk_k12panel_standardkeybindings $name.k12frame.logic.b_logic_sequencer
+
+		# ---------------------------------- OTHER -----------------------------------------
+		match_linux_wm [list frame $name.k12frame.other -relief flat]
+		match_linux_wm [list button $name.k12frame.other.b_preset -image i.preset \
+			-command [concat put_K12_objects $name preset]] 
+		match_linux_wm [list button $name.k12frame.other.b_comment -image i.comment \
+			-command [concat menu_comment $name 1]]
+		pack $name.k12frame.other.b_preset $name.k12frame.other.b_comment -side left -expand 0 -padx 1 -pady 1
+		setTooltip $name.k12frame.other.b_preset "Preset: Use this to store and recall up to four different states of your instrument"
+		setTooltip $name.k12frame.other.b_comment "Comment: Use this to post comments inside your patch"
+		pdtk_k12panel_standardkeybindings $name.k12frame.other.b_preset
+		pdtk_k12panel_standardkeybindings $name.k12frame.other.b_comment
+
+		# ---------------------------------- SOUND LABEL -----------------------------------------
+		#match_linux_wm [list frame $name.k12frame.sound -relief flat]
+		#match_linux_wm [list label $name.k12frame.sound.label -relief flat -text "SOUND"]
+		#pack $name.k12frame.sound.label -fill x -pady 0 -padx 1
+
+		# ---------------------------------- SIGNAL ROW 1 -----------------------------------------
+		match_linux_wm [list frame $name.k12frame.signal_1 -relief flat]
+		match_linux_wm [list button $name.k12frame.signal_1.b_signal_microphone -image i.signal_microphone \
+			-command [concat put_K12_objects $name signal_microphone]]
+
+		match_linux_wm [list button $name.k12frame.signal_1.b_signal_netsend -image i.signal_netsend \
+			-command [concat put_K12_objects $name signal_netsend]]
+		match_linux_wm [list button $name.k12frame.signal_1.b_signal_netreceive -image i.signal_netreceive \
+			-command [concat put_K12_objects $name signal_netreceive]]
+
+		match_linux_wm [list button $name.k12frame.signal_1.b_signal_sampler -image i.signal_sampler \
+			-command [concat put_K12_objects $name signal_sampler]]
+		match_linux_wm [list button $name.k12frame.signal_1.b_signal_player -image i.signal_player \
+			-command [concat put_K12_objects $name signal_player]]
+		pack $name.k12frame.signal_1.b_signal_microphone $name.k12frame.signal_1.b_signal_netsend $name.k12frame.signal_1.b_signal_netreceive $name.k12frame.signal_1.b_signal_sampler $name.k12frame.signal_1.b_signal_player -side left -expand 0 -padx 1 -pady 1
+		setTooltip $name.k12frame.signal_1.b_signal_microphone "Microphone: Use this to capture and monitor microphone input"
+		setTooltip $name.k12frame.signal_1.b_signal_netsend "Netsend Sound: Use this to send your sound over network to another computer"
+		setTooltip $name.k12frame.signal_1.b_signal_netreceive "Netreceive Sound: Use this to receive sound from another computer over network"
+		setTooltip $name.k12frame.signal_1.b_signal_sampler "Sampler: Use this to record audio from microphone and play it back in various ways"
+		setTooltip $name.k12frame.signal_1.b_signal_player "Player: Use this to play WAV files in various ways"
+		pdtk_k12panel_standardkeybindings $name.k12frame.signal_1.b_signal_microphone
+		pdtk_k12panel_standardkeybindings $name.k12frame.signal_1.b_signal_netsend
+		pdtk_k12panel_standardkeybindings $name.k12frame.signal_1.b_signal_netreceive
+		pdtk_k12panel_standardkeybindings $name.k12frame.signal_1.b_signal_sampler
+		pdtk_k12panel_standardkeybindings $name.k12frame.signal_1.b_signal_player
+
+		# ---------------------------------- SIGNAL ROW 2 -----------------------------------------
+		match_linux_wm [list frame $name.k12frame.signal_2 -relief flat]
+		match_linux_wm [list button $name.k12frame.signal_2.b_signal_sine -image i.signal_sine \
+			-command [concat put_K12_objects $name signal_sine]]
+		match_linux_wm [list button $name.k12frame.signal_2.b_signal_saw -image i.signal_saw \
+			-command [concat put_K12_objects $name signal_saw]]
+		match_linux_wm [list button $name.k12frame.signal_2.b_signal_square -image i.signal_square \
+			-command [concat put_K12_objects $name signal_square]]
+		match_linux_wm [list button $name.k12frame.signal_2.b_signal_triangle -image i.signal_triangle \
+			-command [concat put_K12_objects $name signal_triangle]]
+		match_linux_wm [list button $name.k12frame.signal_2.b_signal_envelope -image i.signal_envelope \
+			-command [concat put_K12_objects $name signal_envelope]]
+		pack $name.k12frame.signal_2.b_signal_sine $name.k12frame.signal_2.b_signal_saw $name.k12frame.signal_2.b_signal_square $name.k12frame.signal_2.b_signal_triangle $name.k12frame.signal_2.b_signal_envelope -side left -expand 0 -padx 1 -pady 1
+		setTooltip $name.k12frame.signal_2.b_signal_sine "Sine: Use this to generate sine tone"
+		setTooltip $name.k12frame.signal_2.b_signal_saw "Sawtooth: Use this to generate sawtooth tone"
+		setTooltip $name.k12frame.signal_2.b_signal_square "Square: Use this to generate square tone"
+		setTooltip $name.k12frame.signal_2.b_signal_triangle "Triangle: Use this to generate triangle tone"
+		setTooltip $name.k12frame.signal_2.b_signal_envelope "Envelope: Use this to shape sound loudness"
+		pdtk_k12panel_standardkeybindings $name.k12frame.signal_2.b_signal_sine
+		pdtk_k12panel_standardkeybindings $name.k12frame.signal_2.b_signal_saw
+		pdtk_k12panel_standardkeybindings $name.k12frame.signal_2.b_signal_square
+		pdtk_k12panel_standardkeybindings $name.k12frame.signal_2.b_signal_triangle
+		pdtk_k12panel_standardkeybindings $name.k12frame.signal_2.b_signal_envelope
+
+		# ---------------------------------- SIGNAL ROW 3 -----------------------------------------
+		match_linux_wm [list frame $name.k12frame.signal_3 -relief flat]
+		match_linux_wm [list button $name.k12frame.signal_3.b_signal_noise -image i.signal_noise \
+			-command [concat put_K12_objects $name signal_noise]]
+		match_linux_wm [list button $name.k12frame.signal_3.b_signal_pink -image i.signal_pink \
+			-command [concat put_K12_objects $name signal_pink]]
+		match_linux_wm [list button $name.k12frame.signal_3.b_signal_add -image i.signal_add \
+			-command [concat put_K12_objects $name signal_add]]
+		match_linux_wm [list button $name.k12frame.signal_3.b_signal_multiply -image i.signal_multiply \
+			-command [concat put_K12_objects $name signal_multiply]]
+		pack $name.k12frame.signal_3.b_signal_noise $name.k12frame.signal_3.b_signal_pink $name.k12frame.signal_3.b_signal_add $name.k12frame.signal_3.b_signal_multiply -side left -expand 0 -padx 1 -pady 1
+		setTooltip $name.k12frame.signal_3.b_signal_noise "Noise: Use this to generate white (harsh) noise"
+		setTooltip $name.k12frame.signal_3.b_signal_pink "Pink: Use this to generate pink (softer) noise"
+		setTooltip $name.k12frame.signal_3.b_signal_add "Signal Add: Use this to add two sounds (signals)"
+		setTooltip $name.k12frame.signal_3.b_signal_multiply "Signal Multiply: Use this to multiply two sounds (signals)"
+		pdtk_k12panel_standardkeybindings $name.k12frame.signal_3.b_signal_noise 
+		pdtk_k12panel_standardkeybindings $name.k12frame.signal_3.b_signal_pink
+		pdtk_k12panel_standardkeybindings $name.k12frame.signal_3.b_signal_add
+		pdtk_k12panel_standardkeybindings $name.k12frame.signal_3.b_signal_multiply
+
+		# ---------------------------------- INSTRUMENTS -----------------------------------------
+		match_linux_wm [list frame $name.k12frame.instr -relief flat]
+		match_linux_wm [list button $name.k12frame.instr.b_instr_short1 -image i.instr_short1 \
+			-command [concat put_K12_objects $name instr_short1]]
+		match_linux_wm [list button $name.k12frame.instr.b_instr_short2 -image i.instr_short2 \
+			-command [concat put_K12_objects $name instr_short2]]
+		match_linux_wm [list button $name.k12frame.instr.b_instr_sustained1 -image i.instr_sustained1 \
+			-command [concat put_K12_objects $name instr_sustained1]]
+		match_linux_wm [list button $name.k12frame.instr.b_instr_sustained2 -image i.instr_sustained2 \
+			-command [concat put_K12_objects $name instr_sustained2]]
+		pack $name.k12frame.instr.b_instr_short1 $name.k12frame.instr.b_instr_short2 $name.k12frame.instr.b_instr_sustained1 $name.k12frame.instr.b_instr_sustained2 -side left -expand 0 -padx 1 -pady 1
+		setTooltip $name.k12frame.instr.b_instr_short1 "Bass Drum: Use this to produce short sounds like a single bass drum hit"
+		setTooltip $name.k12frame.instr.b_instr_short2 "Snare Drum: Use this to produce short sounds like a single snare drum hit"
+		setTooltip $name.k12frame.instr.b_instr_sustained1 "Air Instrument: Use this to produce long sustained sound like a sound of a woodwind instrument"
+		setTooltip $name.k12frame.instr.b_instr_sustained2 "Brass Instrument: Use this to produce long sustained sound like a sound of brass instrument"
+		pdtk_k12panel_standardkeybindings $name.k12frame.instr.b_instr_short1
+		pdtk_k12panel_standardkeybindings $name.k12frame.instr.b_instr_short2
+		pdtk_k12panel_standardkeybindings $name.k12frame.instr.b_instr_sustained1
+		pdtk_k12panel_standardkeybindings $name.k12frame.instr.b_instr_sustained2
+
+		# ---------------------------------- F/X -----------------------------------------
+		match_linux_wm [list frame $name.k12frame.fx -relief flat]
+		match_linux_wm [list button $name.k12frame.fx.b_fx_filter -image i.fx_filter \
+			-command [concat put_K12_objects $name fx_filter]]
+		match_linux_wm [list button $name.k12frame.fx.b_fx_multitap -image i.fx_multitap \
+			-command [concat put_K12_objects $name fx_multitap]]
+		match_linux_wm [list button $name.k12frame.fx.b_fx_reverb -image i.fx_reverb \
+			-command [concat put_K12_objects $name fx_reverb]]
+		pack $name.k12frame.fx.b_fx_filter $name.k12frame.fx.b_fx_multitap $name.k12frame.fx.b_fx_reverb -side left -expand 0 -padx 1 -pady 1
+		setTooltip $name.k12frame.fx.b_fx_filter "Filter: Use this to make sound appear muffled or brighter"
+		setTooltip $name.k12frame.fx.b_fx_multitap "Echo: Use this to make sound echo"
+		setTooltip $name.k12frame.fx.b_fx_reverb "Reverb: Use this to make sound appear as if it is being played in a large space"
+		pdtk_k12panel_standardkeybindings $name.k12frame.fx.b_fx_filter
+		pdtk_k12panel_standardkeybindings $name.k12frame.fx.b_fx_multitap
+		pdtk_k12panel_standardkeybindings $name.k12frame.fx.b_fx_reverb
+
+		# ---------------------------------- OUTPUT/OTHER -----------------------------------------
+
+		match_linux_wm [list frame $name.k12frame.output -relief flat]
+		match_linux_wm [list button $name.k12frame.output.b_output -image i.output \
+			-command [concat put_K12_objects $name output]] 	
+		pack $name.k12frame.output.b_output -side left -expand 0 -padx 1 -pady 1
+		setTooltip $name.k12frame.output.b_output "Output: Use this to send audio from computer into speakers"
+		pdtk_k12panel_standardkeybindings $name.k12frame.output.b_output
+
+		# ---------------------------------------- NOW PACK THEM ALL -----------------------------------------
+		pack $name.k12frame.edit $name.k12frame.datasound $name.k12frame.wii $name.k12frame.wii2 $name.k12frame.arduino $name.k12frame.math_1 $name.k12frame.math_2 $name.k12frame.math_3 $name.k12frame.logic $name.k12frame.other -side top -expand 0 -fill x
+	}
+
+	if { $k12_mode == 0 } {
+    	wm minsize $name 50 20
+	} else {
+		wm minsize $name 580 407
+	}
+*/
+
+
+
+// net stuff
+var net = require('net');
+
+var HOST = '127.0.0.1';
+var PORT;
+var client;
+
+exports.set_port = function (port_no) {
+    PORT = port_no;
+}
+
+
+function connect () {
+    client = new net.Socket();
+    client.setNoDelay(true);
+    client.connect(PORT, HOST, function() {
+        console.log('CONNECTED TO: ' + HOST + ':' + PORT);
+        // Write a test message to the socket as soon as the client is connected,
+        //the server will receive it as message from the client. This can be removed
+        // once it's obvious it works...
+        client.write('I am Chuck Norris!;');
+    });
+}
+
+exports.connect = connect;
+
+// Add a 'data' event handler for the client socket
+// data parameter is what the server sent to this socket
+
+// Pd can send us different types of data:
+// 1) The old style tcl commands with "\n" at end (or "\\\n" for continuation)
+// 2) new style commands: "nw selector { param1: 'value', param2: 42, etc. };\n"
+// 3) raw javascript (not implemented yet)
+// Below we separate the wheat from chaff, eval'ing the new commands and just
+// printing the old ones in blue to the console
+
+// This wheat/chaff setup has the side-effect of stripping all newline
+// characters from pd->gui messages.  Because of this a newline is added in
+// gui_post.  Once all the old tcl messages are removed we can parse solely
+// on ";", and the side effect can be removed.
+
+// can be removed.
+
+var nextCmd = ""; // Build up a command across lines (or buffers)
+
+function init_socket_events () {
+	client.on('data', function(data) {
+	//    console.log('DATA: ' + data);
+	      var dataStr = data.toString('utf-8');
+//	      console.log("The whole buffer is " + dataStr);
+	      var arr = dataStr.split("\n");
+	      var arrLen = arr.length;
+	      var cmdHeader = 0;
+	      for (var i = 0; i < arrLen; i++) {
+		  var prefix = arr[i].substring(0, 2);
+		  if (prefix == 'nw' || prefix == 'nn') {
+		      nextCmd = arr[i].substring(3);
+//	              console.log("nextCmd is " + nextCmd);
+		      cmdHeader = 1;
+		  } else if (cmdHeader) {
+		      nextCmd += arr[i];
+	//              console.log("2nd part of cmd is " + arr[i]);
+		  } else {
+//		      gui_post(arr[i], "blue");
+		  }
+		  // check if we end with a semicolon followed by a newline
+		  if (nextCmd.slice(-1) === ";" && nextCmd.slice(-2) !== '\\') {
+// console.log("raw cmd is: " + nextCmd);
+		      nextCmd = nextCmd.replace(/\n/g, "\\n");
+//		      nextCmd = nextCmd.replace(/'/g, "\\\'");
+		      var selector = nextCmd.slice(0, nextCmd.indexOf(" "));
+		      var args = nextCmd.slice(selector.length + 1, -1);
+//                      console.log(selector + '(' + args + ');');
+                      eval(selector + '(' + args + ');');
+		      nextCmd = "";
+		      cmdHeader = 0;
+		  }
+
+	      }
+	    // Close the client socket completely
+	//    client.destroy();
+	//    console.log('Connection closed');
+
+	 });
+
+	// Add a 'close' event handler for the client socket
+	 client.on('close', function() {
+	     //console.log('Connection closed');
+             //client.destroy();
+             nw_app_quit(); // set a timeout here if you need to debug
+	 });
+}
+
+exports.init_socket_events = init_socket_events;
+
+// Send commands to Pd
+function pdsend(string) {
+    client.write(string + ';');
+    // for now, let's reprint the outgoing string to the pdwindow
+//    gui_post(string + ';', "red");
+}
+
+exports.pdsend = pdsend;
+
+// Send a ping message back to Pd
+function gui_ping() {
+    pdsend("pd ping");
+}
+
+// Send a message to Pd to ping the "watchdog", which is a program
+// that supervises Pd when run with -rt flag on some OSes
+function gui_ping_watchdog() {
+    pdsend("pd watchdog");
+}
+
+// Schedule watchdog pings for the life of the GUI
+function gui_watchdog() {
+    setInterval(gui_ping_watchdog, 2000);
+}
+
+// Text drawing stuff
+
+// Here's the main API, structured to make an easier (inital) transition
+// from tcl/tk to javascript
+
+// Gobj container, so that all drawn items are contained in a <g> which
+// handles displacing (and in the future, possibly clicks and other events)
+function get_gobj(cid, object) {
+    return patchwin[cid].window.document.getElementById(object + 'gobj');
+}
+
+// Convenience function to get a drawn item of gobj
+function get_item(cid,item_id) {
+    return patchwin[cid].window.document.getElementById(item_id);
+}
+
+// Similar to [canvas create] in tk
+function create_item(cid,type,args) {
+    var item = patchwin[cid].window.document.createElementNS('http://www.w3.org/2000/svg', type);
+    if (args !== null) {
+        configure_item(item, args);
+    }
+    return item;
+}
+
+// Similar to [canvas itemconfigure], without the need for a reference to the canvas
+function configure_item(item, attributes) {
+    // draw_vis from g_template sends attributes as a ['attr1',val1, 'attr2', val2, etc.] array,
+    // so we check for that here
+    if (Array.isArray(attributes)) {
+        // we should check to make sure length is even here...
+        for (var i = 0; i < attributes.length; i+=2) {
+            item.setAttributeNS(null, attributes[i], attributes[i+1]); 
+        }
+    } else {
+        for (var attr in attributes) {
+            if (attributes.hasOwnProperty(attr)) {
+                item.setAttributeNS(null, attr, attributes[attr]);
+            }
+        }
+    }
+}
+
+// Most of these map either to pd.tk procs, or in some cases Tk canvas subcommands
+function gui_text_create_gobj(cid, tag, xpos, ypos) {
+    var svg = get_item(cid, "patchsvg"); // "patchsvg" is id for the svg in the DOM
+    // Put objects on half-pixels to make them crisp (look in to the difference between
+    // this and the object-rendering 'crispEdges' attribute)
+    xpos += 0.5;
+    ypos += 0.5;
+    var transform_string = 'translate(' + xpos + ',' + ypos + ')';
+    var g = create_item(cid, 'g', {
+            id: tag + 'gobj',
+            transform: transform_string
+    });
+    svg.appendChild(g);
+    return g;
+}
+
+function gui_text_drawborder(cid, tag, isbroken, x1, y1, x2, y2) {
+    var g = get_gobj(cid, tag);
+    // isbroken means either
+    //     a) the object couldn't create or
+    //     b) the box is empty
+    var rect = create_item(cid, 'rect', {
+        width: x2 - x1,
+        height: y2 - y1,
+        stroke: 'black',
+        fill: 'none',
+        'shape-rendering': 'optimizeSpeed',
+        id: tag + 'border',
+        class: (isbroken ? 'broken_border' : '')
+    });
+    g.appendChild(rect);
+}
+
+function gui_canvas_drawio(cid, parenttag, tag, x1, y1, x2, y2, basex, basey) {
+    var g = get_gobj(cid, parenttag);
+    // probably need to add an id for xlets below
+    var rect = create_item(cid, 'rect', {
+        width: x2 - x1,
+        height: y2 - y1,
+        x: x1 - basex,
+        y: y1 - basey,
+        class: 'xlet'
+    });
+    g.appendChild(rect);
+}
+
+function gui_message_drawborder(cid,tag,width,height) {
+    var g = get_gobj(cid, tag);
+    var p_array = [0,0,
+                   width+4, 0,
+                   width, 4,
+                   width, height-4,
+                   width+4, height,
+                   0, height,
+                   0, 0];
+    var polygon = create_item(cid, 'polygon', {
+        points: p_array.join(),
+        fill: 'none',
+        stroke: 'black',
+        'stroke-width': 1,
+        id: tag + 'border'
+    });
+    g.appendChild(polygon);
+}
+
+function gui_message_flash(cid, tag, state) {
+    var b = get_item(cid, tag + 'border');
+    var w;
+    if (state != 0) { w = 4; } else { w = 1; }
+    configure_item(b, { 'stroke-width': w });
+}
+
+function gui_message_redraw_border(cid,tag,p1,p2,p3,p4,p5,p6,p7,p8,p9,p10,p11,p12,p13,p14) {
+    var b = get_item(cid, tag + 'border');
+    var p_array = [p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14];
+    configure_item(b, {
+        points: p_array.join(" "),
+    });
+}
+
+
+function gui_atom_drawborder(cid,tag,p1,p2,p3,p4,p5,p6,p7,p8,p9,p10,p11,p12) {
+    var p_array = [p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12];
+    var g = get_gobj(cid, tag);
+    var polygon = create_item(cid, 'polygon', {
+        points: p_array.join(" "),
+        fill: 'none',
+        stroke: 'gray',
+        'stroke-width': 1,
+        id: tag + 'border'
+    });
+    g.appendChild(polygon);
+}
+
+// draw a patch cord
+function gui_canvas_line(cid,tag,p1,p2,p3,p4,p5,p6,p7,p8,p9,p10) {
+    var d_array = ['M', p1 + 0.5, p2 + 0.5,
+                   'Q', p3 + 0.5, p4 + 0.5, p5 + 0.5, p6 + 0.5,
+                   'Q', p7 + 0.5, p8 + 0.5 ,p9 + 0.5, p10 + 0.5];
+    var svg = get_item(cid, "patchsvg");
+    var path = create_item(cid, 'path', {
+        d: d_array.join(" "),
+        fill: 'none',
+        stroke: 'gray',
+        'stroke-width': 1,
+        'shape-rendering': 'optimizeSpeed',
+        id: tag
+    });
+    svg.appendChild(path);
+}
+
+function gui_canvas_select_line(cid, tag) {
+    var line = get_item(cid, tag);
+    if (line !== null) {
+        configure_item(line, { class: 'selected_line' });
+    } else {
+        gui_post("gui_canvas_select_line: can't find line");
+    }
+}
+
+function gui_canvas_deselect_line(cid, tag) {
+    var line = get_item(cid, tag);
+    if (line !== null) {
+        configure_item(line, { class: '' });
+    } else {
+        gui_post("gui_canvas_select_line: can't find line");
+    }
+}
+
+// rename to erase_line (or at least standardize with gobj_erase)
+function gui_canvas_delete_line(cid, tag) {
+    var line = get_item(cid, tag);
+    if (line !== null) {
+        line.parentNode.removeChild(line);
+    } else {
+        gui_post("canvas_delete_line: something is fucked up because the line doesn't exist");
+    }
+}
+
+function gui_canvas_updateline(cid,tag,x1,y1,x2,y2,yoff) {
+//    console.log("cord tag in gui_canvas_updateline is " + tag);
+    var halfx = parseInt((x2 - x1)/2);
+    var halfy = parseInt((y2 - y1)/2);
+    var d_array = ['M',x1,y1,
+                  'Q',x1,y1+yoff,x1+halfx,y1+halfy,
+                  'Q',x2,y2-yoff,x2,y2];
+//    gui_post(d_array.toString());
+    var cord = get_item(cid, tag);
+    configure_item(cord, { d: d_array.join(" ") });
+}
+
+function gui_text_new(canvasname, myname, type, isselected, x, y, text, font) {
+//    gui_post("font is " + font);
+
+    var g = get_gobj(canvasname, myname);
+    var svg_text = create_item(canvasname, 'text', {
+        // x and y are fudge factors. Text on the tk canvas used an anchor
+        // at the top-right corner of the text's bbox.  SVG uses the baseline.
+        // There's probably a programmatic way to do this, but for now-- fudge factors
+        // based on the DejaVu Sans Mono font. :)
+        x: 1,
+        y: 13,
+        'shape-rendering': 'optimizeSpeed',
+        'font-size': font + 'px',
+        id: myname + 'text'
+    });
+
+    // find a way to abstract away the canvas array and the DOM here
+    var text_node = patchwin[canvasname].window.document.createTextNode(text);
+    svg_text.appendChild(text_node);
+    if (g !== null) {
+        g.appendChild(svg_text);
+    } else {
+        gui_post("gui_text_new: can't find parent group");
+    }
+
+    if (isselected) {
+        gui_text_select(canvasname, myname);
+    }
+}
+
+function gui_gobj_erase(cid, tag) {
+    var g = get_gobj(cid, tag);
+    if (g !== null) {
+        g.parentNode.removeChild(g);
+    } else {
+        gui_post("gui_gobj_erase: gobj " + tag + " didn't exist in the first place!");
+    }
+}
+
+function gui_text_set (cid, tag, text) {
+    var svg_text = get_item(cid, tag + 'text');
+    if (svg_text !== null) {
+        svg_text.textContent = text;
+    } else {
+        gui_post("gui_text_set: svg_text doesn't exist!");
+    }
+}
+
+// Not sure whether this is even used anymore
+function gui_text_redraw_border(cid, tag, x1, y1, x2, y2) {
+    var b = get_item(cid, tag + 'border');
+    configure_item(b, {
+        width: x2 - x1,
+        height: y2 - y1
+    });
+}
+
+function gui_text_select(cid, tag) {
+    var g = get_gobj(cid, tag);
+    var b = get_item(cid, tag + 'border');
+    if (g !== null) {
+        configure_item(g, { class: 'selected' });
+    } else {
+        console.log("text_select: something wrong with group tag: " + tag);
+    }
+    if (b !== null) {
+        configure_item(b, { class: 'selected_border' });
+    }
+}
+
+function gui_text_deselect(cid, tag) {
+    gui_post("deselecting text with tag..." + tag);
+    var gobj = get_gobj(cid, tag)
+    var border = get_item(cid, tag + 'border');
+    if (gobj !== null) {
+        configure_item(gobj, { class: "" });
+    } else {
+        console.log("text_deselect: something wrong with tag: " + tag + 'gobj');
+    }
+    if (border !== null) {
+        configure_item(border, { class: "" });
+    } else {
+        console.log("text_select: something wrong with tag: " + tag + 'border');
+    }
+}
+
+function gui_text_select_color(cid, tag) {
+// nb: this is handled in css now
+return;
+    var rect = get_item(cid, tag + 'border');
+    if (rect !== null) {
+        configure_item(rect, {
+            stroke: 'blue',
+            'stroke-width': 1,
+            'stroke-dasharray': 'none'
+        });
+    } else {
+        gui_post("select_color: something wrong with tag: " + tag + 'border');
+    }
+}
+
+function gui_canvas_displace_withtag(name, dx, dy) {
+    var pwin = patchwin[name];
+    var ol = pwin.window.document.getElementsByClassName('selected');
+    for (var i = 0; i < ol.length; i++) {
+        var new_tx = dx + ol[i].transform.baseVal.getItem(0).matrix.e; 
+        var new_ty = dy + ol[i].transform.baseVal.getItem(0).matrix.f; 
+        ol[i].setAttributeNS(null, 'transform',
+            'translate(' + new_tx + ',' + new_ty + ')');
+    }
+}
+
+function gui_create_selection_rectangle(cid, x1, y1, x2, y2) {
+    var svg = get_item(cid, "patchsvg");
+    var points_array = [x1 + 0.5, y1 + 0.5,
+                        x2 + 0.5, y1 + 0.5,
+                        x2 + 0.5, y2 + 0.5,
+                        x1 + 0.5, y2 + 0.5
+    ];
+    var rect = create_item(cid, 'polygon', {
+        points: points_array.join(" "),
+        fill: 'none',
+        stroke: 'black',
+        'shape-rendering': 'optimizeSpeed',
+        'stroke-width': 1,
+        id: 'selection_rectangle',
+        display: 'inline' 
+    });
+    svg.appendChild(rect);
+}
+
+function gui_move_selection_rectangle(cid, x1, y1, x2, y2) {
+    var points_array = [x1 + 0.5, y1 + 0.5, x2 + 0.5, y1 + 0.5,
+                  x2 + 0.5, y2 + 0.5, x1 + 0.5, y2 + 0.5];
+    var rect = get_item(cid, 'selection_rectangle');
+    configure_item(rect, { points: points_array });
+}
+
+function gui_hide_selection_rectangle(cid) {
+//    gui_post("hiding selection");
+    var rect = get_item(cid, 'selection_rectangle');
+    rect.parentElement.removeChild(rect);
+}
+
+// iemguis
+
+function gui_create_bng(cid, tag, cx, cy, radius) {
+    var g = get_gobj(cid, tag);
+    var circle = create_item(cid, 'circle', {
+        cx: cx,
+        cy: cy,
+        r: radius,
+        fill: 'none',
+        stroke: 'black',
+        'stroke-width': 1,
+        id: tag + 'button'
+    });
+    g.appendChild(circle);
+}
+
+function gui_bng_update(cid, tag, flashed) {
+    var button = get_item(cid, tag + 'button');
+    var fill = flashed ? 'red' : 'none';
+    configure_item(button, { fill: fill });
+}
+
+function gui_create_toggle(cid, tag, color, width, p1,p2,p3,p4,p5,p6,p7,p8,basex,basey) {
+    var g = get_gobj(cid, tag);
+    var points_array = [p1 - basex, p2 - basey,
+                        p3 - basex, p4 - basey
+    ];
+    var cross1 = create_item(cid, 'polyline', {
+        points: points_array.join(" "),
+        stroke: color,
+        fill: 'none',
+        id: tag + 'cross1',
+        display: 'none',
+        'stroke-width': width
+    });
+
+    points_array = [p5 - basex, p6 - basey,
+                    p7 - basex, p8 - basey
+    ];
+    var cross2 = create_item(cid, 'polyline', {
+        points: points_array.join(" "),
+        stroke: color,
+        fill: 'none',
+        id: tag + 'cross2',
+        display: 'none',
+        'stroke-width': width
+    });
+    g.appendChild(cross1);
+    g.appendChild(cross2);
+}
+
+function gui_toggle_update(cid, tag, state) {
+    var cross1 = get_item(cid, tag + 'cross1');
+    var cross2 = get_item(cid, tag + 'cross2');
+    if (state) {
+        configure_item(cross1, { display: 'inline' });
+        configure_item(cross2, { display: 'inline' });
+    } else {
+        configure_item(cross1, { display: 'none' });
+        configure_item(cross2, { display: 'none' });
+    }
+}
+
+// Todo: send fewer parameters from c
+function gui_create_numbox(cid,tag,bgcolor,p1,p2,p3,p4,p5,p6,p7,p8,p9,p10,basex,basey,half) {
+    // numbox doesn't have a standard iemgui border, so we must create its gobj manually
+    var g = gui_text_create_gobj(cid, tag, basex, basey)
+    var data_array = ['M', p1 - basex, p2 - basey,
+                      'L', p3 - basex, p4 - basey,
+                           p5 - basex, p6 - basey,
+                           p7 - basex, p8 - basey,
+                           p9 - basex, p10 - basey,
+                      'z',
+                      'L', basex - basex, basey - basey,
+                           half, half,
+                           p9 - basex, p10 - basey];
+    var border = create_item(cid, 'path', {
+        d: data_array.join(" "),
+        fill: bgcolor,
+        stroke: 'black',
+        'stroke-width': 1,
+        id: (tag + 'border')
+    });
+    g.appendChild(border);
+}
+
+function gui_numbox_drawtext(cid,tag,text,color,xpos,ypos,basex,basey) {
+    var g = get_gobj(cid, tag);
+    var svg_text = create_item(cid, 'text', {
+        x: xpos - basex,
+        y: ypos - basey + 5,
+        'font-size': font,
+        id: tag + 'text'
+    });
+
+    var text_node = patchwin[cid].window.document.createTextNode(text);
+    svg_text.appendChild(text_node);
+    g.appendChild(svg_text);
+}
+
+function gui_create_slider(cid,tag,color,p1,p2,p3,p4,basex, basey) {
+    var g = get_gobj(cid, tag);
+    var indicator = create_item(cid, 'line', {
+        x1: p1 - basex,
+        y1: p2 - basey,
+        x2: p3 - basex,
+        y2: p4 - basey,
+        stroke: color,
+        'stroke-width': 3,
+        fill: 'none',
+        id: tag + 'indicator'
+    });
+    g.appendChild(indicator);
+
+}
+
+function gui_slider_update(cid,tag,p1,p2,p3,p4,basex,basey) {
+    var indicator = get_item(cid, tag + 'indicator');
+    configure_item(indicator, {
+        x1: p1 - basex,
+        y1: p2 - basey,
+        x2: p3 - basex,
+        y2: p4 - basey
+    });
+}
+
+function gui_create_radio(cid,tag,p1,p2,p3,p4,i,basex,basey) {
+    var g = get_gobj(cid, tag);
+    var cell = create_item(cid, 'line', {
+        x1: p1 - basex,
+        y1: p2 - basey,
+        x2: p3 - basex,
+        y2: p4 - basey,
+        // stroke is just black for now
+        stroke: 'black',
+        'stroke-width': 1,
+        fill: 'none',
+        id: tag + 'cell_' + i
+    });
+    g.appendChild(cell);
+}
+
+function gui_create_radio_buttons(cid,tag,color,p1,p2,p3,p4,basex,basey,i,state) {
+    var g = get_gobj(cid, tag);
+    var b = create_item(cid, 'rect', {
+        x: p1 - basex,
+        y: p2 -basey,
+        width: p3 - p1,
+        height: p4 - p2,
+        stroke: color,
+        fill: color,
+        id: tag + 'button_' + i,
+        display: state ? 'inline' : 'none'
+    });
+    g.appendChild(b);
+}
+
+function gui_radio_update(cid,tag,prev,next) {
+    var prev = get_item(cid, tag + 'button_' + prev);
+    var next = get_item(cid, tag + 'button_' + next);
+    configure_item(prev, { display: 'none' });
+    configure_item(next, { display: 'inline' });
+}
+
+function gui_create_vumeter_text(cid,tag,color,xpos,ypos,text,index,basex,basey) {
+    var g = get_gobj(cid, tag);
+    var svg_text = create_item(cid, 'text', {
+        x: xpos - basex,
+        y: ypos - basey,
+        //  font-size: font);
+        id: tag + 'text_' + index
+    });
+
+    var text_node = patchwin[cid].window.document.createTextNode(text);
+    svg_text.appendChild(text_node);
+    g.appendChild(svg_text);
+}
+
+function gui_create_vumeter_steps(cid,tag,color,p1,p2,p3,p4,width,index,basex,basey) {
+    var g = get_gobj(cid, tag);
+    var l = create_item(cid, 'line', {
+        x1: p1 - basex,
+        y1: p2 - basey,
+        x2: p3 - basex,
+        y2: p4 - basey,
+        stroke: color,
+        'stroke-width': width,
+        'id': tag + 'led_' + i
+    });
+    g.appendChild(l);
+}
+
+function gui_create_vumeter_rect(cid,tag,color,p1,p2,p3,p4,basex,basey) {
+    var g = get_gobj(cid, tag);
+    var rect = create_item(cid, 'rect', {
+        x: p1 - basex,
+        y: p2 - basey,
+        width: p3 - p1,
+        height: p4 + 1 - p2,
+        stroke: color,
+        fill: color,
+        id: tag + 'rect'
+    });
+    g.appendChild(rect);
+}
+
+function gui_create_vumeter_peak(cid,tag,color,p1,p2,p3,p4,width,basex,basey) {
+    var g = get_gobj(cid, tag);
+    var line = create_item(cid, 'line', {
+        x1: p1 - basex,
+        y1: p2 - basey,
+        x2: p3 - basex,
+        y2: p4 - basey,
+        stroke: color,
+        'stroke-width': width,
+        id: tag + 'peak'
+    });
+    g.appendChild(line);
+}
+
+// change tag from "rect" to "rms"
+function gui_vumeter_update_rms(cid,tag,p1,p2,p3,p4,basex,basey) {
+    rect = get_item(cid, tag + 'rect');
+    configure_item(rect, {
+        x: p1 - basex,
+        y: p2 - basey,
+        width: p3 - p1,
+        height: p4 - p2 + 1
+    });
+}
+
+function gui_vumeter_update_peak(cid,tag,color,p1,p2,p3,p4,basex,basey) {
+    line = get_item(cid, tag + 'peak');
+    configure_item(line, {
+        x1: p1 - basex,
+        y1: p2 - basey,
+        x2: p3 - basex,
+        y2: p4 - basey,
+        stroke: color
+    });
+}
+
+// Think about merging with gui_text_drawborder
+function gui_iemgui_drawborder(cid, tag, bgcolor, x1, y1, x2, y2) {
+    var g = get_gobj(cid, tag);
+    var rect = create_item(cid, 'rect', {
+        width: x2 - x1,
+        height: y2 - y1,
+        fill: bgcolor,
+        stroke: 'black',
+        'shape-rendering': 'optimizeSpeed',
+        'stroke-width': 1,
+        id: tag + 'border'
+    });
+    g.appendChild(rect);
+}
+
+
+function gui_create_mycanvas(cid,tag,color,x1,y1,x2_vis,y2_vis,x2,y2) {
+    var rect_vis = create_item(cid,'rect', {
+        width: x2_vis - x1,
+        height: y2_vis - y1,
+        fill: color,
+        stroke: color
+        }
+    );
+
+    // we use a drag_handle-- unlike a 'border' it takes
+    // the same color as the visible rectangle when deselected
+    var rect = create_item(cid,'rect', {
+        width: x2 - x1,
+        height: y2 - y1,
+        fill: color,
+        stroke: color,
+        id: tag + 'drag_handle'
+        }
+    );
+    var g = get_gobj(cid,tag);
+    g.appendChild(rect_vis);
+    g.appendChild(rect);
+}
+
+function gui_mycanvas_select_color(cid,tag,color) {
+    item = get_item(cid,tag + 'drag_handle');
+    configure_item(item, {stroke: color});
+}
+
+// This should be merged with gui_create_gobj somehow
+function gui_create_scalar(cid, tag, isselected, t1, t2, t3, t4, t5, t6) {
+    var svg = get_item(cid, "patchsvg"); // "patchsvg" is id for the svg in the DOM
+    // Normally put objects on half-pixels to make them crisp, but if we create a
+    // scalar in an object box we already did that. This unfortunately creates a 0.5
+    // pix discrepancy between scalars created in object boxes and ones created with
+    // [append].  Think about just using shape-rendering value of 'crispEdges' in the
+    // places where it matters...
+    t5 += 0.5;
+    t6 += 0.5;
+    var matrix = [t1,t2,t3,t4,t5,t6];
+    var transform_string = 'matrix(' + matrix.join() + ')';
+    var g = create_item(cid, 'g', {
+            id: tag + 'gobj',
+            transform: transform_string,
+            class: (isselected ? 'selected' : '')
+    });
+    // Let's make a selection rect... but we can't make it
+    // a child of the gobj group because the getrect fn gives
+    // us a bbox in the canvas coord system
+    var selection_rect = create_item(cid, 'rect', {
+        id: tag + 'selection_rect',
+        display: 'none',
+        fill: 'none',
+        'pointer-events': 'none'
+    });
+    g.appendChild(selection_rect);
+    svg.appendChild(g);
+    gui_post("made a scalar...");
+    return g;
+}
+
+function gui_scalar_erase(cid, tag) {
+    var g = get_gobj(cid, tag);
+    g.parentNode.removeChild(g);
+    // selection rect...
+//    var sr = get_item(cid, tag + 'selection_rect');
+//    sr.parentNode.removeChild(sr);
+}
+
+function gui_scalar_draw_select_rect(cid, tag, state, x1, y1, x2, y2, basex, basey) {
+    // This is unnecessarily complex-- the select rect is a child of the parent
+    // scalar group, but in the initial Tkpath API the rect was free-standing.  This
+    // means all the coordinate parameters are in the screen position. But we need
+    // the coords relative to the scalar's x/y-- hence we subtract the scalar's basex/basey
+    // from the coords below
+    //gui_post("drawselectrect: " + x1 + " " + y1 + " " + x2 + " " + y2 + " " + basex + " " + basey);
+    var r = get_item(cid, tag + 'selection_rect');
+    configure_item(r, {
+        display: (state ? 'inline' : 'none'),
+        x: (x1 - basex),
+        y: (y1 - basey),
+        width: x2 - x1,
+        height: y2 - y1,
+        stroke: 'blue',
+    });
+}
+
+function gui_create_scalar_group(cid, tag, parent_tag, attr_array) {
+    var parent = get_item(cid, parent_tag);
+    if (attr_array === undefined) {
+        attr_array = [];
+    }
+    attr_array.push("id", tag);
+    var g = create_item(cid, 'g', attr_array);
+    parent.appendChild(g);
+    gui_post("made an innder scalar group");
+    return g; 
+}
+
+function gui_scalar_configure_gobj(cid, tag, isselected, t1, t2, t3, t4, t5, t6) {
+    var gobj = get_gobj(cid, tag);
+    var matrix = [t1,t2,t3,t4,t5,t6];
+    var transform_string = 'matrix(' + matrix.join() + ')';
+    configure_item(gobj, { transform: transform_string });
+}
+
+function gui_draw_vis(cid, type, coords, attr_array, tag_array) {
+    gui_post("inside gui_draw_vis");
+    for(var i = 0; i < arguments.length; i++) {
+        gui_post("arg1 is " + arguments[i]);
+    } 
+    gui_post("coords is " + coords);
+    gui_post("coords is array: " + Array.isArray(coords));
+    gui_post("arguments[2] is " + arguments[2]);
+    gui_post("type of coords is " + typeof coords);
+    gui_post("type of arguments[2] is " + typeof arguments[2]);
+    gui_post("arguments[2] is array: " + Array.isArray(arguments[2]));
+    var g = get_item(cid, tag_array[0]);
+    if (g !== null) {
+        gui_post("our parent exists.");
+    } else {
+        gui_post("our parent doe not exists.");
+    }
+    //var ca = coords;
+    switch(type) {
+        case 'rect':
+            attr_array.push('x', coords[0]);
+            attr_array.push('y', coords[1]);
+            attr_array.push('width', coords[2]);
+            attr_array.push('height',coords[3]);
+            break;
+        // we originally treated circle as an ellipse because we were manually changing the
+        // aspect ratio using coordinates (with glist_xtopixels/ytopixels). That can probably
+        // be changed now that we're applying that in the parent group transform...
+        case 'circle':
+            type = 'ellipse';
+        case 'ellipse':
+            attr_array.push('cx',coords[0]);
+            attr_array.push('cy',coords[1]);
+            attr_array.push('rx',coords[2]);
+            attr_array.push('ry',coords[3]);
+            break;
+        case 'line':
+            attr_array.push('x1',coords[0]); 
+            attr_array.push('y1',coords[1]);
+            attr_array.push('x2',coords[2]);
+            attr_array.push('y2',coords[3]);
+            break;
+        case 'polyline':
+        case 'polygon':
+            attr_array.push('points',coords.join(" "));
+            break;
+        case 'path':
+            attr_array.push('d',coords.join(" ")); 
+            break;
+    }
+    attr_array.push('id', tag_array[1]);
+    gui_post("create is " + tag_array[1]);
+    var item = create_item(cid, type, attr_array);
+    if (item !== null) {
+        gui_post("we got create.");
+    } else {
+        gui_post("we doe not got creat.");
+    }
+    g.appendChild(item);
+}
+
+function gui_draw_erase_item(cid, tag) {
+    gui_post("baleting... tag is " + tag);
+    var item = get_item(cid, tag);
+    if (item !== null) {
+        item.parentNode.removeChild(item);
+    } else {
+        gui_post("uh oh... gui_draw_erase_item couldn't find the item...");
+    }
+}
+
+// Configure one attr/val pair at a time, received from Pd
+function gui_draw_configure(cid, tag, attr, val) {
+    var item = get_item(cid, tag);
+    var obj = {};
+    if (Array.isArray(val)) {
+        obj[attr] = val.join(" ");
+    } else {
+        // strings or numbers
+        obj[attr] = val;
+    }
+    configure_item(item, obj);
+}
+
+// Configure multiple attr/val pairs (this should be merged with gui_draw_configure at some point
+function gui_draw_configure_all(cid, tag, attr_array) {
+    var item = get_item(cid, tag);
+    configure_item(item, attr_array);
+}
+
+function add_popup(cid, popup) {
+    popup_menu[cid] = popup;    
+}
+
+exports.add_popup = add_popup;
+
+function gui_canvas_popup(cid, xpos, ypos, canprop, canopen, isobject) {
+    gui_post("canvas_popup called... " + JSON.stringify(arguments));
+    // Set the global popup x/y so they can be retrieved by the relevant doc's event handler
+    popup_coords[0] = xpos;
+    popup_coords[1] = ypos;
+    popup_menu[cid].items[0].enabled = canprop;
+    popup_menu[cid].items[1].enabled = canopen;
+
+    // We'll use "isobject" to enable/disable "To Front" and "To Back"
+    //isobject;
+    
+    // Get page coords for top of window, in case we're scrolled
+    var left = patchwin[cid].window.document.body.scrollLeft;
+    var top = patchwin[cid].window.document.body.scrollTop;
+
+    popup_menu[cid].popup(xpos - left, ypos - top);
+}
+
+function popup_action(cid, index) {
+    pdsend(cid + " done-popup " + index + " " + popup_coords.join(" "));
+}
+
+exports.popup_action = popup_action;
+
+
+// Graphs and Arrays
+
+function gui_graph_drawborder(cid, tag, x1, y1, x2, y2) {
+    var svgelem = get_item(cid, 'patchsvg');
+    var b = create_item(cid, 'rect', {
+        x: x1,
+        y: y1,
+        width: x2 - x1,
+        height: y2 - y1,
+        stroke: 'black',
+        fill: 'none',
+        id: tag
+    });
+    svgelem.appendChild(b);
+}
+
+function gui_graph_deleteborder(cid, tag) {
+    var b = get_item(cid, tag);
+    b.parentNode.removeChild(b);
+}
+
+function gui_canvas_drawredrect(cid, x1, y1, x2, y2) {
+    var svgelem = get_item(cid, 'patchsvg');
+    var b = create_item(cid, 'rect', {
+        x: x1,
+        y: y1,
+        width: x2 - x1,
+        height: y2 - y1,
+        stroke: 'red',
+        id: 'GOP'
+    });
+    svgelem.appendChild(b);
+}
+
+function gui_canvas_deleteredrect(cid) {
+    var r = get_item(cid, 'GOP');
+    r.parentNode.removeChild(r);
+}
+
+// Magic Glass (aka Cord Inspector)
+
+// For clarity, this probably shouldn't be a gobj.  Also, it might be easier to
+// make it a div that lives on top of the patchsvg
+function gui_create_cord_inspector(cid) {
+    var g = get_gobj(cid, 'cord_inspector');
+    var ci_rect = create_item(cid, 'rect', { id: 'cord_inspector_rect' });
+    var ci_poly = create_item(cid, 'polygon', { id: 'cord_inspector_polygon' });
+    var ci_text = create_item(cid, 'text', { id: 'cord_inspector_text' });
+    var text_node = patchwin[cid].window.document.createTextNode('');
+    ci_text.appendChild(text_node);
+    g.appendChild(ci_rect);
+    g.appendChild(ci_poly);
+    g.appendChild(ci_text);
+}
+
+function gui_cord_inspector_update(cid, text, basex, basey, bg_size, y1, y2, moved) {
+    var gobj = get_gobj(cid, 'cord_inspector');
+    gobj.setAttributeNS(null, 'transform',
+            'translate(' + (basex + 10.5) + ',' + (basey + 0.5) + ')');
+    gobj.setAttributeNS(null, 'pointer-events', 'none');
+    var rect = get_item(cid, 'cord_inspector_rect');
+    var poly = get_item(cid, 'cord_inspector_polygon');
+    var svg_text = get_item(cid, 'cord_inspector_text');
+    // Lots of fudge factors here, tailored to the current default font size
+    configure_item(rect, {
+        x: 13,
+        y: y1 - basey,
+        width: bg_size - basex,
+        height: y2 - basey + 10,
+        fill: 'none',
+        stroke: 'black'
+    });
+    var polypoints_array = [8,0,13,5,13,-5];
+     configure_item(poly, {
+        points: polypoints_array.join()
+    });
+    configure_item(svg_text, {
+        x: 20,
+        y: 5,
+        fill: 'black'
+    });
+    // set the text
+    svg_text.textContent = text;
+}
+
+function gui_erase_cord_inspector(cid) {
+    var ci = get_gobj(cid, 'cord_inspector');
+    if (ci !== null) {
+        ci.parentNode.removeChild(ci);
+    } else {
+        gui_post("oops, trying to erase cord inspector that doesn't exist!");
+    }
+}
+
+function gui_cord_inspector_flash(cid) {
+    var ct = get_item(cid, 'cord_inspector_text');
+    if (ct !== null) {
+        configure_item(ct, { fill: 'red' });
+    } else {
+        gui_post("gui_cord_inspector_flash: trying to flash a non-existent cord inspector!");
+    }
+}
+
+
+// Window functions
+
+function gui_raise_window(cid) {
+    patchwin[cid].focus();
+}
diff --git a/pd/nw/todo.txt b/pd/nw/todo.txt
new file mode 100644
index 0000000000000000000000000000000000000000..9946e7abf9e596cc2a3c491b25b0ad069eb1cb87
--- /dev/null
+++ b/pd/nw/todo.txt
@@ -0,0 +1,80 @@
+Problems to put off until all (or most) sys_vgui calls are eliminated:
+1) gui-side parser inside -- pdgui.js.  Currently we're splitting on newlines so we can separate
+   gui_vmess from sys_vgui calls.  This makes it very difficult to handle multi-line msg and text
+   boxes.
+2) Semicolons -- currently the parser can't tell the difference between semicolons inside symbols
+   and semicolons that end statements.  Again, this will be easy to solve once we eliminate sys_gui.
+
+HTML5 which may not be standard yet:
+1) mouse.pageX/pageY -- exist in Chromium but maybe not in FF et al.
+2) svg 'overflow' attribute -- probably doesn't work in other browsers
+3) document.body.scrollTop (might be Chromium-specific, not sure)
+3) document.body.scrollLeft (might be Chromium-specific, not sure)
+
+Node-webkit stuff:
+1) popup API
+2) new window API
+3) window menus
+4) (probably) present working directory
+5) command line argv
+
+Everything else:
+* packaging as app, setting correct appname, etc
+* get -unique to work (relied on tcl [send] command)
+* check if patch windows with screenposition (0,0) get stuck underneath Ubuntu/OSX menu.  If so,
+  Node-webkit has a "screen" interface to retrieve the "workable" area of the screen
+* choosing the same directory multiple times (see dialog API page)
+* figure out why there is a "pd_opendir" global var
+* pass k12 mode arg
+* implement recent files (using Pd's prefs loading mechanism instead of reimplementing gui prefs)
+* implement verifyquit
+* save and saveas dialogs aren't defaulting to the present working directory
+* create a loop (maybe every second) to check bbox of patchsvg, and set the height/width to that
+  bbox for the svg.  That should trigger the correct scrollbar behavior.
+* change canvas string to a single int (and investigate any side-effects)
+* destroy (or, more likely, hide) selection rectangle when not needed
+* change gui_text_select to gui_gobj_select
+* make it possible to have the inner cells of hradio change color with the selection, as the border currently does.  Tk just uses the non-hierarchical tags and appends the word "BASE".  (Maybe use an inner <g> to do this.)
+* make mycnv use its own selection logic
+* abstract away appendChild
+* font-size should be set as css property for a class of text, rather than per each gobj in svg
+* remove the "fudge_factor" kludge in g_rtext.c, and handle fonts in a sane manner.  In doing so
+  we must decide whether or not we want to be able to fetch the bounding box of objects from
+  within a Pd patch.  (Currently [canvasinfo] and [objectinfo] are the only objects that can
+  do this, though there might also be)
+* replace things like gui_select_color with css
+* find a better approach to escaping characters for the gui than escape_double_quotes kludge (and handle any other problematic chars)
+* look into using <def> and <use> for scalars.
+* use Atom's selectors for standard OSX behavior (cut, paste, about, etc.)
+* in draw_vis, make circle and ellipse radii attr floats instead of ints
+* should stroke-dasharray values be floats?
+* once garrays are drawn inside a <g> we won't have to individually erase each child of a scalar
+* clean up svg_sendupdate -- put a single gui_vmess at the end
+* svg_sendupdate -- make sure there aren't arbitrary %d's that should actually be %g's
+* svg_sendupdate -- need a gui interface for stuff like stroke-dasharray (and garrays)
+* ibid., for path data, points
+* make pdgui function names more consistent
+* in draw_vis, move tags to front of function call so they don't have to be an ugly array
+* in draw_vis, remove all the coords logic and let svg_togui do that work.  In svg_new,
+  leverage the "d", "points", etc. methods to do the same.
+* remove the draw_vis warning/code about needing 2 points to draw
+* might want to clean up dead windows inside patchwin object (on close)
+* make gui_menu_close filename less hacky (currently includes patch args in the middle) 
+* figure out why -nrt flag causes Pd to bail on the GUI (it wasn't doing this
+  on the amd_64 virtual Jessie machine)
+* make dialogs with [yes/no] instead of [cancel/ok]
+* make a gui_mess interface for the edge cases that don't require args (similar to sys_gui)
+* make sure we're breaking down the socket properly from the GUI side on quit
+* nwworkingdir isn't working with the saveas dialog in pdcanvas.html
+* nwworkingdir only works on the second file open
+* implement the stuff at the end of saveas_callback (recentfiles et al);
+* make an "export" menu command for max patch saving.  That way we can accept a single extension
+  for saving (.pd), and the native save dialog will warn us on potential file overwrites (even when
+  the user neglects to type an extension).
+* draw graph inside gobj group
+* merge gui_text_select and gui_text_deselect
+* in function gui_cord_inspector_update, use parameter "moved" to flash text
+* cord_inspector_flash doesn't seem to be deterministic-- if you move among several cords very fast
+  it will try to flash a non-existent svg item.  (Probably want to either make a permanent item in
+  the pdcanvas.html for it, or hard-code a separate div.)
+* for data structures, change "" to 'none'
diff --git a/pd/src/g_all_guis.c b/pd/src/g_all_guis.c
index 2194b67c86f3e3b106bc220fc9465c46f36514a9..312d1e51c4865840ad682ac87fc9b56cb3a07c02 100644
--- a/pd/src/g_all_guis.c
+++ b/pd/src/g_all_guis.c
@@ -496,7 +496,9 @@ void iemgui_displace_withtag(t_gobj *z, t_glist *glist, int dx, int dy)
 
 void iemgui_select(t_gobj *z, t_glist *glist, int selected)
 {
+    char tagbuf[MAXPDSTRING];
     t_iemgui *x = (t_iemgui *)z;
+    sprintf(tagbuf, "x%lx", (long unsigned int)x);
     t_canvas *canvas=glist_getcanvas(glist);
     if (selected)
         x->x_selected = canvas;
@@ -504,6 +506,7 @@ void iemgui_select(t_gobj *z, t_glist *glist, int selected)
         x->x_selected = NULL;
     sys_vgui(".x%lx.c itemconfigure {x%lx&&border} -stroke %s\n", canvas, x,
         x->x_selected && x->x_glist == canvas ? selection_color : border_color);
+    gui_vmess("gui_text_select_color", "ss", canvas_string(canvas), tagbuf);
     x->x_draw((void *)z, glist, IEM_GUI_DRAW_MODE_SELECT);
     if (selected < 2)
     {
@@ -941,11 +944,19 @@ static void scalehandle_check_and_redraw(t_iemgui *x)
 // IEMGUI refactor (by Mathieu)
 
 void iemgui_tag_selected(t_iemgui *x) {
+    char tagbuf[MAXPDSTRING];
+    sprintf(tagbuf, "x%lx", (long unsigned int)x);
     t_canvas *canvas=glist_getcanvas(x->x_glist);
     if(x->x_selected)
+    {
         sys_vgui(".x%lx.c addtag selected withtag x%lx\n", canvas, x);
+        gui_vmess("gui_text_select", "ss", canvas_string(canvas), tagbuf);
+    }
     else
+    {
         sys_vgui(".x%lx.c dtag x%lx selected\n", canvas, x);
+        gui_vmess("gui_text_deselect", "ss", canvas_string(canvas), tagbuf);
+    }
 }
 
 void iemgui_label_draw_new(t_iemgui *x) {
@@ -1023,12 +1034,18 @@ void iemgui_draw_io(t_iemgui *x, int old_sr_flags)
     //fprintf(stderr,"%lx SND: old_sr_flags=%d SND_FLAG=%d || OUTCOME: OLD_SND_FLAG=%d not_empty=%d\n", (t_int)x, old_sr_flags, IEM_GUI_OLD_SND_FLAG, a, b);
     
     if(a && !b) for (i=0; i<n; i++)
+    {
         sys_vgui(".x%lx.c create prect %d %d %d %d "
                  "-stroke $pd_colors(iemgui_nlet) "
                  "-tags {%so%d x%lx outlet %s}\n",
              canvas, x1+i*k, y2-1, x1+i*k + IOWIDTH, y2,
              iem_get_tag(canvas, x), i, x,
              x->x_selected == x->x_glist ? "iemgui selected" : "iemgui");
+        char tagbuf[MAXPDSTRING];
+        sprintf(tagbuf, "x%lx", (long unsigned int)x);
+        gui_vmess("gui_canvas_drawio", "sssiiiiii", canvas_string(canvas), tagbuf,
+            iem_get_tag(canvas, x), x1+i*k, y2-1, x1+i*k + IOWIDTH, y2, x1, y1);
+    }
     if(!a && b) for (i=0; i<n; i++)
         sys_vgui(".x%lx.c delete %so%d\n", canvas, iem_get_tag(canvas, x), i);
 
@@ -1036,12 +1053,18 @@ void iemgui_draw_io(t_iemgui *x, int old_sr_flags)
     b=x->x_rcv!=s_empty;
     //fprintf(stderr,"%lx RCV: old_sr_flags=%d RCV_FLAG=%d || OUTCOME: OLD_RCV_FLAG=%d not_empty=%d\n", (t_int)x, old_sr_flags, IEM_GUI_OLD_RCV_FLAG, a, b);
     if(a && !b) for (i=0; i<n; i++)
+    {
         sys_vgui(".x%lx.c create prect %d %d %d %d "
                  "-stroke $pd_colors(iemgui_nlet) "
                  "-tags {%si%d x%lx inlet %s}\n",
              canvas, x1+i*k, y1, x1+i*k + IOWIDTH, y1+1,
              iem_get_tag(canvas, x), i, x,
              x->x_selected == x->x_glist ? "iemgui selected" : "iemgui");
+        char tagbuf[MAXPDSTRING];
+        sprintf(tagbuf, "x%lx", (long unsigned int)x);
+        gui_vmess("gui_canvas_drawio", "sssiiiiii", canvas_string(canvas), tagbuf,
+            iem_get_tag(canvas, x), x1+i*k, y1, x1+i*k + IOWIDTH, y1+1, x1, y1);
+    }
     if(!a && b) for (i=0; i<n; i++)
         sys_vgui(".x%lx.c delete %si%d\n", canvas, iem_get_tag(canvas, x), i);
 }
@@ -1061,6 +1084,8 @@ void iemgui_io_draw_move(t_iemgui *x) {
 }
 
 void iemgui_base_draw_new(t_iemgui *x) {
+    char tagbuf[MAXPDSTRING]; 
+    sprintf(tagbuf, "x%lx", (long unsigned int)x);
     t_canvas *canvas=glist_getcanvas(x->x_glist);
     t_class *c = pd_class((t_pd *)x);
     int x1,y1,x2,y2,gr=gop_redraw; gop_redraw=0;
@@ -1070,6 +1095,12 @@ void iemgui_base_draw_new(t_iemgui *x) {
              "-stroke $pd_colors(iemgui_border) -fill #%6.6x "
              "-tags {%lxBASE x%lx text iemgui border}\n",
          canvas, x1,y1,x2,y2, x->x_bcol, x, x);
+    gui_vmess("gui_text_create_gobj", "ssii", canvas_string(canvas), tagbuf,
+        x1, y1);
+    char colorbuf[MAXPDSTRING];
+    sprintf(colorbuf, "#%6.6x", x->x_bcol);
+    gui_vmess("gui_iemgui_drawborder", "sssiiii", canvas_string(canvas), tagbuf,
+        colorbuf, x1, y1, x2, y2);
 }
 
 void iemgui_base_draw_move(t_iemgui *x) {
@@ -1114,6 +1145,9 @@ void iemgui_draw_move(t_iemgui *x) {
 void iemgui_draw_erase(t_iemgui *x) {
     t_canvas *canvas=glist_getcanvas(x->x_glist);
     sys_vgui(".x%lx.c delete x%lx\n", canvas, x);
+    char tagbuf[MAXPDSTRING];
+    sprintf(tagbuf, "x%lx", (long unsigned int)x);
+    gui_vmess("gui_gobj_erase", "ss", canvas_string(canvas), tagbuf);
     scalehandle_draw_erase2(x);
 }
 
diff --git a/pd/src/g_bang.c b/pd/src/g_bang.c
index 4370ebe4b6590c32b1fe6abaaf7bca466344486b..3ca7b2e71265f89cb277c1f8d9ce1fbb4337affb 100644
--- a/pd/src/g_bang.c
+++ b/pd/src/g_bang.c
@@ -24,18 +24,24 @@ static t_class *bng_class;
 
 void bng_draw_update(t_gobj *xgobj, t_glist *glist)
 {
+    char tagbuf[MAXPDSTRING];
     t_bng *x = (t_bng *)xgobj;
+    sprintf(tagbuf, "x%lx", (long unsigned int)&x->x_gui);
     if (x->x_gui.x_changed != x->x_flashed && glist_isvisible(glist))
     {
-        sys_vgui(".x%lx.c itemconfigure %lxBUT -fill #%6.6x\n",
-            glist_getcanvas(glist), x,
-            x->x_flashed?x->x_gui.x_fcol:x->x_gui.x_bcol);
+//        sys_vgui(".x%lx.c itemconfigure %lxBUT -fill #%6.6x\n",
+//            glist_getcanvas(glist), x,
+//            x->x_flashed?x->x_gui.x_fcol:x->x_gui.x_bcol);
+        gui_vmess("gui_bng_update", "ssi",
+            canvas_string(glist_getcanvas(glist)), tagbuf, x->x_flashed);
     }
     x->x_gui.x_changed = x->x_flashed;
 }
 
 void bng_draw_new(t_bng *x, t_glist *glist)
 {
+    char tagbuf[MAXPDSTRING];
+    sprintf(tagbuf, "x%lx", (long unsigned int)&x->x_gui);
     t_canvas *canvas=glist_getcanvas(glist);
     int x1=text_xpix(&x->x_gui.x_obj, glist);
     int y1=text_ypix(&x->x_gui.x_obj, glist);
@@ -49,6 +55,8 @@ void bng_draw_new(t_bng *x, t_glist *glist)
              "-tags {%lxBUT x%lx text iemgui border}\n",
          canvas, cx, cy, cr, x->x_flashed?x->x_gui.x_fcol:x->x_gui.x_bcol,
          x, x);
+    gui_vmess("gui_create_bng", "ssfff", canvas_string(canvas), tagbuf,
+        cx - x1 - 0.5, cy - y1 - 0.5, cr);
 }
 
 void bng_draw_move(t_bng *x, t_glist *glist)
diff --git a/pd/src/g_canvas.c b/pd/src/g_canvas.c
index 4c6d9372d5b28a737313ffd4d0c56d7ac5551160..10a16bc1ea6dd91d221fe9dfe3ee8d0b20f52601 100644
--- a/pd/src/g_canvas.c
+++ b/pd/src/g_canvas.c
@@ -248,6 +248,13 @@ void canvas_makefilename(t_canvas *x, char *file, char *result, int resultsize)
     //fprintf(stderr,"resulting file = <%s>\n", result);          
 }
 
+char *canvas_string(t_canvas *x)
+{
+    static char s[MAXPDSTRING];
+    sprintf(s, ".x%lx", (long unsigned int)x);
+    return s;
+}
+
 void canvas_rename(t_canvas *x, t_symbol *s, t_symbol *dir)
 {
     canvas_unbind(x);
@@ -650,9 +657,32 @@ t_symbol *canvas_makebindsym(t_symbol *s)
     return (gensym(buf));
 }
 
+void canvas_args_to_string(char *namebuf, t_canvas *x)
+{
+    t_canvasenvironment *env = canvas_getenv(x);
+    if (env->ce_argc)
+    {
+        int i;
+        strcpy(namebuf, " (");
+        for (i = 0; i < env->ce_argc; i++)
+        {
+            if (strlen(namebuf) > MAXPDSTRING/2 - 5)
+                break;
+            if (i != 0)
+                strcat(namebuf, " ");
+            atom_string(&env->ce_argv[i], namebuf + strlen(namebuf), 
+                MAXPDSTRING/2);
+        }
+        strcat(namebuf, ")");
+    }
+    else namebuf[0] = 0;
+}
+
 void canvas_reflecttitle(t_canvas *x)
 {
     char namebuf[MAXPDSTRING];
+    canvas_args_to_string(namebuf, x);
+/*
     t_canvasenvironment *env = canvas_getenv(x);
     if (env->ce_argc)
     {
@@ -670,6 +700,7 @@ void canvas_reflecttitle(t_canvas *x)
         strcat(namebuf, ")");
     }
     else namebuf[0] = 0;
+*/
 #ifdef __APPLE__
     sys_vgui("wm attributes .x%lx -modified %d -titlepath {%s/%s}\n",
         x, x->gl_dirty, canvas_getdir(x)->s_name, x->gl_name->s_name);
@@ -681,9 +712,11 @@ void canvas_reflecttitle(t_canvas *x)
     x->gl_isgraph && x->gl_havewindow, x->gl_loading,
     x->gl_dirty);*/
 
-    sys_vgui("wm title .x%lx {%s%c%s - %s}\n", 
-        x, x->gl_name->s_name, (x->gl_dirty? '*' : ' '), namebuf,
-            canvas_getdir(x)->s_name);
+    //sys_vgui("wm title .x%lx {%s%c%s - %s}\n", 
+    //    x, x->gl_name->s_name, (x->gl_dirty? '*' : ' '), namebuf,
+    //        canvas_getdir(x)->s_name);
+    gui_vmess("gui_canvas_set_title", "ssssi", canvas_string(x), x->gl_name->s_name,
+        namebuf, canvas_getdir(x)->s_name, x->gl_dirty);
 //}
 #endif
 }
@@ -753,14 +786,22 @@ void canvas_drawredrect(t_canvas *x, int doit)
     {
         int x1=x->gl_xmargin, y1=x->gl_ymargin;
         int x2=x1+x->gl_pixwidth, y2=y1+x->gl_pixheight;
-        sys_vgui(".x%lx.c create line\
-            %d %d %d %d %d %d %d %d %d %d -fill #ff8080 -tags GOP\n",
-            glist_getcanvas(x), x1, y1, x2, y1, x2, y2, x1, y2, x1, y1);
+        //sys_vgui(".x%lx.c create line "
+        //    "%d %d %d %d %d %d %d %d %d %d -fill #ff8080 -tags GOP\n",
+        //    glist_getcanvas(x), x1, y1, x2, y1, x2, y2, x1, y2, x1, y1);
+        gui_vmess("gui_canvas_drawredrect", "siiii",
+            canvas_string(glist_getcanvas(x)),
+            x1, y1, x2, y2);
         //dpsaha@vt.edu for drawing the GOP_blobs
         if (x->gl_goprect && x->gl_edit)
             canvas_draw_gop_resize_hooks(x);
     }
-    else sys_vgui(".x%lx.c delete GOP\n",  glist_getcanvas(x));
+    else
+    {
+        //sys_vgui(".x%lx.c delete GOP\n",  glist_getcanvas(x));
+        gui_vmess("gui_canvas_deleteredrect", "s",
+            canvas_string(glist_getcanvas(x)));
+    }
 }
 
     /* the window becomes "mapped" (visible and not miniaturized) or
@@ -1008,8 +1049,12 @@ void canvas_eraselinesfor(t_canvas *x, t_text *text)
         {
             if (x->gl_editor)
             {
-                sys_vgui(".x%lx.c delete l%lx\n",
-                    glist_getcanvas(x), oc);
+                //sys_vgui(".x%lx.c delete l%lx\n",
+                //    glist_getcanvas(x), oc);
+                char tagbuf[MAXPDSTRING];
+                sprintf(tagbuf, "l%lx", (long unsigned int)oc);
+                gui_vmess("gui_canvas_delete_line", "ss",
+                    canvas_string(glist_getcanvas(x)), tagbuf);
             }
         }
     }
diff --git a/pd/src/g_canvas.h b/pd/src/g_canvas.h
index 90574b3deb26a43d3af2691081870e2c47a57334..d6bdecee1084aedf77cbde2e2a01661f410a70e3 100644
--- a/pd/src/g_canvas.h
+++ b/pd/src/g_canvas.h
@@ -480,6 +480,7 @@ EXTERN void text_drawborder(t_text *x, t_glist *glist, char *tag,
     int width, int height, int firsttime);
 EXTERN void text_drawborder_withtag(t_text *x, t_glist *glist, char *tag,
     int width, int height, int firsttime);
+EXTERN void text_erase_gobj(t_text *x, t_glist *glist, char *tag);
 EXTERN void text_eraseborder(t_text *x, t_glist *glist, char *tag);
 EXTERN int text_xcoord(t_text *x, t_glist *glist);
 EXTERN int text_ycoord(t_text *x, t_glist *glist);
@@ -561,6 +562,7 @@ EXTERN t_gobj *canvas_findhitbox(t_canvas *x, int xpos, int ypos,
     int *x1p, int *y1p, int *x2p, int *y2p);
 EXTERN int canvas_setdeleting(t_canvas *x, int flag);
 EXTERN int canvas_hasarray(t_canvas *x);
+EXTERN char *canvas_string(t_canvas *x); /* canvas as string to send to gui */
 
 /* ---- for parsing @pd_extra and other sys paths in filenames  --------------------- */
 
diff --git a/pd/src/g_editor.c b/pd/src/g_editor.c
index 58ff2c21c066a76fe8bf8a217d4edd9b1e945899..0bacbb29b44cba71b69cbd3ae80e997077c39150 100644
--- a/pd/src/g_editor.c
+++ b/pd/src/g_editor.c
@@ -430,10 +430,13 @@ void glist_selectline(t_glist *x, t_outconnect *oc, int index1,
         x->gl_editor->e_selectline_index2 = index2;
         x->gl_editor->e_selectline_inno = inno;
         x->gl_editor->e_selectline_tag = oc;
-        sys_vgui(".x%lx.c itemconfigure l%lx -stroke $pd_colors(selection)\n",
-            x, x->gl_editor->e_selectline_tag);
-        sys_vgui(".x%lx.c addtag selected withtag l%lx\n",
-            glist_getcanvas(x), x->gl_editor->e_selectline_tag);
+        //sys_vgui(".x%lx.c itemconfigure l%lx -stroke $pd_colors(selection)\n",
+        //    x, x->gl_editor->e_selectline_tag);
+        //sys_vgui(".x%lx.c addtag selected withtag l%lx\n",
+        //    glist_getcanvas(x), x->gl_editor->e_selectline_tag);
+        char tagbuf[MAXPDSTRING];
+        sprintf(tagbuf, "l%lx", (long unsigned int)oc);
+        gui_vmess("gui_canvas_select_line", "ss", canvas_string(x), tagbuf);
         c_selection = x;
         canvas_draw_gop_resize_hooks(x);
     }
@@ -455,14 +458,18 @@ void glist_deselectline(t_glist *x)
             issignal = 1;
         else
             issignal = 0;
-        sys_vgui(".x%lx.c itemconfigure l%lx -stroke %s\n",
-            x, x->gl_editor->e_selectline_tag,
-            (issignal ?
-                "$pd_colors(signal_cord)" : "$pd_colors(control_cord)"));
-        sys_vgui(".x%lx.c dtag l%lx selected\n",
-            glist_getcanvas(x),
-            glist_getcanvas(x)->gl_editor->e_selectline_tag);
+        //sys_vgui(".x%lx.c itemconfigure l%lx -stroke %s\n",
+        //    x, x->gl_editor->e_selectline_tag,
+        //    (issignal ?
+        //        "$pd_colors(signal_cord)" : "$pd_colors(control_cord)"));
+        //sys_vgui(".x%lx.c dtag l%lx selected\n",
+        //    glist_getcanvas(x),
+        //    glist_getcanvas(x)->gl_editor->e_selectline_tag);
         canvas_draw_gop_resize_hooks(x);
+
+        char tagbuf[MAXPDSTRING];
+        sprintf(tagbuf, "l%lx", (long unsigned int)x->gl_editor->e_selectline_tag);
+        gui_vmess("gui_canvas_deselect_line", "ss", canvas_string(x), tagbuf);
     }    
 }
 
@@ -858,7 +865,11 @@ void canvas_disconnect(t_canvas *x,
         if (srcno == index1 && t.tr_outno == outno &&
             sinkno == index2 && t.tr_inno == inno)
         {
-            sys_vgui(".x%lx.c delete l%lx\n", x, oc);
+            //sys_vgui(".x%lx.c delete l%lx\n", x, oc);
+            char tagbuf[MAXPDSTRING];
+            sprintf(tagbuf, "l%lx", (long unsigned int)oc);
+            gui_vmess("gui_canvas_delete_line", "ss",
+                canvas_string(x), tagbuf);
             // jsarlo
             if(x->gl_editor && x->gl_editor->gl_magic_glass)
             {
@@ -2177,16 +2188,16 @@ void canvas_undo_font(t_canvas *x, void *z, int action)
 /* ------------------------ event handling ------------------------ */
 
 static char *cursorlist[] = {
-    "$cursor_runmode_nothing",
-    "$cursor_runmode_clickme",
-    "$cursor_runmode_thicken",
-    "$cursor_runmode_addpoint",
-    "$cursor_editmode_nothing",
-    "$cursor_editmode_connect",
-    "$cursor_editmode_disconnect",
-    "$cursor_editmode_resize",
-    "$cursor_editmode_resize_bottom_right",
-    "$cursor_scroll"
+    "cursor_runmode_nothing",
+    "cursor_runmode_clickme",
+    "cursor_runmode_thicken",
+    "cursor_runmode_addpoint",
+    "cursor_editmode_nothing",
+    "cursor_editmode_connect",
+    "cursor_editmode_disconnect",
+    "cursor_editmode_resize",
+    "cursor_editmode_resize_bottom_right",
+    "cursor_scroll"
 };
 
 void canvas_setcursor(t_canvas *x, unsigned int cursornum)
@@ -2203,7 +2214,9 @@ void canvas_setcursor(t_canvas *x, unsigned int cursornum)
         }
         if (xwas != x || cursorwas != cursornum)
         {
-            sys_vgui(".x%lx configure -cursor %s\n", x, cursorlist[cursornum]);
+            //sys_vgui(".x%lx configure -cursor %s\n", x, cursorlist[cursornum]);
+            gui_vmess("gui_canvas_cursor", "ss", canvas_string(x),
+                cursorlist[cursornum]);
             xwas = x;
             cursorwas = cursornum;
         }
@@ -2305,8 +2318,10 @@ static void canvas_rightclick(t_canvas *x, int xpos, int ypos, t_gobj *y_sel)
         isobject = 1;
     }
     else isobject = 0;
-    sys_vgui("pdtk_canvas_popup .x%lx %d %d %d %d %d\n",
-        x, xpos, ypos, canprop, canopen, isobject);
+    //sys_vgui("pdtk_canvas_popup .x%lx %d %d %d %d %d\n",
+    //    x, xpos, ypos, canprop, canopen, isobject);
+    gui_vmess("gui_canvas_popup", "siiiii",
+        canvas_string(x), xpos, ypos, canprop, canopen, isobject);
 }
 
 /* ----  editors -- perhaps this and "vis" should go to g_editor.c ------- */
@@ -2380,7 +2395,11 @@ void canvas_destroy_editor(t_glist *x)
     }
 }
 
-void canvas_reflecttitle(t_canvas *x);
+
+// void canvas_reflecttitle(t_canvas *x);
+// This should replace canvas_reflecttitle above
+extern void canvas_args_to_string(char *namebuf, t_canvas *x);
+
 void canvas_map(t_canvas *x, t_floatarg f);
 
     /* we call this when we want the window to become visible, mapped, and
@@ -2394,6 +2413,10 @@ void canvas_map(t_canvas *x, t_floatarg f);
 void canvas_vis(t_canvas *x, t_floatarg f)
 {
     //fprintf(stderr,"canvas_vis .x%lx %f\n", (t_int)x, f);
+    char geobuf[MAXPDSTRING];
+    char argsbuf[MAXPDSTRING];
+    sprintf(geobuf, "+%d+%d",
+        (int)(x->gl_screenx1), (int)(x->gl_screeny1));
 
     t_gobj *g;
     t_int properties;
@@ -2408,21 +2431,38 @@ void canvas_vis(t_canvas *x, t_floatarg f)
         if (x->gl_editor && x->gl_havewindow && glist_isvisible(x))
         {           /* just put us in front */
             //fprintf(stderr,"existing\n");
-            sys_vgui("raise .x%lx\n", x);
-            sys_vgui("focus .x%lx.c\n", x);
-            sys_vgui("wm deiconify .x%lx\n", x);  
+            //sys_vgui("raise .x%lx\n", x);
+            //sys_vgui("focus .x%lx.c\n", x);
+            //sys_vgui("wm deiconify .x%lx\n", x);  
+            gui_vmess("gui_raise_window", "s", canvas_string(x));
         }
         else
         {
             //fprintf(stderr,"new\n");
             canvas_create_editor(x);
-            sys_vgui("pdtk_canvas_new .x%lx %d %d +%d+%d %d\n", x,
+//            sys_vgui("pdtk_canvas_new .x%lx %d %d +%d+%d %d\n", x,
+//                (int)(x->gl_screenx2 - x->gl_screenx1),
+//                (int)(x->gl_screeny2 - x->gl_screeny1),
+//                (int)(x->gl_screenx1), (int)(x->gl_screeny1),
+//                x->gl_edit);
+
+            canvas_args_to_string(argsbuf, x);
+
+            gui_vmess("gui_canvas_new", "siisissis",
+                canvas_string(x),
                 (int)(x->gl_screenx2 - x->gl_screenx1),
                 (int)(x->gl_screeny2 - x->gl_screeny1),
-                (int)(x->gl_screenx1), (int)(x->gl_screeny1),
-                x->gl_edit);
+                geobuf,
+                x->gl_edit,
+                x->gl_name->s_name,
+                canvas_getdir(x)->s_name,
+                x->gl_dirty,
+                argsbuf);
+                
+
+
             sys_vgui("pdtk_canvas_set_font .x%lx %d\n", x, x->gl_font);
-            canvas_reflecttitle(x);
+//            canvas_reflecttitle(x);
             x->gl_havewindow = 1;
 
             // check if this is a subpatch with an array
@@ -2507,7 +2547,7 @@ void canvas_vis(t_canvas *x, t_floatarg f)
         if (glist_isvisible(x))
             canvas_map(x, 0);
         canvas_destroy_editor(x);
-        sys_vgui("destroy .x%lx\n", x);
+        gui_vmess("gui_close_window", "s", canvas_string(x));
         // delete properties windows of objects in the patcher we're closing
         g = x->gl_list;
         while (g)
@@ -3289,6 +3329,9 @@ void canvas_doclick(t_canvas *x, int xpos, int ypos, int which,
                             "$pd_colors(signal_cord_width)" :
                             "$pd_colors(control_cord_width)"),
                         (issignal ? "signal" : "control"));
+                        gui_vmess("gui_canvas_line", "ssiiiiiiiiii",
+                            canvas_string(x), "newcord",
+                            xpos, ypos, xpos, ypos, xpos, ypos, xpos, ypos, xpos, ypos);
                     }   
                     else
                     // jsarlo
@@ -3309,7 +3352,6 @@ void canvas_doclick(t_canvas *x, int xpos, int ypos, int which,
                                      "-strokewidth $highlight_width\n",
                                      x,
                                      x->gl_editor->canvas_cnct_outlet_tag);
-                            
                             //sys_vgui(".x%x.c raise %s\n",
                             //         x,
                             //         x->gl_editor->canvas_cnct_outlet_tag);
@@ -3591,6 +3633,8 @@ void canvas_doclick(t_canvas *x, int xpos, int ypos, int which,
         sys_vgui(".x%lx.c create prect %d %d %d %d -tags x "
                  "-stroke $pd_colors(selection_rectangle)\n",
             x, xpos, ypos, xpos, ypos);
+        gui_vmess("gui_create_selection_rectangle", "siiii", canvas_string(x),
+            xpos, ypos, xpos, ypos);
         x->gl_editor->e_xwas = xpos;
         x->gl_editor->e_ywas = ypos;
         x->gl_editor->e_onmotion = MA_REGION;
@@ -3686,6 +3730,7 @@ void canvas_sort_selection_according_to_location(t_canvas *x)
 void canvas_drawconnection(t_canvas *x, int lx1, int ly1, int lx2, int ly2,
     t_int tag, int issignal)
 {
+    char tagbuf[MAXPDSTRING];
     int ymax = 0;
     int halfx = (lx2 - lx1)/2;
     int halfy = (ly2 - ly1)/2;
@@ -3727,11 +3772,16 @@ void canvas_drawconnection(t_canvas *x, int lx1, int ly1, int lx2, int ly2,
             "$pd_colors(signal_cord_width)" :
             "$pd_colors(control_cord_width)"), 
         tag, (issignal ? "signal" : "control"));
+    sprintf(tagbuf, "l%lx", (long unsigned int)tag);
+    gui_vmess("gui_canvas_line", "ssiiiiiiiiii",
+        canvas_string(x), tagbuf, lx1, ly1, lx1, ly1 + yoff,
+        lx1 + halfx, ly1 + halfy, lx2, ly2 - yoff, lx2, ly2);
 }
 
 void canvas_updateconnection(t_canvas *x, int lx1, int ly1, int lx2, int ly2,
     t_int tag)
 {
+    char cord_tag[MAXPDSTRING];
     if (glist_isvisible(x) && glist_istoplevel(x))
     {
         int ymax = 0;
@@ -3768,6 +3818,11 @@ void canvas_updateconnection(t_canvas *x, int lx1, int ly1, int lx2, int ly2,
                 x, tag, lx1, ly1,
                 lx1, ly1 + yoff, lx1 + halfx, ly1 + halfy,
                 lx2, ly2 - yoff, lx2, ly2);
+            sprintf(cord_tag, "l%lx", (long unsigned int)tag);
+            gui_vmess("gui_canvas_updateline", "ssiiiii", canvas_string(x), cord_tag,
+                lx1, ly1, lx2, ly2, yoff);
+//                lx1, ly1, lx1, ly1 + yoff, lx1 + halfx, ly1 + halfy,
+//                lx2, ly2 - yoff, lx2, ly2);
         }
         else
         {
@@ -3777,6 +3832,10 @@ void canvas_updateconnection(t_canvas *x, int lx1, int ly1, int lx2, int ly2,
                 x, lx1, ly1,
                 lx1, ly1 + yoff, lx1 + halfx, ly1 + halfy,
                 lx2, ly2 - yoff, lx2, ly2);
+            gui_vmess("gui_canvas_updateline", "ssiiiii", canvas_string(x), "newcord",
+                lx1, ly1, lx2, ly2, yoff);
+//                lx1, ly1, lx1, ly1 + yoff, lx1 + halfx, ly1 + halfy, lx2, ly2 - yoff,
+//                lx2, ly2);
         }
     }
 }
@@ -4371,7 +4430,10 @@ void canvas_doconnect(t_canvas *x, int xpos, int ypos, int which, int doit)
     int xwas = x->gl_editor->e_xwas,
         ywas = x->gl_editor->e_ywas;
     if (doit && !glob_shift)
+    {
         sys_vgui(".x%lx.c delete x\n", x);
+        gui_vmess("gui_canvas_delete_line", "ss", canvas_string(x), "newcord");
+    }
     else
     {
         canvas_updateconnection(x, x->gl_editor->e_xwas, x->gl_editor->e_ywas,
@@ -4514,11 +4576,17 @@ static void canvas_doregion(t_canvas *x, int xpos, int ypos, int doit)
         else hiy = x->gl_editor->e_ywas, loy = ypos;
         canvas_selectinrect(x, lox, loy, hix, hiy);
         sys_vgui(".x%lx.c delete x\n", x);
+        gui_vmess("gui_hide_selection_rectangle", "s", canvas_string(x));
         x->gl_editor->e_onmotion = MA_NONE;
     }
-    else sys_vgui(".x%lx.c coords x %d %d %d %d\n",
+    else
+    {
+        sys_vgui(".x%lx.c coords x %d %d %d %d\n",
             x, x->gl_editor->e_xwas,
                 x->gl_editor->e_ywas, xpos, ypos);
+        gui_vmess("gui_move_selection_rectangle", "siiii",
+            canvas_string(x), x->gl_editor->e_xwas, x->gl_editor->e_ywas, xpos, ypos);
+    }
 }
 
 /*
@@ -4744,6 +4812,7 @@ void canvas_displaceselection(t_canvas *x, int dx, int dy)
     if (dx || dy)
     {
         sys_vgui("pdtk_canvas_displace_withtag .x%lx.c %d %d\n", x, dx, dy);
+        gui_vmess("gui_canvas_displace_withtag", "sii", canvas_string(x), dx, dy);
         if (resortin) canvas_resortinlets(x);
         if (resortout) canvas_resortoutlets(x);
         //scrollbar_update(x);
@@ -5254,13 +5323,21 @@ void glob_verifyquit(void *dummy, t_floatarg f)
             if (!glist_istoplevel(g2) && g->gl_env)
             {
                 /* if this is an abstraction */
-                sys_vgui("pdtk_canvas_menuclose .x%lx {.x%lx menuclose 3;}\n",
-                     g2, g2);
+                //sys_vgui("pdtk_canvas_menuclose .x%lx {.x%lx menuclose 3;}\n",
+                //     g2, g2);
+                gui_vmess("gui_canvas_menuclose", "ssi",
+                    canvas_string(g2),
+                    canvas_string(g2),
+                    3);
             }
             else
             {
-                sys_vgui("pdtk_canvas_menuclose .x%lx {.x%lx menuclose 3;}\n",
-                     canvas_getrootfor(g2), g2);
+                //sys_vgui("pdtk_canvas_menuclose .x%lx {.x%lx menuclose 3;}\n",
+                //     canvas_getrootfor(g2), g2);
+                gui_vmess("gui_canvas_menuclose", "ssi",
+                    canvas_string(canvas_getrootfor(g2)),
+                    canvas_string(g2),
+                    3);
             }
             //canvas_vis(g2, 1);
             //sys_vgui("pdtk_canvas_menuclose .x%lx {.x%lx menuclose 3;\n}\n",
@@ -5268,8 +5345,15 @@ void glob_verifyquit(void *dummy, t_floatarg f)
         return;
     }
     if (f == 0 && sys_perf)
-        sys_vgui("pdtk_check . {really quit?} {pd quit;\n} yes\n");
-    else glob_quit(0);
+    {
+        //sys_vgui("pdtk_check . {really quit?} {pd quit;\n} yes\n");
+        gui_vmess("gui_pd_quit_dialog", "");
+    }
+    else
+    {
+        gui_vmess("gui_post", "s", "about to quite seresly dood");
+        glob_quit(0);
+    }
 }
 
 //void canvas_dofree(t_gobj *dummy, t_glist *x)
@@ -5305,6 +5389,7 @@ void canvas_menuclose(t_canvas *x, t_floatarg fforce)
             /* first open window */
             if (!glist_istoplevel(g) && g->gl_env)
             {
+      post("opening menu");
                 /* if this is an abstraction */
                 vmess(&g->gl_pd, gensym("menu-open"), "");
             }
@@ -5316,13 +5401,21 @@ void canvas_menuclose(t_canvas *x, t_floatarg fforce)
             if (!glist_istoplevel(g) && g->gl_env)
             {
                 /* if this is an abstraction */
-                sys_vgui("pdtk_canvas_menuclose .x%lx {.x%lx menuclose 2;}\n",
-                     g, g);
+                //sys_vgui("pdtk_canvas_menuclose .x%lx {.x%lx menuclose 2;}\n",
+                //     g, g);
+                gui_vmess("gui_canvas_menuclose", "ssi",
+                    canvas_string(g),
+                    canvas_string(g),
+                    2);
             }
             else
             {
-                sys_vgui("pdtk_canvas_menuclose .x%lx {.x%lx menuclose 2;}\n",
-                     canvas_getrootfor(g), g);
+                //sys_vgui("pdtk_canvas_menuclose .x%lx {.x%lx menuclose 2;}\n",
+                //     canvas_getrootfor(g), g);
+                gui_vmess("gui_canvas_menuclose", "ssi",
+                    canvas_string(canvas_getrootfor(g)),
+                    canvas_string(g),
+                    2);
             }
             return;
         }
@@ -5359,6 +5452,7 @@ void canvas_menuclose(t_canvas *x, t_floatarg fforce)
         {
             if (!glist_istoplevel(g) && g->gl_env)
             {
+post("opening up the motherfucker x%lx", g);
                 /* if this is an abstraction */
                 vmess(&g->gl_pd, gensym("menu-open"), "");
             }
@@ -5366,18 +5460,29 @@ void canvas_menuclose(t_canvas *x, t_floatarg fforce)
             {
                 /* is this even necessary? */
                 canvas_vis(g, 1);
+post("fuck you canvas_vis x%lx", g);
             }
             //vmess(&g->gl_pd, gensym("menu-open"), "");
             if (!glist_istoplevel(g) && g->gl_env)
             {
+post("farging...");
                 /* if this is an abstraction */
-                sys_vgui("pdtk_canvas_menuclose .x%lx {.x%lx menuclose 2;}\n",
-                     g, g);
+                //sys_vgui("pdtk_canvas_menuclose .x%lx {.x%lx menuclose 2;}\n",
+                //     g, g);
+                gui_vmess("gui_canvas_menuclose", "ssi",
+                    canvas_string(g),
+                    canvas_string(g),
+                    2);
             }
             else
             {
-                sys_vgui("pdtk_canvas_menuclose .x%lx {.x%lx menuclose 2;}\n",
-                     canvas_getrootfor(g), g);
+post("dildo");
+                //sys_vgui("pdtk_canvas_menuclose .x%lx {.x%lx menuclose 2;}\n",
+                //     canvas_getrootfor(g), g);
+                gui_vmess("gui_canvas_menuclose", "ssi",
+                    canvas_string(canvas_getrootfor(g)),
+                    canvas_string(g),
+                    2);
             }
             //sys_vgui("pdtk_canvas_menuclose .x%lx {.x%lx menuclose 2;\n}\n",
             //         canvas_getrootfor(x), g);
diff --git a/pd/src/g_graph.c b/pd/src/g_graph.c
index 6c1b05bdf4290d1131845f0baf74df688c3a4040..b5771002a66dde463166e9f92d514ba89229ac5e 100644
--- a/pd/src/g_graph.c
+++ b/pd/src/g_graph.c
@@ -881,14 +881,32 @@ static void graph_vis(t_gobj *gr, t_glist *parent_glist, int vis)
     //}
     //fprintf(stderr,"tgt=.x%lx %d\n", (t_int)tgt, exception);
 
-    if (vis && canvas_showtext(x) && gobj_shouldvis(gr, parent_glist))
-        rtext_draw(glist_findrtext(parent_glist, &x->gl_obj));
+    sprintf(tag, "%s", rtext_gettag(glist_findrtext(parent_glist, &x->gl_obj)));
+//post("before rtext_draw: %s", tag);
+
+    if (vis & gobj_shouldvis(gr, parent_glist))
+    {
+        int xpix, ypix;
+        xpix = text_xpix(&x->gl_obj, parent_glist);
+        ypix = text_ypix(&x->gl_obj, parent_glist);
+        gui_vmess("gui_text_create_gobj", "ssii",
+            canvas_string(glist_getcanvas(x->gl_owner)),
+            tag, xpix, ypix);
+        if (canvas_showtext(x))
+            rtext_draw(glist_findrtext(parent_glist, &x->gl_obj));
+    }
+
+//    sprintf(tag, "%s", rtext_gettag(glist_findrtext(parent_glist, &x->gl_obj)));
+//post("after rtext_draw: %s", tag);
+
+    // need the rect to create the gobj, so this should perhaps be above the
+    // conditional
     graph_getrect(gr, parent_glist, &x1, &y1, &x2, &y2);
     //fprintf(stderr,"%d %d %d %d\n", x1, y1, x2, y2);
+
     if (!vis)
         rtext_erase(glist_findrtext(parent_glist, &x->gl_obj));
 
-    sprintf(tag, "%s", rtext_gettag(glist_findrtext(parent_glist, &x->gl_obj)));
     //sprintf(tag, "graph%lx", (t_int)x);
     //fprintf(stderr, "gettag=%s, tag=graph%lx\n",
     //    rtext_gettag(glist_findrtext(parent_glist, &x->gl_obj)),(t_int)x);
@@ -898,19 +916,30 @@ static void graph_vis(t_gobj *gr, t_glist *parent_glist, int vis)
     {
         if (vis && gobj_shouldvis(gr, parent_glist))
         {
-            sys_vgui(".x%lx.c create ppolygon %d %d %d %d %d %d %d %d %d %d "
-                     "-tags {%sfill graph} -fill $pd_colors(graph_border) "
-                     "-stroke $pd_colors(graph_border)\n",
-                glist_getcanvas(x->gl_owner),
-                //parent_glist,
-                x1, y1, x1, y2, x2, y2, x2, y1, x1, y1, tag);
+            //sys_vgui(".x%lx.c create ppolygon %d %d %d %d %d %d %d %d %d %d "
+            //         "-tags {%sfill graph} -fill $pd_colors(graph_border) "
+            //         "-stroke $pd_colors(graph_border)\n",
+            //    glist_getcanvas(x->gl_owner),
+                ////parent_glist,
+            //    x1, y1, x1, y2, x2, y2, x2, y1, x1, y1, tag);
+            char tagbuf[MAXPDSTRING];
+            sprintf(tagbuf, "%sfill", tag);
+            gui_vmess("gui_text_drawborder", "ssiiiii",
+                canvas_string(glist_getcanvas(x->gl_owner)),
+                tag,
+                0, x1, y1, x2, y2);
             glist_noselect(x->gl_owner);
         }
         else if (gobj_shouldvis(gr, parent_glist))
         {
-            sys_vgui(".x%lx.c delete %sfill\n",
-                glist_getcanvas(x->gl_owner), tag);
-                //parent_glist, tag);
+            //sys_vgui(".x%lx.c delete %sfill\n",
+            //    glist_getcanvas(x->gl_owner), tag);
+               ////parent_glist, tag);
+            char tagbuf2[MAXPDSTRING];
+            sprintf(tagbuf2, "%sfill", tag);
+            gui_vmess("gui_graph_deleteborder", "ss",
+                canvas_string(glist_getcanvas(x->gl_owner)),
+                tagbuf2);
         }
         return;
     }
@@ -926,12 +955,17 @@ static void graph_vis(t_gobj *gr, t_glist *parent_glist, int vis)
                    "-stroke $pd_colors(graph_border) -tags {%sR %s graph}\n",
             glist_getcanvas(x->gl_owner),
             x1, y1, x1, y2, x2, y2, x2, y1, x1, y1, tag, tag);*/
-        sys_vgui(".x%lx.c create prect %d %d %d %d "
-                 "-stroke $pd_colors(graph_border) -tags {%sR graph}\n",
-                 //REMOVED: -fill $pd_colors(graph) 
-            glist_getcanvas(x->gl_owner),
-            x1, y1, x2, y2, tag); // -fill $pd_colors(graph)
-
+        //sys_vgui(".x%lx.c create prect %d %d %d %d "
+        //         "-stroke $pd_colors(graph_border) -tags {%sR graph}\n",
+        //         //REMOVED: -fill $pd_colors(graph) 
+        //    glist_getcanvas(x->gl_owner),
+        //    x1, y1, x2, y2, tag); // -fill $pd_colors(graph)
+        char tagbuf[MAXPDSTRING];
+        sprintf(tagbuf, "%sR", tag);
+        gui_vmess("gui_text_drawborder", "ssiiiii",
+            canvas_string(glist_getcanvas(x->gl_owner)),
+            tag,
+            0, x1, y1, x2, y2);
             /* write garrays' names along the top */
         for (i = (y1 < y2 ? y1 : y2)-1, g = x->gl_list; g; g = g->g_next)
         {
@@ -1380,6 +1414,12 @@ static void graph_select(t_gobj *z, t_glist *glist, int state)
                  canvas, rtext_gettag(y), 
                  (state? "$pd_colors(selection)" : "$pd_colors(graph_border)"),
                  (state? "$pd_colors(selection)" : "$pd_colors(graph_border)"));
+            if (state)
+                gui_vmess("gui_text_select", "ss",
+                    canvas_string(canvas), rtext_gettag(y));
+            else
+                gui_vmess("gui_text_deselect", "ss",
+                    canvas_string(canvas), rtext_gettag(y));
         }
 
         t_gobj *g;
diff --git a/pd/src/g_magicglass.c b/pd/src/g_magicglass.c
index bdde53081870642f7344de7a96e067bee5e67fd0..22cd07fe0d45cb013ba9a0552d6b59d01cc541f5 100644
--- a/pd/src/g_magicglass.c
+++ b/pd/src/g_magicglass.c
@@ -5,6 +5,7 @@
 #include "m_imp.h"
 #include "s_stuff.h"
 #include "g_magicglass.h"
+#include "g_canvas.h"
 
 #define MG_CLOCK_CLEAR_DELAY 500.5
 #define MG_CLOCK_FLASH_DELAY 50
@@ -116,6 +117,12 @@ void magicGlass_updateText(t_magicGlass *x, int moved)
                  x->x_y - (int)(sys_fontheight(x->x_display_font)/2) - 3,
                  bgSize,
                  x->x_y + (int)(sys_fontheight(x->x_display_font)/2) + 3);
+        gui_vmess("gui_cord_inspector_update", "ssiiiiii",
+            canvas_string(x->x_c), x->x_string,
+            x->x_x, x->x_y, bgSize, 
+            x->x_y - (int)(sys_fontheight(x->x_display_font)/2) - 3,
+            x->x_y + (int)(sys_fontheight(x->x_display_font)/2) + 3,
+            moved);
     }
 }
 
@@ -137,6 +144,10 @@ void magicGlass_drawNew(t_magicGlass *x)
              x->x_c);
     sys_vgui(".x%x.c raise magicGlassText\n",
              x->x_c);
+    gui_vmess("gui_text_create_gobj", "ssii",
+        canvas_string(x->x_c), "cord_inspector", 0, 0);
+    gui_vmess("gui_create_cord_inspector", "s",
+        canvas_string(x->x_c)); 
     magicGlass_updateText(x, 0);
     clock_delay(x->x_flashClock, MG_CLOCK_FLASH_DELAY);
 }
@@ -147,6 +158,8 @@ void magicGlass_undraw(t_magicGlass *x)
     sys_vgui(".x%x.c delete magicGlassBg\n", x->x_c);
     sys_vgui(".x%x.c delete magicGlassLine\n", x->x_c);
     sys_vgui(".x%x.c delete magicGlassText\n", x->x_c);
+    gui_vmess("gui_erase_cord_inspector", "s",
+        canvas_string(x->x_c));
 }
 
 void magicGlass_flashText(t_magicGlass *x)
@@ -155,6 +168,8 @@ void magicGlass_flashText(t_magicGlass *x)
     sys_vgui(".x%x.c itemconfigure magicGlassText "
              "-fill $pd_colors(magic_glass_text)\n",
          x->x_c);
+    gui_vmess("gui_cord_inspector_flash", "s",
+        canvas_string(x->x_c));
 }
 
 void magicGlass_clearText(t_magicGlass *x)
diff --git a/pd/src/g_mycanvas.c b/pd/src/g_mycanvas.c
index c3254df71a4f95cff18f815fbec523005f510644..bb13898654a02e873cd1e709c636fc7b9bc6f355 100644
--- a/pd/src/g_mycanvas.c
+++ b/pd/src/g_mycanvas.c
@@ -33,6 +33,15 @@ void my_canvas_draw_new(t_my_canvas *x, t_glist *glist)
         "-tags {%lxBASE x%lx text iemgui}\n",
         canvas, x1, y1, x1+x->x_gui.x_w, y1+x->x_gui.x_h,
         x->x_gui.x_bcol, x, x);
+    char tagbuf[MAXPDSTRING];
+    sprintf(tagbuf, "x%lx", (long unsigned int)x);
+    char colorbuf[MAXPDSTRING];
+    sprintf(colorbuf, "#%6.6x", x->x_gui.x_bcol);
+    gui_vmess("gui_text_create_gobj", "ssii", canvas_string(canvas),
+        tagbuf, x1, y1);
+    gui_vmess("gui_create_mycanvas", "sssiiiiii", canvas_string(canvas),
+        tagbuf, colorbuf, x1, y1, x1+x->x_vis_w, y1+x->x_vis_h,
+        x1+x->x_gui.x_w, y1+x->x_gui.x_h);
 }
 
 void my_canvas_draw_move(t_my_canvas *x, t_glist *glist)
@@ -67,6 +76,11 @@ void my_canvas_draw_select(t_my_canvas* x, t_glist* glist)
     sys_vgui(".x%lx.c itemconfigure %lxBASE -stroke %s\n", canvas, x,
         x->x_gui.x_selected == canvas && x->x_gui.x_glist == canvas ?
         "$pd_colors(selection)" : bcol);
+    char tagbuf[MAXPDSTRING];
+    sprintf(tagbuf, "x%lx", (long unsigned int)x);
+    gui_vmess("gui_mycanvas_select_color", "sss", canvas_string(canvas),
+        tagbuf, x->x_gui.x_selected == canvas && x->x_gui.x_glist == canvas ?
+            "blue" : bcol); 
 }
 
 static void my_canvas__clickhook(t_scalehandle *sh, int newstate)
diff --git a/pd/src/g_numbox.c b/pd/src/g_numbox.c
index b120354e51f43b6db123e64bf4a5d7a4d33ae361..6459ba4d101127790f5cc6a20a32841940fcf042 100644
--- a/pd/src/g_numbox.c
+++ b/pd/src/g_numbox.c
@@ -140,6 +140,11 @@ static void my_numbox_draw_update(t_gobj *client, t_glist *glist)
         sys_vgui(
             ".x%lx.c itemconfigure %lxNUMBER -fill #%6.6x -text {%s}\n",
                 glist_getcanvas(glist), x, IEM_GUI_COLOR_EDITED, cp);
+        char tagbuf[MAXPDSTRING];
+        sprintf(tagbuf, "x%lx", (long unsigned int)x);
+        gui_vmess("gui_text_set", "sss", canvas_string(glist_getcanvas(glist)),
+            tagbuf, cp);
+            
         x->x_buf[sl] = 0;
     }
     else
@@ -153,6 +158,10 @@ static void my_numbox_draw_update(t_gobj *client, t_glist *glist)
             glist_getcanvas(glist), x,
             x->x_gui.x_selected == glist_getcanvas(glist) && 
                 !x->x_gui.x_change ? selection_color : fcol, x->x_buf);
+        char tagbuf[MAXPDSTRING];
+        sprintf(tagbuf, "x%lx", (long unsigned int)x);
+        gui_vmess("gui_text_set", "sss", canvas_string(glist_getcanvas(glist)),
+            tagbuf, x->x_buf);
         x->x_buf[0] = 0;
     }
 }
@@ -171,7 +180,10 @@ static void my_numbox_draw_new(t_my_numbox *x, t_glist *glist)
         canvas, x1, y1, x2-4, y1, x2, y1+4, x2, y2, x1, y2,
         x->x_hide_frame <= 1 ? "$pd_colors(iemgui_border)" : bcol,
         bcol, x, x);
-
+    char tagbuf[MAXPDSTRING];
+    sprintf(tagbuf, "x%lx", (long unsigned int)x);
+    gui_vmess("gui_create_numbox", "sssiiiiiiiiiiiii", canvas_string(canvas),
+        tagbuf, bcol, x1, y1, x2-4, y1, x2, y1+4, x2, y2, x1, y2, x1, y1, half);
     if (!x->x_hide_frame || x->x_hide_frame == 2)
         sys_vgui(".x%lx.c create polyline %d %d %d %d %d %d -stroke #%6.6x "
             "-tags {%lxBASE2 x%lx text iemgui}\n",
@@ -182,6 +194,10 @@ static void my_numbox_draw_new(t_my_numbox *x, t_glist *glist)
         "-font %s -fill #%6.6x -tags {%lxNUMBER x%lx noscroll text iemgui}\n",
         canvas, x1+half+2, y1+half+d, x->x_buf, iemgui_font(&x->x_gui),
         x->x_gui.x_fcol, x, x);
+    char colorbuf[MAXPDSTRING];
+    sprintf(colorbuf, "#%6.6x", x->x_gui.x_fcol);
+    gui_vmess("gui_numbox_drawtext", "ssssiiii", canvas_string(canvas), tagbuf,
+        x->x_buf, colorbuf, x1+half+2, y1+half+d, x1, y1);
 }
 
 static void my_numbox_draw_move(t_my_numbox *x, t_glist *glist)
diff --git a/pd/src/g_radio.c b/pd/src/g_radio.c
index ca476b52678797997cc01a8fce5bfbd6ac2ff0e4..aa9fb81f2b431aeb29b33a8501e18642453b6782 100644
--- a/pd/src/g_radio.c
+++ b/pd/src/g_radio.c
@@ -32,6 +32,10 @@ void radio_draw_update(t_gobj *client, t_glist *glist)
         canvas, x, x->x_drawn, x->x_gui.x_bcol, x->x_gui.x_bcol);
     sys_vgui(".x%lx.c itemconfigure %lxBUT%d -fill #%6.6x -stroke #%6.6x\n",
         canvas, x, x->x_on,    x->x_gui.x_fcol, x->x_gui.x_fcol);
+    char tagbuf[MAXPDSTRING];
+    sprintf(tagbuf, "x%lx", (long unsigned int)x);
+    gui_vmess("gui_radio_update", "ssii", canvas_string(canvas),
+        tagbuf, x->x_drawn, x->x_on);
     x->x_drawn = x->x_on;
 }
 
@@ -44,29 +48,55 @@ void radio_draw_new(t_radio *x, t_glist *glist)
     iemgui_base_draw_new(&x->x_gui);
 
     for(i=0; i<n; i++) if (x->x_orient) {
-        if (i) sys_vgui(".x%lx.c create prect %d %d %d %d "
+        if (i)
+        {
+            sys_vgui(".x%lx.c create prect %d %d %d %d "
             "-stroke $pd_colors(iemgui_border) "
             "-tags {%lxBASE%d %lxBASE x%lx text iemgui border}\n",
             canvas, x1, yi, x1+d, yi, x, i, x, x);
+            char tagbuf[MAXPDSTRING];
+            sprintf(tagbuf, "x%lx", (long unsigned int)x);
+            gui_vmess("gui_create_radio", "ssiiiiiii",
+              canvas_string(canvas), tagbuf, x1, yi, x1+d, yi, i, x1, y1);
+        }
         sys_vgui(".x%lx.c create prect %d %d %d %d -fill #%6.6x "
             "-stroke #%6.6x -tags {%lxBUT%d x%lx text iemgui}\n",
             canvas, x1+s, yi+s, x1+d-s, yi+d-s,
             (x->x_on==i)?x->x_gui.x_fcol:x->x_gui.x_bcol,
             (x->x_on==i)?x->x_gui.x_fcol:x->x_gui.x_bcol,
             x, i, x);
+        char tagbuf[MAXPDSTRING];
+        sprintf(tagbuf, "x%lx", (long unsigned int)x);
+        char colorbuf[MAXPDSTRING];
+        sprintf(colorbuf, "#%6.6x", x->x_gui.x_fcol);
+        gui_vmess("gui_create_radio_buttons", "sssiiiiiiii", canvas_string(canvas),
+            tagbuf, colorbuf, x1+s, yi+s, x1+d-s, yi+d-s, x1, y1, i, x->x_on==i);
         yi += d;
         x->x_drawn = x->x_on;
     } else {
-        if (i) sys_vgui(".x%lx.c create prect %d %d %d %d "
+        if (i)
+        {
+            sys_vgui(".x%lx.c create prect %d %d %d %d "
             "-stroke $pd_colors(iemgui_border) "
             "-tags {%lxBASE%d %lxBASE x%lx text iemgui border}\n",
             canvas, xi, y1, xi, y1+d, x, i, x, x);
+            char tagbuf[MAXPDSTRING];
+            sprintf(tagbuf, "x%lx", (long unsigned int)x);
+            gui_vmess("gui_create_radio", "ssiiiiiii", canvas_string(canvas),
+                tagbuf, xi, y1, xi, y1+d, i, x1, y1);
+        }
         sys_vgui(".x%lx.c create prect %d %d %d %d -fill #%6.6x "
             "-stroke #%6.6x -tags {%lxBUT%d x%lx text iemgui}\n",
             canvas, xi+s, y1+s, xi+d-s, y1+d-s,
             (x->x_on==i)?x->x_gui.x_fcol:x->x_gui.x_bcol,
             (x->x_on==i)?x->x_gui.x_fcol:x->x_gui.x_bcol,
             x, i, x);
+        char tagbuf[MAXPDSTRING];
+        sprintf(tagbuf, "x%lx", (long unsigned int)x);
+        char colorbuf[MAXPDSTRING];
+        sprintf(colorbuf, "#%6.6x", x->x_gui.x_fcol);
+        gui_vmess("gui_create_radio_buttons", "sssiiiiiiii", canvas_string(canvas),
+            tagbuf, colorbuf, xi+s, y1+s, xi+d-s, yi+d-s, x1, y1, i, x->x_on==i);
         xi += d;
         x->x_drawn = x->x_on;
     }
diff --git a/pd/src/g_readwrite.c b/pd/src/g_readwrite.c
index 6d31a8a2ae9daa6e1da58d870d3c9f727d24e342..5dd0951803f5dc22fe25318faea3b32ce229afea 100644
--- a/pd/src/g_readwrite.c
+++ b/pd/src/g_readwrite.c
@@ -776,8 +776,9 @@ static void canvas_savetofile(t_canvas *x, t_symbol *filename, t_symbol *dir)
 static void canvas_menusaveas(t_canvas *x)
 {
     t_canvas *x2 = canvas_getrootfor(x);
-    sys_vgui("pdtk_canvas_saveas .x%lx \"%s\" \"%s\"\n", x2,
-        x2->gl_name->s_name, canvas_getdir(x2)->s_name);
+    gui_vmess("gui_canvas_saveas", "sss", canvas_string(x2), x2->gl_name->s_name, canvas_getdir(x2)->s_name);
+//    sys_vgui("pdtk_canvas_saveas .x%lx \"%s\" \"%s\"\n", x2,
+//        x2->gl_name->s_name, canvas_getdir(x2)->s_name);
 }
 
 static void canvas_menusave(t_canvas *x)
diff --git a/pd/src/g_rtext.c b/pd/src/g_rtext.c
index 4e7ba86b77cbc9d9c113ec9a53b6baccb8426522..961e07918c14a71b3069bc77e3d5ac7ec6b6d990 100644
--- a/pd/src/g_rtext.c
+++ b/pd/src/g_rtext.c
@@ -330,7 +330,12 @@ static void rtext_senditup(t_rtext *x, int action, int *widthp, int *heightp,
         // add a null character at the end of the string (for u8_charnum)
         tempbuf[outchars_b++] = '\0';
 
-        pixwide = ncolumns * fontwidth + (LMARGIN + RMARGIN);
+        // The following is an enormous hack to get the box width to match size 12px
+        // DejaVu as closely as possible.  A better solution would require a fuller
+        // understanding of the font logic here and in s_main.
+        //pixwide = ncolumns * fontwidth + (LMARGIN + RMARGIN);
+        float fudge_factor = 0.2;
+        pixwide = (int)(ncolumns * (fontwidth + fudge_factor) + (LMARGIN + RMARGIN));
         pixhigh = nlines * fontheight + (TMARGIN + BMARGIN);
         //printf("outchars_b=%d bufsize=%d %d\n", outchars_b, x->x_bufsize, x->x_buf[outchars_b]);
 
@@ -357,11 +362,20 @@ static void rtext_senditup(t_rtext *x, int action, int *widthp, int *heightp,
                 dispx + LMARGIN, dispy + TMARGIN,
                 outchars_b, tempbuf, sys_hostfontsize(font),
                 (glist_isselected(x->x_glist, ((t_gobj*)x->x_text)) ? "$pd_colors(selection)" : "$pd_colors(text)"));
+            gui_vmess("gui_text_new", "sssiffsi",
+                canvas_string(canvas), x->x_tag, rtext_gettype(x)->s_name,
+                glist_isselected(x->x_glist, ((t_gobj*)x->x_text)),
+                dispx + LMARGIN,
+                dispy + TMARGIN,
+                tempbuf,
+                sys_hostfontsize(font));
+               
         }
         else if (action == SEND_UPDATE)
         {
             sys_vgui("pdtk_text_set .x%lx.c %s {%.*s}\n",
                 canvas, x->x_tag, outchars_b, tempbuf);
+            gui_vmess("gui_text_set", "sss", canvas_string(canvas), x->x_tag, tempbuf);
 
             if (pixwide != x->x_drawnwidth || pixhigh != x->x_drawnheight) 
                 text_drawborder(x->x_text, x->x_glist, x->x_tag,
diff --git a/pd/src/g_scalar.c b/pd/src/g_scalar.c
index 02d2e78b1dc3d592fac20fcfbf729d106adcceaa..baebc17ae9da94003a52ac02fb7eb0ebf03d1a17 100644
--- a/pd/src/g_scalar.c
+++ b/pd/src/g_scalar.c
@@ -412,45 +412,56 @@ static void scalar_getrect(t_gobj *z, t_glist *owner,
 
 void scalar_drawselectrect(t_scalar *x, t_glist *glist, int state)
 {
+    char tagbuf[MAXPDSTRING];
+    sprintf(tagbuf, "scalar%lx", (long unsigned int)x->sc_vec);
+
     //fprintf(stderr,"scalar_drawselecterect%d\n", state);
     if (state)
     {
         int x1, y1, x2, y2;
-       
+        t_float basex, basey;
+        scalar_getbasexy(x, &basex, &basey);
+
         scalar_getrect(&x->sc_gobj, glist, &x1, &y1, &x2, &y2);
         x1--; x2++; y1--; y2++;
         if (glist_istoplevel(glist))
+        {
             sys_vgui(".x%lx.c create prect %d %d %d %d "
                      "-strokewidth 1 -stroke $pd_colors(selection) "
                      "-tags {select%lx selected}\n",
                     glist_getcanvas(glist), x1, y1, x2, y2,
                     x);
+            gui_vmess("gui_scalar_draw_select_rect", "ssiiiiiff",
+                canvas_string(glist_getcanvas(glist)), tagbuf,
+                state,
+                x1, y1, x2, y2, basex, basey);
+        }
     }
     else
     {
         if (glist_istoplevel(glist))
             sys_vgui(".x%lx.c delete select%lx\n", glist_getcanvas(glist), x);
+        gui_vmess("gui_scalar_draw_select_rect", "ssiiiiiii",
+            canvas_string(glist_getcanvas(glist)), tagbuf,
+            state,
+            0, 0, 0, 0, 0, 0);
     }
 }
 
-/* This is a workaround.  Since scalars are contained within a tkpath
-   group, and since tkpath groups don't have coords, we can't just use
-   the same "selected" tag that is used to move all other Pd objects. That
-   would move scalars in their local coordinate system, which is wrong
-   for transformed objects. For example, if a rectangle is rotated 45 and
-   we try to do a [canvas move 10 0] command on it, it would get moved to
-   the northeast instead of to the right!
+/* This is greatly simplified with Node-Webkit-- we just need to get the
+   basex/basey for the scalar, in addition to the bbox of the scalar.
 
-   Instead, we tag selected scalars with the "scalar_selected" tag. Then in
-   the GUI we use that tag to loop through and change each scalar's group
-   matrix, and add (dx,dy) to its current translation values. The scalar
-   group matrix .scalar%lx isn't accessible by the user, so it will only
-   ever contain these translation values.
+   This can be simplified further by using a single function on the GUI
+   side, and sending it the "state" parameter.
 */
 void scalar_select(t_gobj *z, t_glist *owner, int state)
 {
     //fprintf(stderr,"scalar_select %d\n", state);
     t_scalar *x = (t_scalar *)z;
+
+    char tagbuf[MAXPDSTRING];
+    sprintf(tagbuf, "scalar%lx", (long unsigned int)x->sc_vec);
+
     t_template *tmpl;
     t_symbol *templatesym = x->sc_template;
     t_atom at;
@@ -473,6 +484,8 @@ void scalar_select(t_gobj *z, t_glist *owner, int state)
             glist_getcanvas(owner), x);
         sys_vgui(".x%lx.c addtag scalar_selected withtag {.scalar%lx}\n",
             glist_getcanvas(owner), x->sc_vec);
+        gui_vmess("gui_text_select", "ss",
+            canvas_string(glist_getcanvas(owner)), tagbuf);
     }
     else
     {
@@ -481,6 +494,8 @@ void scalar_select(t_gobj *z, t_glist *owner, int state)
             glist_getcanvas(owner), x);
         sys_vgui(".x%lx.c dtag .scalar%lx scalar_selected\n",
             glist_getcanvas(owner), x->sc_vec);
+        gui_vmess("gui_text_deselect", "ss",
+            canvas_string(glist_getcanvas(owner)), tagbuf);
     }
     //sys_vgui("pdtk_select_all_gop_widgets .x%lx %lx %d\n",
     //    glist_getcanvas(owner), owner, state);
@@ -617,6 +632,112 @@ static void scalar_delete(t_gobj *z, t_glist *glist)
 
 extern void svg_grouptogui(t_glist *g, t_template *template, t_word *data);
 
+extern void svg_parentwidgettogui(t_gobj *z, t_glist *owner, t_word *data,
+    t_template *template);
+
+static void scalar_group_configure(t_scalar *x, t_glist *owner,
+    t_template *template, t_glist *gl, t_glist *parent)
+{
+    t_gobj *y;
+    char tagbuf[MAXPDSTRING];
+    sprintf(tagbuf, "dgroup%lx.%lx", (long unsigned int)gl,
+        (long unsigned int)x->sc_vec);
+    char parentbuf[MAXPDSTRING];
+    sprintf(parentbuf, "dgroup%lx.%lx", (long unsigned int)parent,
+        (long unsigned int)x->sc_vec);
+    gui_start_vmess("gui_draw_configure_all", "ss",
+        canvas_string(glist_getcanvas(owner)), tagbuf);
+    svg_grouptogui(gl, template, x->sc_vec);
+    gui_end_vmess();
+    for (y = gl->gl_list; y; y = y->g_next)
+    {
+        if (pd_class(&y->g_pd) == canvas_class &&
+            ((t_glist *)y)->gl_svg)
+        {
+            scalar_group_configure(x, owner, template, (t_glist *)y, gl);
+        }
+        t_parentwidgetbehavior *wb = pd_getparentwidget(&y->g_pd);
+        if (!wb) continue;
+        //(*wb->w_parentvisfn)(y, owner, gl, x, x->sc_vec, template,
+        //   0, 0, vis);
+        svg_parentwidgettogui(y, owner, x->sc_vec, template);
+    }
+}
+
+void scalar_configure(t_scalar *x, t_glist *owner)
+{
+    int vis = glist_isvisible(owner);
+    if (vis)
+    {
+        //fprintf(stderr,"scalar_vis %d %lx\n", vis, (t_int)z);
+        x->sc_bboxcache = 0;
+
+        t_template *template = template_findbyname(x->sc_template);
+        t_canvas *templatecanvas = template_findcanvas(template);
+        t_gobj *y;
+        t_float basex, basey;
+        scalar_getbasexy(x, &basex, &basey);
+            /* if we don't know how to draw it, make a small rectangle */
+
+        t_float xscale = glist_xtopixels(owner, 1) - glist_xtopixels(owner, 0);
+        t_float yscale = glist_ytopixels(owner, 1) - glist_ytopixels(owner, 0);
+
+        char tagbuf[MAXPDSTRING];
+        sprintf(tagbuf, "scalar%lx", (long unsigned int)x->sc_vec);
+        gui_vmess("gui_scalar_configure_gobj", "ssiffffii",
+            canvas_string(glist_getcanvas(owner)), 
+            tagbuf,
+            glist_isselected(owner, &x->sc_gobj),
+            xscale, 0.0, 0.0, yscale,
+            (int)glist_xtopixels(owner, basex),
+            (int)glist_ytopixels(owner, basey));
+
+        char groupbuf[MAXPDSTRING];
+        // Quick hack to make gui_create_scalar_group more general (so we
+        // don't have to tack on "gobj" manually)
+
+
+        //Not sure if we need this here...
+        //sprintf(tagbuf, "scalar%lxgobj", (long unsigned int)x->sc_vec);
+        //sprintf(groupbuf, "dgroup%lx.%lx", (long unsigned int)templatecanvas,
+        //    (long unsigned int)x->sc_vec);
+        //gui_vmess("gui_create_scalar_group", "sss",
+        //    canvas_string(glist_getcanvas(owner)), groupbuf, tagbuf); 
+        //sys_vgui("pdtk_bind_scalar_mouseover "
+        //         ".x%lx.c .x%lx.x%lx.template%lx {.x%lx}\n",
+        //    glist_getcanvas(owner), glist_getcanvas(owner),
+        //    owner, x->sc_vec, x);
+
+        for (y = templatecanvas->gl_list; y; y = y->g_next)
+        {
+            t_parentwidgetbehavior *wb = pd_getparentwidget(&y->g_pd);
+            if (!wb)
+            {
+                /* check subpatches for more drawing commands.
+                   (Optimized to only search [group] subpatches) */
+                if (pd_class(&y->g_pd) == canvas_class &&
+                    ((t_glist *)y)->gl_svg)
+                {
+                    scalar_group_configure(x, owner, template,
+                        (t_glist *)y, templatecanvas);
+                }
+                continue;
+            }
+            //(*wb->w_parentvisfn)(y, owner, 0, x, x->sc_vec, template,
+            //    basex, basey, vis);
+            svg_parentwidgettogui(y, owner, x->sc_vec, template);
+        }
+        if (glist_isselected(owner, &x->sc_gobj))
+        {
+            // we removed this because it caused infinite recursion
+            // in the scalar-help.pd example
+            //scalar_select(z, owner, 1);
+            scalar_drawselectrect(x, owner, 0);
+            scalar_drawselectrect(x, owner, 1);
+        }
+    }
+}
+
 static void scalar_groupvis(t_scalar *x, t_glist *owner, t_template *template,
     t_glist *gl, t_glist *parent, int vis)
 {
@@ -627,8 +748,17 @@ static void scalar_groupvis(t_scalar *x, t_glist *owner, t_template *template,
                  "-parent {.dgroup%lx.%lx}\\\n",
             glist_getcanvas(owner), gl, x->sc_vec,
             parent, x->sc_vec);
+        char tagbuf[MAXPDSTRING];
+        sprintf(tagbuf, "dgroup%lx.%lx", (long unsigned int)gl,
+            (long unsigned int)x->sc_vec);
+        char parentbuf[MAXPDSTRING];
+        sprintf(parentbuf, "dgroup%lx.%lx", (long unsigned int)parent,
+            (long unsigned int)x->sc_vec);
+        gui_start_vmess("gui_create_scalar_group", "sss",
+            canvas_string(glist_getcanvas(owner)), tagbuf, parentbuf);
         svg_grouptogui(gl, template, x->sc_vec);
-        sys_gui("\n");
+        gui_end_vmess();
+        //sys_gui("\n");
 
     }
     for (y = gl->gl_list; y; y = y->g_next)
@@ -727,9 +857,26 @@ static void scalar_vis(t_gobj *z, t_glist *owner, int vis)
             xscale, 0.0, 0.0, yscale, (int)glist_xtopixels(owner, basex),
             (int)glist_ytopixels(owner, basey)
             );
+        char tagbuf[MAXPDSTRING];
+        sprintf(tagbuf, "scalar%lx", (long unsigned int)x->sc_vec);
+        gui_vmess("gui_create_scalar", "ssiffffii",
+            canvas_string(glist_getcanvas(owner)), 
+            tagbuf,
+            glist_isselected(owner, &x->sc_gobj),
+            xscale, 0.0, 0.0, yscale,
+            (int)glist_xtopixels(owner, basex),
+            (int)glist_ytopixels(owner, basey));
         sys_vgui(".x%lx.c create group -tags {.dgroup%lx.%lx} "
                  "-parent {.scalar%lx}\n",
             glist_getcanvas(owner), templatecanvas, x->sc_vec, x->sc_vec);
+        char groupbuf[MAXPDSTRING];
+        // Quick hack to make gui_create_scalar_group more general (so we
+        // don't have to tack on "gobj" manually)
+        sprintf(tagbuf, "scalar%lxgobj", (long unsigned int)x->sc_vec);
+        sprintf(groupbuf, "dgroup%lx.%lx", (long unsigned int)templatecanvas,
+            (long unsigned int)x->sc_vec);
+        gui_vmess("gui_create_scalar_group", "sss",
+            canvas_string(glist_getcanvas(owner)), groupbuf, tagbuf);
         sys_vgui("pdtk_bind_scalar_mouseover "
                  ".x%lx.c .x%lx.x%lx.template%lx {.x%lx}\n",
             glist_getcanvas(owner), glist_getcanvas(owner),
@@ -742,8 +889,8 @@ static void scalar_vis(t_gobj *z, t_glist *owner, int vis)
         t_parentwidgetbehavior *wb = pd_getparentwidget(&y->g_pd);
         if (!wb)
         {
-            /* check subpatches for more drawing commands.  This
-               can be optimized to only search [group] subpatches */
+            /* check subpatches for more drawing commands.
+               (Optimized to only search [group] subpatches) */
             if (pd_class(&y->g_pd) == canvas_class &&
                 ((t_glist *)y)->gl_svg)
             {
@@ -756,8 +903,14 @@ static void scalar_vis(t_gobj *z, t_glist *owner, int vis)
             basex, basey, vis);
     }
     if (!vis)
+    {
         sys_vgui(".x%lx.c delete .scalar%lx\n", glist_getcanvas(owner),
             x->sc_vec);
+        char tagbuf[MAXPDSTRING];
+        sprintf(tagbuf, "scalar%lx", (long unsigned int)x->sc_vec);
+        gui_vmess("gui_scalar_erase", "ss",
+            canvas_string(glist_getcanvas(owner)), tagbuf);
+    }
 
     sys_unqueuegui(x);
     if (glist_isselected(owner, &x->sc_gobj))
diff --git a/pd/src/g_slider.c b/pd/src/g_slider.c
index 08fc5c5113d68b049398a6327d75ab2688accd91..1be19a1e2ada06be0a95bab39c5792a2e40de67d 100644
--- a/pd/src/g_slider.c
+++ b/pd/src/g_slider.c
@@ -33,10 +33,20 @@ static void slider_draw_update(t_gobj *client, t_glist *glist)
         r=y2-3 - (x->x_val+50)/100;
         sys_vgui(".x%lx.c coords %lxKNOB %d %d %d %d\n",
             canvas, x, x1+2, r, x2-2, r);
+        char tagbuf[MAXPDSTRING];
+        sprintf(tagbuf, "x%lx", (long unsigned int)x);
+        gui_vmess("gui_slider_update", "ssiiiiii",
+            canvas_string(canvas), tagbuf, x1+2, r, x2-2, r,
+            x1, y1);
     } else {
         r=x1+3 + (x->x_val+50)/100;
         sys_vgui(".x%lx.c coords %lxKNOB %d %d %d %d\n",
             canvas, x, r, y1+2, r, y2-2);
+        char tagbuf[MAXPDSTRING];
+        sprintf(tagbuf, "x%lx", (long unsigned int)x);
+        gui_vmess("gui_slider_update", "ssiiiiii",
+            canvas_string(canvas), tagbuf, r, y1+2, r, y2-2,
+            x1, y1);
     }
     int t = x->x_thick;
     x->x_thick = x->x_val == x->x_center;
@@ -59,10 +69,22 @@ static void slider_draw_new(t_slider *x, t_glist *glist)
         sys_vgui(".x%lx.c create polyline %d %d %d %d -strokewidth 3 "
             "-stroke #%6.6x -tags {%lxKNOB x%lx text iemgui}\n",
             canvas, x1+2, r, x2-2, r, x->x_gui.x_fcol, x, x);
+        char tagbuf[MAXPDSTRING];
+        sprintf(tagbuf, "x%lx", (long unsigned int)x);
+        char colorbuf[MAXPDSTRING];
+        sprintf(colorbuf, "#%6.6x", x->x_gui.x_fcol);
+        gui_vmess("gui_create_slider", "sssiiiiii", canvas_string(canvas), tagbuf,
+            colorbuf, x1+2, r, x2-2, r, x1, y1);
     } else {
         sys_vgui(".x%lx.c create polyline %d %d %d %d -strokewidth 3 "
             "-stroke #%6.6x -tags {%lxKNOB x%lx text iemgui}\n",
             canvas, r, y1+2, r, y2-2, x->x_gui.x_fcol, x, x);
+        char tagbuf[MAXPDSTRING];
+        sprintf(tagbuf, "x%lx", (long unsigned int)x);
+        char colorbuf[MAXPDSTRING];
+        sprintf(colorbuf, "#%6.6x", x->x_gui.x_fcol);
+        gui_vmess("gui_create_slider", "sssiiiiii", canvas_string(canvas), tagbuf,
+            colorbuf, r, y1+2, r, y2-2, x1, y1);
     }
 }
 
diff --git a/pd/src/g_template.c b/pd/src/g_template.c
index 08fba1d1ec2acb4b9fefd880dbeae4ddd03c5425..32c415f500ee61bc567e9e9eb6f69edcd44eb50e 100644
--- a/pd/src/g_template.c
+++ b/pd/src/g_template.c
@@ -1472,13 +1472,13 @@ void svg_sendupdate(t_svg *x, t_canvas *c, t_symbol *s,
     if (x->x_type == gensym("group"))
     {
         sprintf(tag, "%s%lx.%lx",
-            (in_array ? ".scelem" : ".dgroup"),
+            (in_array ? "scelem" : "dgroup"),
             (long unsigned int)x->x_parent,
             (long unsigned int)data);
     }
     else
     {
-        sprintf(tag, ".draw%lx.%lx",
+        sprintf(tag, "draw%lx.%lx",
             (long unsigned int)x->x_parent,
             (long unsigned int)data);
     }
@@ -1499,8 +1499,10 @@ void svg_sendupdate(t_svg *x, t_canvas *c, t_symbol *s,
         }
         else
             fill = &s_;
-        sys_vgui(".x%lx.c itemconfigure %s -fill %s\n",
-            glist_getcanvas(c), tag, fill->s_name);
+        //sys_vgui(".x%lx.c itemconfigure %s -fill %s\n",
+        //    glist_getcanvas(c), tag, fill->s_name);
+        gui_vmess("gui_draw_configure", "ssss",
+            canvas_string(glist_getcanvas(c)), tag, s->s_name, fill->s_name);
     }
     else if (s == gensym("stroke"))
     {
@@ -1516,79 +1518,120 @@ void svg_sendupdate(t_svg *x, t_canvas *c, t_symbol *s,
                 (int)fielddesc_getfloat(fd+2, template, data, 1)));
         }
         else stroke = &s_;
-        sys_vgui(".x%lx.c itemconfigure %s -stroke %s\n",
-            glist_getcanvas(c), tag, stroke->s_name);
+        //sys_vgui(".x%lx.c itemconfigure %s -stroke %s\n",
+        //    glist_getcanvas(c), tag, stroke->s_name);
+        gui_vmess("gui_draw_configure", "ssss",
+            canvas_string(glist_getcanvas(c)), tag, s->s_name, stroke->s_name);
     }
     else if (s == gensym("fill-opacity"))
-        sys_vgui(".x%lx.c itemconfigure %s -fillopacity %g\n",
-            glist_getcanvas(c), tag,
+        //sys_vgui(".x%lx.c itemconfigure %s -fillopacity %g\n",
+        //    glist_getcanvas(c), tag,
+        //    fielddesc_getcoord(&x->x_fillopacity.a_attr, template, data, 1));
+        gui_vmess("gui_draw_configure", "sssf",
+            canvas_string(glist_getcanvas(c)), tag, "fill-opacity",
             fielddesc_getcoord(&x->x_fillopacity.a_attr, template, data, 1));
     else if (s == gensym("fill-rule"))
-        sys_vgui(".x%lx.c itemconfigure %s -fillrule %s\n",
-            glist_getcanvas(c), tag, (int)fielddesc_getcoord(
+        //sys_vgui(".x%lx.c itemconfigure %s -fillrule %s\n",
+        //    glist_getcanvas(c), tag, (int)fielddesc_getcoord(
+        //        &x->x_fillrule.a_attr, template, data, 1) ?
+        //            "evenodd" : "nonzero");
+        gui_vmess("gui_draw_configure", "sssi",
+            canvas_string(glist_getcanvas(c)), tag, "fill-rule", (int)fielddesc_getcoord(
                 &x->x_fillrule.a_attr, template, data, 1) ?
                     "evenodd" : "nonzero");
     else if (s == gensym("pointer-events"))
         *predraw_bbox = 1;
     else if (s == gensym("stroke-linecap"))
-        sys_vgui(".x%lx.c itemconfigure %s -strokelinecap %s\n",
-            glist_getcanvas(c), tag, get_strokelinecap(
+        //sys_vgui(".x%lx.c itemconfigure %s -strokelinecap %s\n",
+        //   glist_getcanvas(c), tag, get_strokelinecap(
+        //        (int)fielddesc_getcoord(&x->x_strokelinecap.a_attr,
+        //            template, data, 1)));
+        gui_vmess("gui_draw_configure", "ssss",
+            canvas_string(glist_getcanvas(c)), tag, "stroke-linecap", get_strokelinecap(
                 (int)fielddesc_getcoord(&x->x_strokelinecap.a_attr,
                     template, data, 1)));
     else if (s == gensym("stroke-linejoin"))
-        sys_vgui(".x%lx.c itemconfigure %s -strokelinejoin %s\n",
-            glist_getcanvas(c), tag, get_strokelinejoin(
+        //sys_vgui(".x%lx.c itemconfigure %s -strokelinejoin %s\n",
+        //    glist_getcanvas(c), tag, get_strokelinejoin(
+        //        (int)fielddesc_getcoord(&x->x_strokelinejoin.a_attr,
+        //            template, data, 1)));
+        gui_vmess("gui_draw_configure", "ssss",
+            canvas_string(glist_getcanvas(c)), tag, "stroke-linejoin", get_strokelinejoin(
                 (int)fielddesc_getcoord(&x->x_strokelinejoin.a_attr,
                     template, data, 1)));
     else if (s == gensym("stroke-miterlimit"))
-        sys_vgui(".x%lx.c itemconfigure %s -strokemiterlimit %g\n",
-            glist_getcanvas(c), tag, fielddesc_getcoord(
+        //sys_vgui(".x%lx.c itemconfigure %s -strokemiterlimit %g\n",
+        //    glist_getcanvas(c), tag, fielddesc_getcoord(
+        //        &x->x_strokemiterlimit.a_attr, template, data, 1));
+        gui_vmess("gui_draw_configure", "sssf",
+            canvas_string(glist_getcanvas(c)), tag, "stroke-miterlimit", fielddesc_getcoord(
                 &x->x_strokemiterlimit.a_attr, template, data, 1));
     else if (s == gensym("stroke-opacity"))
-        sys_vgui(".x%lx.c itemconfigure %s -strokeopacity %g\n",
-            glist_getcanvas(c), tag, fielddesc_getcoord(
+        //sys_vgui(".x%lx.c itemconfigure %s -strokeopacity %g\n",
+        //    glist_getcanvas(c), tag, fielddesc_getcoord(
+        //        &x->x_strokeopacity.a_attr, template, data, 1));
+        gui_vmess("gui_draw_configure", "sssg",
+            canvas_string(glist_getcanvas(c)), tag, "stroke-opacity", fielddesc_getcoord(
                 &x->x_strokeopacity.a_attr, template, data, 1));
     else if (s == gensym("stroke-width"))
     {
-        sys_vgui(".x%lx.c itemconfigure %s -strokewidth %g\n",
-            glist_getcanvas(c), tag, fielddesc_getcoord(
+        //sys_vgui(".x%lx.c itemconfigure %s -strokewidth %g\n",
+        //    glist_getcanvas(c), tag, fielddesc_getcoord(
+        //        &x->x_strokewidth.a_attr, template, data, 1));
+        gui_vmess("gui_draw_configure", "sssf",
+            canvas_string(glist_getcanvas(c)), tag, "stroke-width", fielddesc_getcoord(
                 &x->x_strokewidth.a_attr, template, data, 1));
         *predraw_bbox = 1;
     }
     else if (s == gensym("r"))
     {
-        sys_vgui(".x%lx.c itemconfigure %s -rx %g -ry %g\n",
-        glist_getcanvas(c), tag,
-        fielddesc_getcoord(x->x_vec+2, template, data, 1),
-        fielddesc_getcoord(x->x_vec+2, template, data, 1));
+        //sys_vgui(".x%lx.c itemconfigure %s -rx %g -ry %g\n",
+        //glist_getcanvas(c), tag,
+        //fielddesc_getcoord(x->x_vec+2, template, data, 1),
+        //fielddesc_getcoord(x->x_vec+2, template, data, 1));
+        gui_vmess("gui_draw_configure", "sssf",
+            canvas_string(glist_getcanvas(c)), tag, s->s_name,
+            fielddesc_getcoord(x->x_vec+2, template, data, 1));
         *predraw_bbox = 1;
     }
     else if (s == gensym("rx"))
     {
         if (x->x_type == gensym("rect"))
-            sys_vgui(".x%lx.c itemconfigure %s -rx %d\n",
-                glist_getcanvas(c), tag, (int)fielddesc_getcoord(
+            //sys_vgui(".x%lx.c itemconfigure %s -rx %d\n",
+            //    glist_getcanvas(c), tag, (int)fielddesc_getcoord(
+            //        &x->x_rx.a_attr, template, data, 1));
+            gui_vmess("gui_draw_configure", "sssi",
+                canvas_string(glist_getcanvas(c)), tag, s->s_name, (int)fielddesc_getcoord(
                     &x->x_rx.a_attr, template, data, 1));
         else
         {
-            sys_vgui(".x%lx.c itemconfigure %s -rx %g\n",
-                glist_getcanvas(c), tag, fielddesc_getcoord(
-                x->x_vec+2, template, data, 1));
+            //sys_vgui(".x%lx.c itemconfigure %s -rx %g\n",
+            //    glist_getcanvas(c), tag, fielddesc_getcoord(
+            //    x->x_vec+2, template, data, 1));
+            gui_vmess("gui_draw_configure", "sssf",
+                canvas_string(glist_getcanvas(c)), tag, s->s_name, fielddesc_getcoord(
+                    x->x_vec+2, template, data, 1));
             *predraw_bbox = 1;
         }
     }
     else if (s == gensym("ry"))
     {
         if (x->x_type == gensym("rect"))
-            sys_vgui(".x%lx.c itemconfigure %s -ry %d\n",
-                glist_getcanvas(c), tag, (int)fielddesc_getcoord(
-                &x->x_ry.a_attr, template, data, 1));
+            //sys_vgui(".x%lx.c itemconfigure %s -ry %d\n",
+            //    glist_getcanvas(c), tag, (int)fielddesc_getcoord(
+            //    &x->x_ry.a_attr, template, data, 1));
+            gui_vmess("gui_draw_configure", "sssi",
+                canvas_string(glist_getcanvas(c)), tag, s->s_name, (int)fielddesc_getcoord(
+                    &x->x_ry.a_attr, template, data, 1));
         else
         {
-             sys_vgui(".x%lx.c itemconfigure %s -ry %g\n",
-                 glist_getcanvas(c), tag, fielddesc_getcoord(
-                 x->x_vec+3, template, data, 1));
-             *predraw_bbox = 1;
+            //sys_vgui(".x%lx.c itemconfigure %s -ry %g\n",
+            //    glist_getcanvas(c), tag, fielddesc_getcoord(
+            //    x->x_vec+3, template, data, 1));
+            gui_vmess("gui_draw_configure", "sssf",
+                canvas_string(glist_getcanvas(c)), tag, fielddesc_getcoord(
+                    x->x_vec+3, template, data, 1));
+            *predraw_bbox = 1;
         }
     }
     else if (s == gensym("transform"))
@@ -1607,39 +1650,57 @@ void svg_sendupdate(t_svg *x, t_canvas *c, t_symbol *s,
             x->x_pathrect_cache = 0;
         }
         svg_parsetransform(x, template, data, &m1, &m2, &m3, &m4, &m5, &m6);
-            sys_vgui(".x%lx.c itemconfigure %s -matrix "
-                    "{ {%g %g} {%g %g} {%g %g} }\n",
-                    glist_getcanvas(c), tag,
-                    m1, m2, m3, m4, m5, m6);
+        //sys_vgui(".x%lx.c itemconfigure %s -matrix "
+        //         "{ {%g %g} {%g %g} {%g %g} }\n",
+        //            glist_getcanvas(c), tag,
+        //            m1, m2, m3, m4, m5, m6);
+        char mbuf[MAXPDSTRING];
+        sprintf(mbuf, "matrix(%g %g %g %g %g %g)", m1, m2, m3, m4, m5, m6);
+        gui_vmess("gui_draw_configure", "ssss",
+            canvas_string(glist_getcanvas(c)), tag, s->s_name,
+            mbuf);
         *predraw_bbox = 1;
-
     }
     else if (s == gensym("vis"))
     {
-        sys_vgui(".x%lx.c itemconfigure %s -state %s\n",
-            glist_getcanvas(c), tag, (int)fielddesc_getcoord(
-            &x->x_vis.a_attr, template, data, 1) ? "normal" : "hidden");
+        //sys_vgui(".x%lx.c itemconfigure %s -state %s\n",
+        //    glist_getcanvas(c), tag, (int)fielddesc_getcoord(
+        //    &x->x_vis.a_attr, template, data, 1) ? "normal" : "hidden");
+        gui_vmess("gui_draw_configure", "ssss",
+            canvas_string(glist_getcanvas(c)), tag, "visibility", (int)fielddesc_getcoord(
+            &x->x_vis.a_attr, template, data, 1) ? "visible" : "hidden");
         *predraw_bbox = 1;
     }
     else if (s == gensym("stroke-dasharray"))
     {
+        // not ready yet... need a gui interface for variable size attrs
         if (x->x_ndash)
         {
             t_fielddesc *fd = x->x_strokedasharray;
             int i;
-            sys_vgui(".x%lx.c itemconfigure %s -strokedasharray {",
-                glist_getcanvas(c), tag);
+            //sys_vgui(".x%lx.c itemconfigure %s -strokedasharray {",
+            //    glist_getcanvas(c), tag);
+            gui_start_vmess("gui_draw_configure", "sss",
+                canvas_string(glist_getcanvas(c)), tag, s->s_name);
+            gui_start_array();
             for (i = 0; i < x->x_ndash; i++)
             {
-                sys_vgui(" %g ", fielddesc_getcoord(fd+i, template, data, 1));
+                //sys_vgui(" %g ", fielddesc_getcoord(fd+i, template, data, 1));
+                gui_float_elem(fielddesc_getcoord(fd+i, template, data, 1));
             }
-            sys_gui("}\n");
+            //sys_gui("}\n");
+            gui_end_array();
+            gui_end_vmess();
         }
     }
     else if (s == gensym("data"))
     {
-        sys_vgui(".x%lx.c coords .draw%lx.%lx {\\\n",
-            glist_getcanvas(c), parent, data);
+        //sys_vgui(".x%lx.c coords .draw%lx.%lx {\\\n",
+        //    glist_getcanvas(c), parent, data);
+        char tagbuf[MAXPDSTRING];
+        sprintf(tagbuf, "draw%lx.%lx", (long unsigned int)parent, (long unsigned int)data);
+        gui_start_vmess("gui_draw_configure", "sss",
+            canvas_string(glist_getcanvas(c)), tagbuf, "d"); 
         /* let's turn off bbox caching so we can recalculate the bbox */
         if (x->x_pathrect_cache != -1)
            x->x_pathrect_cache = 0;
@@ -1648,19 +1709,28 @@ void svg_sendupdate(t_svg *x, t_canvas *c, t_symbol *s,
         char *cmd;
         t_fielddesc *f;
         int totalpoints = 0; /* running tally */
+        /* start an array parameter */
+        char cmdbuf[2];
+        gui_start_array();
         /* path parser: no error checking yet */
         for (i = 0, cmd = x->x_pathcmds; i < x->x_npathcmds; i++, cmd++)
         {
             int j;
             int cargs = x->x_nargs_per_cmd[i];
             f = (x->x_vec)+totalpoints;
-            sys_vgui("%c\\\n", *(cmd));
+            //sys_vgui("%c\\\n", *(cmd));
+            sprintf(cmdbuf, "%c", *(cmd));
+            gui_string_elem(cmdbuf);
             for (j = 0; j < x->x_nargs_per_cmd[i]; j++)
-                sys_vgui("%g\\\n", fielddesc_getcoord(
-                    f+j, template, data, 1));
+                //sys_vgui("%g\\\n", fielddesc_getcoord(
+                //    f+j, template, data, 1));
+                gui_float_elem(fielddesc_getcoord(
+                    f+j, template, data, 0));
             totalpoints += x->x_nargs_per_cmd[i];
         }
-        sys_gui("}\n");
+        //sys_gui("}\n");
+        gui_end_array();
+        gui_end_vmess();
     }
     else if (s == gensym("index"))
     {
@@ -2421,16 +2491,16 @@ void svg_parsetransform(t_svg *x, t_template *template, t_word *data,
         }
         else if (type == gensym("skewx"))
         {
-            t_float a = fielddesc_getfloat(fd++, template, data, 0) *
-                3.14159 / 180;
+            t_float a = fielddesc_getfloat(fd++, template, data, 0); // *
+            //    3.14159 / 180;
             argc--;
             mset(m2, 1, 0, tan(a), 1, 0, 0);
             mmult(m, m2, m);
         }
         else if (type == gensym("skewy"))
         {
-            t_float a = fielddesc_getfloat(fd++, template, data, 0) *
-                3.14159 / 180;
+            t_float a = fielddesc_getfloat(fd++, template, data, 0); // *
+            //    3.14159 / 180;
             argc--;
             mset(m2, 1, tan(a), 0, 1, 0, 0);
             mmult(m, m2, m);
@@ -3429,6 +3499,10 @@ static void draw_activate(t_gobj *z, t_glist *glist,
 
 static void svg_togui(t_svg *x, t_template *template, t_word *data)
 {
+    // Hack to send parameter as an object to the GUI. Not sure yet if
+    // we want to generalize that...
+    //sys_gui(",{ "); 
+    gui_start_array();
     if (x->x_type != gensym("line"))
     {
         if (x->x_filltype)
@@ -3437,24 +3511,60 @@ static void svg_togui(t_svg *x, t_template *template, t_word *data)
             if (x->x_filltype == 1)
             {
                 t_symbol *f = fielddesc_getsymbol(fd, template, data, 1);
-                sys_vgui("-fill %s ", f->s_name);
+                //sys_vgui("-fill %s ", f->s_name);
+                //sys_vgui("fill: '%s',", f->s_name);
+                gui_string_elem("fill");
+                gui_string_elem(f->s_name);
             }
             else if (x->x_filltype == 2)
-                sys_vgui("-fill %s ", rgb_to_hex(
+            {
+                //sys_vgui("-fill %s ", rgb_to_hex(
+                //    (int)fielddesc_getfloat(fd,
+                //    template, data, 1),
+                //    (int)fielddesc_getfloat(fd+1,
+                //    template, data, 1),
+                //    (int)fielddesc_getfloat(fd+2,
+                //    template, data, 1)));
+
+                //sys_vgui("fill: '%s',", rgb_to_hex(
+                //    (int)fielddesc_getfloat(fd,
+                //    template, data, 1),
+                //    (int)fielddesc_getfloat(fd+1,
+                //    template, data, 1),
+                //    (int)fielddesc_getfloat(fd+2,
+                //    template, data, 1)));
+                gui_string_elem("fill");
+                gui_string_elem(rgb_to_hex(
                     (int)fielddesc_getfloat(fd,
                     template, data, 1),
                     (int)fielddesc_getfloat(fd+1,
                     template, data, 1),
                     (int)fielddesc_getfloat(fd+2,
                     template, data, 1)));
+            }
         }
         if (x->x_fillopacity.a_flag)
-            sys_vgui("-fillopacity %g ",
-               fielddesc_getfloat(&x->x_fillopacity.a_attr, template, data, 1));
+        {
+            //sys_vgui("-fillopacity %g ",
+            //   fielddesc_getfloat(&x->x_fillopacity.a_attr, template, data, 1));
+            //sys_vgui("fill-opacity: %g,",
+            //   fielddesc_getfloat(&x->x_fillopacity.a_attr, template, data, 1));
+            gui_string_elem("fill-opacity");
+            gui_float_elem(fielddesc_getfloat(&x->x_fillopacity.a_attr, template, data, 1));
+        }
         if (x->x_fillrule.a_flag)
-            sys_vgui("-fillrule %s ", (int)fielddesc_getfloat(
+        {
+            //sys_vgui("-fillrule %s ", (int)fielddesc_getfloat(
+            //    &x->x_fillrule.a_attr, template, data, 1) ?
+            //        "evenodd" : "nonzero");
+            //sys_vgui("'fill-rule': '%s',", (int)fielddesc_getfloat(
+            //    &x->x_fillrule.a_attr, template, data, 1) ?
+            //        "evenodd" : "nonzero");
+            gui_string_elem("fill-rule");
+            gui_string_elem((int)fielddesc_getfloat(
                 &x->x_fillrule.a_attr, template, data, 1) ?
                     "evenodd" : "nonzero");
+        }
     }
     if (x->x_stroketype)
     {
@@ -3462,46 +3572,124 @@ static void svg_togui(t_svg *x, t_template *template, t_word *data)
         if (x->x_stroketype == 1)
         {
             t_symbol *s = fielddesc_getsymbol(fd, template, data, 1);
-            sys_vgui("-stroke %s ", s->s_name);
+            //sys_vgui("-stroke %s ", s->s_name);
+            //sys_vgui("stroke: '%s',", s->s_name);
+            gui_string_elem("stroke");
+            gui_string_elem(s->s_name);
         }
         else if (x->x_stroketype == 2)
-            sys_vgui("-stroke %s ", rgb_to_hex(
+        {
+            //sys_vgui("-stroke %s ", rgb_to_hex(
+            //    (int)fielddesc_getfloat(fd,
+            //    template, data, 1),
+            //    (int)fielddesc_getfloat(fd+1,
+            //    template, data, 1),
+            //    (int)fielddesc_getfloat(fd+2,
+            //    template, data, 1)));
+            //sys_vgui("stroke: '%s',", rgb_to_hex(
+            //    (int)fielddesc_getfloat(fd,
+            //    template, data, 1),
+            //    (int)fielddesc_getfloat(fd+1,
+            //    template, data, 1),
+            //    (int)fielddesc_getfloat(fd+2,
+            //    template, data, 1)));
+
+            gui_string_elem("stroke");
+            gui_string_elem(rgb_to_hex(
                 (int)fielddesc_getfloat(fd,
                 template, data, 1),
                 (int)fielddesc_getfloat(fd+1,
                 template, data, 1),
                 (int)fielddesc_getfloat(fd+2,
                 template, data, 1)));
+        }
     }
     if (x->x_strokewidth.a_flag)
-        sys_vgui("-strokewidth %g\\\n",
-            fielddesc_getfloat(&x->x_strokewidth.a_attr, template, data, 1));
+    {
+        //sys_vgui("-strokewidth %g\\\n",
+        //    fielddesc_getfloat(&x->x_strokewidth.a_attr, template, data, 1));
+        //sys_vgui("stroke-width: %g,",
+        //    fielddesc_getfloat(&x->x_strokewidth.a_attr, template, data, 1));
+        gui_string_elem("stroke-width");
+        gui_float_elem(fielddesc_getfloat(&x->x_strokewidth.a_attr, template, data, 1));
+    }
+    if (x->x_type == gensym("circle"))
+    {
+        if (x->x_nargs > 0)
+        {
+            gui_string_elem("cx");
+            gui_float_elem(fielddesc_getfloat(&x->x_vec[0], template, data, 1));
+        }
+    }
     if (x->x_strokeopacity.a_flag)
-        sys_vgui("-strokeopacity %g ",
-            fielddesc_getfloat(&x->x_strokeopacity.a_attr, template, data, 1));
+    {
+        //sys_vgui("-strokeopacity %g ",
+        //    fielddesc_getfloat(&x->x_strokeopacity.a_attr, template, data, 1));
+        //sys_vgui("'stroke-opacity': %g,",
+        //    fielddesc_getfloat(&x->x_strokeopacity.a_attr, template, data, 1));
+
+        gui_string_elem("stroke-opacity");
+        gui_float_elem(fielddesc_getfloat(&x->x_strokeopacity.a_attr, template, data, 1));
+    }
     if (x->x_strokelinecap.a_flag)
-        sys_vgui("-strokelinecap %s ", get_strokelinecap(
+    {
+        //sys_vgui("-strokelinecap %s ", get_strokelinecap(
+        //    (int)fielddesc_getcoord(&x->x_strokelinecap.a_attr,
+        //    template, data, 1)));
+        //sys_vgui("'stroke-linecap': '%s',", get_strokelinecap(
+        //    (int)fielddesc_getcoord(&x->x_strokelinecap.a_attr,
+        //    template, data, 1)));
+
+        gui_string_elem("stroke-linecap");
+        gui_string_elem(get_strokelinecap(
             (int)fielddesc_getcoord(&x->x_strokelinecap.a_attr,
             template, data, 1)));
+    }
     if (x->x_strokelinejoin.a_flag)
-        sys_vgui("-strokelinejoin %s ", get_strokelinejoin(
+    {
+        //sys_vgui("-strokelinejoin %s ", get_strokelinejoin(
+        //    (int)fielddesc_getfloat(&x->x_strokelinejoin.a_attr,
+        //template, data, 1)));
+        //sys_vgui("'stroke-linejoin': '%s',", get_strokelinejoin(
+        //    (int)fielddesc_getfloat(&x->x_strokelinejoin.a_attr,
+        //template, data, 1)));
+
+        gui_string_elem("stroke-linejoin");
+        gui_string_elem(get_strokelinejoin(
             (int)fielddesc_getfloat(&x->x_strokelinejoin.a_attr,
         template, data, 1)));
+    }
     if (x->x_strokemiterlimit.a_flag)
-        sys_vgui("-strokemiterlimit %g ",
-            fielddesc_getfloat(&x->x_strokemiterlimit.a_attr,
+    {
+        //sys_vgui("-strokemiterlimit %g ",
+        //    fielddesc_getfloat(&x->x_strokemiterlimit.a_attr,
+        //    template, data, 1));
+        //sys_vgui("'stroke-miterlimit': %g,",
+        //    fielddesc_getfloat(&x->x_strokemiterlimit.a_attr,
+        //    template, data, 1));
+        gui_string_elem("stroke-miterlimit");
+        gui_float_elem(fielddesc_getfloat(&x->x_strokemiterlimit.a_attr,
             template, data, 1));
+    }
     if (x->x_ndash)
     {
         int i;
         t_fielddesc *fd;
-        sys_gui(" -strokedasharray {\\\n");
+        // inner array...
+        //sys_gui(" -strokedasharray {\\\n");
+        //sys_gui("'stroke-dasharray': \"");
+        gui_string_elem("stroke-dasharray");
+        gui_start_array();
         for (i = 0, fd = x->x_strokedasharray; i < x->x_ndash; i++)
         {
-            sys_vgui("%d\\\n", (int)fielddesc_getfloat(fd+i,
+            // Should this be a float?
+            //sys_vgui("%d ", (int)fielddesc_getfloat(fd+i,
+            //template, data, 1));
+            gui_int_elem((int)fielddesc_getfloat(fd+i,
             template, data, 1));
         }
-        sys_gui("}\\\n");
+        //sys_gui("\",");
+        gui_end_array();
     }
     if (x->x_transform_n > 0)
     {
@@ -3509,24 +3697,48 @@ static void svg_togui(t_svg *x, t_template *template, t_word *data)
         t_float m1, m2, m3, m4, m5, m6;
         svg_parsetransform(x, template, data, &m1, &m2, &m3,
             &m4, &m5, &m6);
-        sys_vgui("-matrix { {%g %g} {%g %g} {%g %g} }\\\n",
+        //sys_vgui("-matrix { {%g %g} {%g %g} {%g %g} }\\\n",
+        //    m1, m2, m3, m4, m5, m6);
+        gui_string_elem("transform");
+        char transbuf[MAXPDSTRING];
+        sprintf(transbuf, "matrix(%g,%g,%g,%g,%g,%g)",
             m1, m2, m3, m4, m5, m6);
+        gui_string_elem(transbuf);
     }
     if (x->x_vis.a_flag) 
     { 
-        sys_vgui("-state %s ", fielddesc_getfloat(&x->x_vis.a_attr, 
-            template, data, 1) ? "normal" : "hidden"); 
+        //sys_vgui("-state %s ", fielddesc_getfloat(&x->x_vis.a_attr, 
+        //    template, data, 1) ? "normal" : "hidden"); 
+        //sys_vgui("visibility: '%s',", fielddesc_getfloat(&x->x_vis.a_attr, 
+        //    template, data, 1) ? "visible" : "hidden"); 
+        gui_string_elem("visibility");
+        gui_string_elem(fielddesc_getfloat(&x->x_vis.a_attr, 
+            template, data, 1) ? "visible" : "hidden"); 
     }
     if (x->x_rx.a_flag)
     {
-        sys_vgui("-rx %d ", (int)fielddesc_getfloat(&x->x_rx.a_attr,
+        //sys_vgui("-rx %d ", (int)fielddesc_getfloat(&x->x_rx.a_attr,
+        //    template, data, 1));
+        //sys_vgui("rx: %d,", (int)fielddesc_getfloat(&x->x_rx.a_attr,
+        //    template, data, 1));
+        gui_string_elem("rx");
+        gui_int_elem((int)fielddesc_getfloat(&x->x_rx.a_attr,
             template, data, 1));
     }
     if (x->x_ry.a_flag)
     {
-        sys_vgui("-ry %d ", (int)fielddesc_getfloat(&x->x_ry.a_attr,
+        //sys_vgui("-ry %d ", (int)fielddesc_getfloat(&x->x_ry.a_attr,
+        //    template, data, 1));
+        //sys_vgui("ry: %d,", (int)fielddesc_getfloat(&x->x_ry.a_attr,
+        //    template, data, 1));
+        gui_string_elem("ry");
+        gui_float_elem((int)fielddesc_getfloat(&x->x_ry.a_attr,
             template, data, 1));
     }
+    gui_string_elem("display");
+    gui_string_elem("inline");
+    //sys_gui("display: 'inline'},");
+    gui_end_array();
 }
 
 void svg_grouptogui(t_glist *g, t_template *template, t_word *data)
@@ -3535,6 +3747,23 @@ void svg_grouptogui(t_glist *g, t_template *template, t_word *data)
     svg_togui(x, template, data);
 }
 
+void svg_parentwidgettogui(t_gobj *z, t_glist *owner, t_word *data,
+    t_template *template)
+{
+    if (pd_class(&z->g_pd) == draw_class)
+    {
+        t_draw *x = (t_draw *)z;
+        char tagbuf[MAXPDSTRING];
+        sprintf(tagbuf, "draw%lx.%lx", (long unsigned int)x,
+            (long unsigned int)data);
+        gui_start_vmess("gui_draw_configure_all", "ss",
+            canvas_string(glist_getcanvas(owner)), tagbuf);
+        svg_togui((t_svg *)x->x_attr, template, data);
+        gui_end_vmess();
+    }
+}
+    
+
 static void draw_vis(t_gobj *z, t_glist *glist, t_glist *parentglist,
     t_scalar *sc, t_word *data, t_template *template,
     t_float basex, t_float basey, int vis)
@@ -3608,6 +3837,15 @@ static void draw_vis(t_gobj *z, t_glist *glist, t_glist *parentglist,
                 sys_vgui(".x%lx.c create path {\\\n", glist_getcanvas(glist));
             else if (sa->x_type == gensym("circle"))
                 sys_vgui(".x%lx.c create ellipse\\\n", glist_getcanvas(glist));
+
+            /* cheap hack... there should instead be a gui_vmess style function
+               for incrementally building a message to the GUI (possibly with atom 
+               arrays) */
+            //sys_vgui("nn gui_draw_vis \".x%lx\",\"%s\",\"", glist_getcanvas(glist),
+            //    sa->x_type->s_name);
+            gui_start_vmess("gui_draw_vis", "ss", canvas_string(glist_getcanvas(glist)),
+                sa->x_type->s_name);
+
             /* next send the gui drawing arguments: commands and points
                for paths, points for everything else */
             if (sa->x_type == gensym("path"))
@@ -3616,75 +3854,142 @@ static void draw_vis(t_gobj *z, t_glist *glist, t_glist *parentglist,
                    the bbox */
                 if (sa->x_pathrect_cache != -1)
                     sa->x_pathrect_cache = 0;
-                char *cmd;
+                char *cmd, cmdbuf[2];
                 int totalpoints = 0; /* running tally */
+                gui_start_array();
                 /* path parser: no error checking yet */
                 for (i = 0, cmd = sa->x_pathcmds; i < sa->x_npathcmds; i++, cmd++)
                 {
                     int j;
                     int cargs = sa->x_nargs_per_cmd[i];
                     f = (sa->x_vec)+totalpoints;
-                    sys_vgui("%c\\\n", *(cmd));
+                    //sys_vgui("%c\\\n", *(cmd));
+                    //sys_vgui("%c ", *(cmd));
+                    sprintf(cmdbuf, "%c", *(cmd));
+                    gui_string_elem(cmdbuf);
                     for (j = 0; j < sa->x_nargs_per_cmd[i]; j++)
-                        sys_vgui("%g\\\n", fielddesc_getcoord(
-                            f+j, template, data, 1));
+                        //sys_vgui("%g\\\n", fielddesc_getcoord(
+                        //    f+j, template, data, 1));
+                        //sys_vgui("%g ", fielddesc_getcoord(f+j, template, data, 1));
+                        gui_float_elem(fielddesc_getcoord(f+j, template, data, 0));
                     totalpoints += sa->x_nargs_per_cmd[i];
                 }
-                sys_gui("}\\\n");
+                //sys_gui("}\\\n");
+                //sys_gui("\",");
+                gui_end_array();
             }
             else /* all other shapes */
             {
+                gui_start_array();
                 int nxy = n >> 1;
                 for (i = 0; i < nxy; i++)
                 {
-                    sys_vgui("%g %g\\\n", pix[2*i], pix[2*i+1]);
+                    //sys_vgui("%g %g\\\n", pix[2*i], pix[2*i+1]);
+                    //sys_vgui("%g %g ", pix[2*i], pix[2*i+1]);
+                    gui_float_elem(pix[2*i]);
+                    gui_float_elem(pix[2*i+1]);
                     if ((sa->x_type == gensym("ellipse") ||
                          sa->x_type == gensym("circle")) && n > 1)
                     {
-                        sys_vgui("-rx %d -ry %d\\\n",
-                            (int)(fielddesc_getcoord(sa->x_vec+2,
-                                template, data, 1)),
-                            (int)(fielddesc_getcoord(sa->x_vec +
+                        //sys_vgui("-rx %d -ry %d\\\n",
+                        //    (int)(fielddesc_getcoord(sa->x_vec+2,
+                        //        template, data, 1)),
+                        //    (int)(fielddesc_getcoord(sa->x_vec +
+                        //        (sa->x_type == gensym("ellipse")?
+                        //         3 : 2),
+                        //        template, data, 1)));
+                        // These should be floats...
+                        //sys_vgui("%d %d ",
+                        //    (int)(fielddesc_getcoord(sa->x_vec+2,
+                        //        template, data, 1)),
+                        //    (int)(fielddesc_getcoord(sa->x_vec +
+                        //        (sa->x_type == gensym("ellipse")?
+                        //         3 : 2),
+                        //        template, data, 1)));
+
+                        gui_float_elem(fielddesc_getcoord(sa->x_vec+2,
+                                template, data, 0));
+                        gui_float_elem(fielddesc_getcoord(sa->x_vec +
                                 (sa->x_type == gensym("ellipse")?
                                  3 : 2),
-                                template, data, 1)));
+                                template, data, 1));
                         break;
                     }
                     else if (sa->x_type == gensym("rect") && n > 1)
                     {
-                        sys_vgui("%g %g\\\n",
-                            fielddesc_getcoord(sa->x_vec,
-                                template, data, 1) +
-                                fielddesc_getcoord(sa->x_vec+2,
-                                template, data, 1),
-                            fielddesc_getcoord(sa->x_vec+1,
-                                template, data, 1) +
-                                fielddesc_getcoord(sa->x_vec+3,
-                                template, data, 1));
+                        //sys_vgui("%g %g\\\n",
+                        //    fielddesc_getcoord(sa->x_vec,
+                        //        template, data, 1) +
+                        //        fielddesc_getcoord(sa->x_vec+2,
+                        //        template, data, 1),
+                        //    fielddesc_getcoord(sa->x_vec+1,
+                        //        template, data, 1) +
+                        //        fielddesc_getcoord(sa->x_vec+3,
+                        //        template, data, 1));
+
+                        //sys_vgui("%g %g ",
+                        //        fielddesc_getcoord(sa->x_vec+2,
+                        //        template, data, 1),
+                        //        fielddesc_getcoord(sa->x_vec+3,
+                        //        template, data, 1));
+
+                        gui_float_elem(fielddesc_getcoord(sa->x_vec+2,
+                                template, data, 0));
+                        gui_float_elem(fielddesc_getcoord(sa->x_vec+3,
+                                template, data, 0));
                         break;
                     }
                     else if (sa->x_type == gensym("circle"))
                     {
-                        sys_vgui("-r %d\\\n",
-                            (t_int)fielddesc_getcoord(sa->x_vec+2,
-                                template, data, 1));
+                        //sys_vgui("-r %d\\\n",
+                        //    (t_int)fielddesc_getcoord(sa->x_vec+2,
+                        //        template, data, 1));
+                        //sys_vgui("%d ",
+                        //    (t_int)fielddesc_getcoord(sa->x_vec+2,
+                        //        template, data, 1));
+                        gui_int_elem((t_int)fielddesc_getcoord(sa->x_vec+2,
+                            template, data, 1));
                         break;
                     }
                 }
+                //sys_gui("\",");
+                gui_end_array();
             }
 
             svg_togui(sa, template, data);
 
+
+            gui_start_array();
+            char parent_tagbuf[MAXPDSTRING];
             if (in_array)
-                sys_vgui(" -parent .scelem%lx.%lx \\\n", parentglist, data);
+            {
+                //sys_vgui(" -parent .scelem%lx.%lx \\\n", parentglist, data);
+                //sys_vgui("\"scelem%lx.%lx\",", parentglist, data);
+                sprintf(parent_tagbuf, "scelem%lx.%lx", (long unsigned int)parentglist,
+                    (long unsigned int)data);
+                gui_string_elem(parent_tagbuf);
+            }
             else
-                sys_vgui(" -parent .dgroup%lx.%lx \\\n",
-                    x->x_canvas, data);
+            {
+                //sys_vgui(" -parent .dgroup%lx.%lx \\\n",
+                //sys_vgui("\"dgroup%lx.%lx\",",
+                //    x->x_canvas, data);
+                sprintf(parent_tagbuf, "dgroup%lx.%lx", (long unsigned int)x->x_canvas,
+                    (long unsigned int)data);
+                gui_string_elem(parent_tagbuf);
+            }
             /* tags - one for this scalar (not sure why the double glist thingy)
               one for this specific draw item
             */
-            sys_vgui("-tags {.x%lx.x%lx.template%lx .draw%lx.%lx}\n",
-                glist_getcanvas(glist), glist, data, x, data);
+            //sys_vgui("-tags {.x%lx.x%lx.template%lx .draw%lx.%lx}\n",
+            //    glist_getcanvas(glist), glist, data, x, data);
+
+            // Let's try to get rid of Ico's tag
+            //sys_vgui("\"draw%lx.%lx\"", x, data);
+            char tagbuf[MAXPDSTRING];
+            sprintf(tagbuf, "draw%lx.%lx", (long unsigned int)x,
+                (long unsigned int)data);
+            gui_string_elem(tagbuf);
             if (!glist_istoplevel(glist))
             {
                 t_canvas *gl = glist_getcanvas(glist);
@@ -3696,14 +4001,25 @@ static void draw_vis(t_gobj *z, t_glist *glist, t_glist *parentglist,
                 canvas_restore_original_position(gl, (t_gobj *)glist,
                     objtag, -1);
             }
+            //sys_gui(";\n");
+            gui_end_array();
+            gui_end_vmess();
         }
         else post("warning: draws need at least two points to be graphed");
     }
     else
     {
         if (n > 1)
-            sys_vgui(".x%lx.c delete .x%lx.x%lx.template%lx\n",
-                glist_getcanvas(glist), glist_getcanvas(glist), glist, data);
+        {
+//            sys_vgui(".x%lx.c delete .x%lx.x%lx.template%lx\n",
+//                glist_getcanvas(glist), glist_getcanvas(glist), glist, data);
+            char itemtagbuf[MAXPDSTRING];
+            sprintf(itemtagbuf, "draw%lx.%lx", (long unsigned int)x,
+                (long unsigned int)data);
+            gui_vmess("gui_draw_erase_item", "ss", canvas_string(glist_getcanvas(glist)),
+                itemtagbuf);
+        }
+                
     }
 }
 
diff --git a/pd/src/g_text.c b/pd/src/g_text.c
index 84a72926c28232d8ed693d88c1cbe16b30f03165..51ab8316f24849445f504b826deecbd83a725897 100644
--- a/pd/src/g_text.c
+++ b/pd/src/g_text.c
@@ -709,6 +709,9 @@ static void message_click(t_message *x,
         t_rtext *y = glist_findrtext(x->m_glist, &x->m_text);
         sys_vgui(".x%lx.c itemconfigure %sR -strokewidth 5\n", 
             glist_getcanvas(x->m_glist), rtext_gettag(y));
+        gui_vmess("gui_message_flash", "ssi",
+            canvas_string(glist_getcanvas(x->m_glist)),
+            rtext_gettag(y), 1);
         clock_delay(x->m_clock, 120);
     }
 }
@@ -720,6 +723,9 @@ static void message_tick(t_message *x)
         t_rtext *y = glist_findrtext(x->m_glist, &x->m_text);
         sys_vgui(".x%lx.c itemconfigure %sR -strokewidth 1\n",
             glist_getcanvas(x->m_glist), rtext_gettag(y));
+        gui_vmess("gui_message_flash", "ssi",
+            canvas_string(glist_getcanvas(x->m_glist)),
+            rtext_gettag(y), 0);
     }
 }
 
@@ -1559,6 +1565,8 @@ static void text_select(t_gobj *z, t_glist *glist, int state)
             sys_vgui(".x%lx.c itemconfigure %sR -stroke %s\n",
                 glist_getcanvas(glist), rtext_gettag(y),
                 (state? "$pd_colors(selection)" : outline));
+            gui_vmess("gui_text_select_color", "ss",
+                canvas_string(glist_getcanvas(glist)), rtext_gettag(y));
             if (z->g_pd == gatom_class)
             {
                 sys_vgui(".x%lx.c itemconfigure %lx.l -fill %s\n",
@@ -1578,6 +1586,8 @@ static void text_select(t_gobj *z, t_glist *glist, int state)
                 }
                 sys_vgui(".x%lx.c addtag selected withtag %sR \n",
                     glist_getcanvas(glist), rtext_gettag(y));
+                gui_vmess("gui_text_select", "ss",
+                    canvas_string(glist_getcanvas(glist)), rtext_gettag(y));
 
                 if (pd_class(&x->te_pd) == text_class && x->te_type != T_TEXT && glist_istoplevel(glist))
                     sys_vgui(".x%lx.c itemconfigure %sR -strokewidth 1 -strokedasharray {} "
@@ -1608,6 +1618,9 @@ static void text_select(t_gobj *z, t_glist *glist, int state)
                 }
                 sys_vgui(".x%lx.c dtag %sR selected\n",
                     glist_getcanvas(glist), rtext_gettag(y));
+                gui_vmess("gui_text_deselect", "ss",
+                    canvas_string(glist_getcanvas(glist)),
+                    rtext_gettag(y));
 
                 if (pd_class(&x->te_pd) == text_class && x->te_type != T_TEXT)
                        sys_vgui(".x%lx.c itemconfigure %sR -strokewidth 2  -strokelinecap projecting -strokedasharray {2 3} "
@@ -1651,6 +1664,7 @@ static void text_vis(t_gobj *z, t_glist *glist, int vis)
 {
     //fprintf(stderr,"text_vis %d\n", vis);
     t_text *x = (t_text *)z;
+    int x1, y1, x2, y2;
 
 #ifdef PDL2ORK
     //if we are in k12 mode and this is hub with level 1 (global)
@@ -1675,6 +1689,12 @@ static void text_vis(t_gobj *z, t_glist *glist, int vis)
             {
                 //fprintf(stderr,"    draw it\n");
                 t_rtext *y = glist_findrtext(glist, x);
+
+                // make a group
+                text_getrect(&x->te_g, glist, &x1, &y1, &x2, &y2);
+                gui_vmess("gui_text_create_gobj", "ssii",
+                    canvas_string(glist), rtext_gettag(y), x1, y1);
+
                 if (x->te_type == T_ATOM)
                     glist_retext(glist, x);
                 text_drawborder(x, glist, rtext_gettag(y),
@@ -1688,8 +1708,9 @@ static void text_vis(t_gobj *z, t_glist *glist, int vis)
             if (gobj_shouldvis(&x->te_g, glist))
             {
                 //fprintf(stderr,"    erase it %lx %lx\n", x, glist);
-                text_eraseborder(x, glist, rtext_gettag(y));
-                rtext_erase(y);
+                text_erase_gobj(x, glist, rtext_gettag(y));
+                //text_eraseborder(x, glist, rtext_gettag(y));
+                //rtext_erase(y);
             }
         }
 #ifdef PDL2ORK
@@ -1860,6 +1881,7 @@ static t_widgetbehavior gatom_widgetbehavior =
 void glist_drawiofor(t_glist *glist, t_object *ob, int firsttime,
     char *tag, int x1, int y1, int x2, int y2)
 {
+    t_rtext *y = glist_findrtext(glist, ob);
     //if this is a comment or we are drawing inside gop on one of
     //our parents return
     if (pd_class(&ob->te_pd) == text_class || glist_getcanvas(glist) != glist)
@@ -1886,8 +1908,12 @@ void glist_drawiofor(t_glist *glist, t_object *ob, int firsttime,
                 tag, i, tag,
                 (issignal ? "signal" : "control"),
                 (selected ? "selected" : ""));
+            gui_vmess("gui_canvas_drawio", "sssiiiiii",
+                canvas_string(glist_getcanvas(glist)), rtext_gettag(y), tag,
+                onset, y2 - 2, onset + IOWIDTH, y2, x1, y1);
         }
-        else {
+        else
+        {
             //fprintf(stderr,"glist_drawiofor o redraw\n");
             sys_vgui(".x%lx.c coords %so%d %d %d %d %d\n",
                 glist_getcanvas(glist), tag, i,
@@ -1921,8 +1947,12 @@ void glist_drawiofor(t_glist *glist, t_object *ob, int firsttime,
                 tag, i, tag,
                 (issignal ? "signal" : "control"),
                 (selected ? "selected" : ""));
+            gui_vmess("gui_canvas_drawio", "sssiiiiii",
+                canvas_string(glist_getcanvas(glist)), rtext_gettag(y), tag,
+                onset, y1, onset + IOWIDTH, y1 + EXTRAPIX, x1, y1);
         }
-        else {
+        else
+        {
             //fprintf(stderr,"glist_drawiofor i firsttime\n");
             sys_vgui(".x%lx.c coords %si%d %d %d %d %d\n",
                 glist_getcanvas(glist), tag, i,
@@ -2018,6 +2048,7 @@ void text_drawborder(t_text *x, t_glist *glist,
 
     t_object *ob;
     int x1, y1, x2, y2;
+    int broken;
 
     int selected = glist_isselected(glist, (t_gobj*)x);
 
@@ -2052,6 +2083,7 @@ void text_drawborder(t_text *x, t_glist *glist,
             outline = "$pd_colors(dash_outline) -strokewidth 2 -strokelinecap projecting -strokedasharray {2 3}"; //-strokedasharray {4 1}
             fill = invalid_fill;
             box_tag = "broken box";
+            broken = 1;
         }
         else
         {
@@ -2059,9 +2091,11 @@ void text_drawborder(t_text *x, t_glist *glist,
             outline = "$pd_colors(box_border)";
             fill = "$pd_colors(box)";
             box_tag = "box";
+            broken = 0;
         }
         if (firsttime)
         {
+            /*
             sys_vgui(".x%lx.c create ppolygon %d %d %d %d %d %d %d %d %d %d \
                     -stroke %s -fill %s -tags {%sR %lx text %s %s}\n", 
                     glist_getcanvas(glist),
@@ -2069,6 +2103,10 @@ void text_drawborder(t_text *x, t_glist *glist,
                     (selected ? "$pd_colors(selection)" : outline),
                     fill, tag, tag, box_tag,
                     (selected ? "selected" : ""));
+            */
+            gui_vmess("gui_text_drawborder", "ssiiiii",
+                canvas_string(glist_getcanvas(glist)), tag, broken, x1, y1, x2, y2);
+               
                 //-dash %s -> pattern disabled for tkpath
         }
         else
@@ -2078,6 +2116,8 @@ void text_drawborder(t_text *x, t_glist *glist,
             sys_vgui(".x%lx.c coords %sR %d %d %d %d %d %d %d %d %d %d\n",
                 glist_getcanvas(glist), tag,
                     x1, y1,  x2, y1,  x2, y2,  x1, y2,  x1, y1);
+            gui_vmess("gui_text_redraw_border", "ssiiii",
+                canvas_string(glist_getcanvas(glist)), tag, x1, y1, x2, y2);
 /* this seems to be totally extraneous  hans@at.or.at
              sys_vgui(".x%lx.c itemconfigure %sR -dash %s -outline %s\n", 
                      glist_getcanvas(glist), tag, pattern, outline); */
@@ -2086,6 +2126,8 @@ void text_drawborder(t_text *x, t_glist *glist,
     else if (x->te_type == T_MESSAGE)
     {
         if (firsttime)
+        {
+            /*
             sys_vgui(".x%lx.c create ppolygon "
                      "%d %d %d %d %d %d %d %d %d %d %d %d %d %d "
                      "-stroke %s -fill $pd_colors(msg) "
@@ -2095,16 +2137,32 @@ void text_drawborder(t_text *x, t_glist *glist,
                 x1, y2,  x1, y1,
                 (selected ? "$pd_colors(selection)" : "$pd_colors(msg_border)"),
                     tag, tag, (selected ? "selected" : ""));
+            */
+
+            // coords are a quick hack to separate gobj's x/y from the polygon's coords
+            // which of course can be greatly simplified
+            gui_vmess("gui_message_drawborder", "ssii",
+                canvas_string(glist_getcanvas(glist)), tag, x2 - x1, y2 - y1);
+//                x1-x1, y1-y1, x2+4-x1, y1-y1, x2-x1,
+//                y1+4-y1, x2-x1, y2-4-y1, x2+4-x1, y2-y1, x1-x1, y2-y1, x1-x1, y1-y1);
+        }
         else
+        {
             sys_vgui(".x%lx.c coords %sR "
                      "%d %d %d %d %d %d %d %d %d %d %d %d %d %d\n",
                 glist_getcanvas(glist), tag,
                 x1, y1,  x2+4, y1,  x2, y1+4,  x2, y2-4,  x2+4, y2,
                 x1, y2,  x1, y1);
+            gui_vmess("gui_message_redraw_border", "ssiiiiiiiiiiiiii",
+                canvas_string(glist_getcanvas(glist)), tag,
+                x1-x1, y1-y1,  x2+4-x1, y1-y1,  x2-x1, y1+4-y1, x2-x1, y2-4-y1,  x2+4-x1, y2-y1,
+                x1-x1, y2-y1,  x1-x1, y1-y1);
+        }
     }
     else if (x->te_type == T_ATOM)
     {
         if (firsttime)
+        {
             sys_vgui(".x%lx.c create ppolygon "
                      "%d %d %d %d %d %d %d %d %d %d %d %d "
                      "-stroke %s "
@@ -2114,11 +2172,19 @@ void text_drawborder(t_text *x, t_glist *glist,
                 x1, y1,  x2-4, y1,  x2, y1+4,  x2, y2,  x1, y2,  x1, y1,
                 (selected ? "$pd_colors(selection)" : "$pd_colors(atom_box_border)"),
                     tag, tag, (selected ? "selected" : ""));
+
+            gui_vmess("gui_atom_drawborder", "ssiiiiiiiiiiii",
+                canvas_string(glist_getcanvas(glist)), tag,
+                x1-x1, y1-y1, x2-4-x1, y1-y1, x2-x1,
+                y1+4-y1, x2-x1, y2-y1, x1-x1, y2-y1, x1-x1, y1-y1);
+        }
         else
+        {
             sys_vgui(".x%lx.c coords %sR "
                      "%d %d %d %d %d %d %d %d %d %d %d %d\n",
                 glist_getcanvas(glist), tag,
                 x1, y1,  x2-4, y1,  x2, y1+4,  x2, y2,  x1, y2,  x1, y1);
+        }
     }
         /* for comments, just draw a dotted rectangle unlocked; when a visible
         canvas is unlocked we have to call this anew on all comments, and when
@@ -2274,6 +2340,13 @@ void glist_eraseiofor(t_glist *glist, t_object *ob, char *tag)
             glist_getcanvas(glist), tag, i);
 }
 
+// erase the whole gobj in the gui one go
+void text_erase_gobj(t_text *x, t_glist *glist, char *tag)
+{
+    if (x->te_type == T_TEXT && !glist->gl_edit) return;
+    gui_vmess("gui_gobj_erase", "ss", canvas_string(glist_getcanvas(glist)), tag);
+}
+
 void text_eraseborder(t_text *x, t_glist *glist, char *tag)
 {
     if (x->te_type == T_TEXT && !glist->gl_edit) return;
diff --git a/pd/src/g_toggle.c b/pd/src/g_toggle.c
index bb8b3beb1fb86be9ec568f31f85befcaf943f48b..f91af52f49af9f933b6b38841cb1d7b07eb6d1c1 100644
--- a/pd/src/g_toggle.c
+++ b/pd/src/g_toggle.c
@@ -30,6 +30,10 @@ void toggle_draw_update(t_gobj *xgobj, t_glist *glist)
                      (x->x_on!=0.0)?x->x_gui.x_fcol:x->x_gui.x_bcol);
             sys_vgui(".x%lx.c itemconfigure %lxX2 -stroke #%6.6x\n", canvas, x,
                      (x->x_on!=0.0)?x->x_gui.x_fcol:x->x_gui.x_bcol);
+            char tagbuf[MAXPDSTRING];
+            sprintf(tagbuf, "x%lx", (long unsigned int)x);
+            gui_vmess("gui_toggle_update", "ssi", canvas_string(canvas),
+                tagbuf, x->x_on != 0.0);
         }
         x->x_gui.x_changed = 0;
     }
@@ -37,12 +41,15 @@ void toggle_draw_update(t_gobj *xgobj, t_glist *glist)
 
 void toggle_draw_new(t_toggle *x, t_glist *glist)
 {
+    char tagbuf[MAXPDSTRING], colorbuf[MAXPDSTRING];
+    sprintf(tagbuf, "x%lx", (long unsigned int)x);
     t_canvas *canvas=glist_getcanvas(glist);
     int w=(x->x_gui.x_w+29)/30;
     int x1=text_xpix(&x->x_gui.x_obj, glist);
     int y1=text_ypix(&x->x_gui.x_obj, glist);
     int x2=x1+x->x_gui.x_w, y2=y1+x->x_gui.x_h;
     int col = (x->x_on!=0.0)?x->x_gui.x_fcol:x->x_gui.x_bcol;
+    sprintf(colorbuf, "#%6.6x", x->x_gui.x_fcol);
 
     iemgui_base_draw_new(&x->x_gui);
     sys_vgui(".x%lx.c create polyline %d %d %d %d -strokewidth %d "
@@ -51,6 +58,10 @@ void toggle_draw_new(t_toggle *x, t_glist *glist)
     sys_vgui(".x%lx.c create polyline %d %d %d %d -strokewidth %d "
         "-stroke #%6.6x -tags {%lxX2 x%lx text iemgui}\n",
         canvas, x1+w+1, y2-w-1, x2-w-1, y1+w+1, w, col, x, x);
+    gui_vmess("gui_create_toggle", "sssiiiiiiiiiii", canvas_string(canvas), tagbuf,
+        colorbuf, w,
+        x1+w+1, y1+w+1, x2-w-1, y2-w-1,
+        x1+w+1, y2-w-1, x2-w-1, y1+w+1, x1, y1);
 }
 
 void toggle_draw_move(t_toggle *x, t_glist *glist)
diff --git a/pd/src/g_traversal.c b/pd/src/g_traversal.c
index 49488dd169d8074a249aa7fbacf32935014769d8..4a29f7416c66dabc1005dc5bc130c5a35ab6b6ca 100644
--- a/pd/src/g_traversal.c
+++ b/pd/src/g_traversal.c
@@ -582,6 +582,8 @@ static void set_set(t_set *x, t_symbol *templatesym, t_symbol *field)
     }
 }
 
+static void scalar_configure(t_scalar *x, t_glist *owner);
+
 static void set_bang(t_set *x)
 {
     int nitems = x->x_nin, i;
@@ -618,7 +620,7 @@ static void set_bang(t_set *x)
     else for (i = 0, vp = x->x_variables; i < nitems; i++, vp++)
         template_setfloat(template, vp->gv_sym, vec, vp->gv_w.w_float, 1);
     if (gs->gs_which == GP_GLIST)
-        scalar_redraw((t_scalar *)(gp->gp_un.gp_gobj), gs->gs_un.gs_glist);  
+        scalar_configure((t_scalar *)(gp->gp_un.gp_gobj), gs->gs_un.gs_glist);  
     else
     {
         t_array *owner_array = gs->gs_un.gs_array;
diff --git a/pd/src/g_vumeter.c b/pd/src/g_vumeter.c
index fd42b1e0b493b704762ad94387a2033ef6f966b8..d86fbc31f140072b306645694449407c4d1799e4 100644
--- a/pd/src/g_vumeter.c
+++ b/pd/src/g_vumeter.c
@@ -71,11 +71,18 @@ static void vu_update_rms(t_vu *x, t_glist *glist)
     {
         int w4 = x->x_gui.x_w / 4, off=text_ypix(&x->x_gui.x_obj, glist) - 1;
         int x1 = text_xpix(&x->x_gui.x_obj, glist),
+            y1 = text_ypix(&x->x_gui.x_obj, glist),
             quad1 = x1 + w4 + 1, quad3 = x1 + x->x_gui.x_w-w4 - 1;
 
         sys_vgui(".x%lx.c coords %lxRCOVER %d %d %d %d\n",
             glist_getcanvas(glist), x, quad1 + 1, off + 2, quad3 + 1,
             off + (x->x_led_size + 1) * (IEM_VU_STEPS - x->x_rms) + 2);
+        char tagbuf[MAXPDSTRING];
+        sprintf(tagbuf, "x%lx", (long unsigned int)x);
+        gui_vmess("gui_vumeter_update_rms", "ssiiiiii",
+            canvas_string(glist_getcanvas(glist)),
+            tagbuf, quad1 + 1, off + 2, quad3 + 1, off + (x->x_led_size + 1) *
+            (IEM_VU_STEPS - x->x_rms) + 2, x1, y1);
     }
 }
 
@@ -96,6 +103,12 @@ static void vu_update_peak(t_vu *x, t_glist *glist)
 
             sys_vgui(".x%lx.c coords %lxPLED %d %d %d %d\n",
                 canvas, x, x1 + 1, j + 2, x1 + x->x_gui.x_w + 2, j + 2);
+            char tagbuf[MAXPDSTRING];
+            sprintf(tagbuf, "x%lx", (long unsigned int)x);
+            char colorbuf[MAXPDSTRING];
+            sprintf(colorbuf, "#%6.6x", iemgui_color_hex[i]);
+            gui_vmess("gui_vumeter_update_peak", "sssiiiiii", canvas_string(canvas),
+                tagbuf, colorbuf, x1 + 1, j + 2, x1 + x->x_gui.x_w + 2, j + 2, x1, y1);
             sys_vgui(".x%lx.c itemconfigure %lxPLED -stroke #%6.6x\n",
                 canvas, x, iemgui_color_hex[i]);
         }
@@ -141,28 +154,56 @@ static void vu_draw_new(t_vu *x, t_glist *glist)
     {
         yyy = k4 + k1 * (k2-i);
         if((i&3)==1 && (x->x_scale))
+        {
             sys_vgui(".x%lx.c create text %d %d -text {%s} -anchor w "
                 "-font %s -fill #%6.6x "
                 "-tags {%lxSCALEN %lxSCALE%d x%lx text iemgui}\n",
                 canvas, end+1, yyy+k3+2, iemgui_vu_scale_str[i/4], 
                 iemgui_font(&x->x_gui), x->x_gui.x_lcol, x, x, i, x);
-
+            char tagbuf[MAXPDSTRING];
+            sprintf(tagbuf, "x%lx", (long unsigned int)x);
+            char colorbuf[MAXPDSTRING];
+            sprintf(colorbuf, "#%6.6x", x->x_gui.x_lcol);
+            // not handling font size yet
+            gui_vmess("gui_create_vumeter_text", "sssiisiii", canvas_string(canvas),
+                tagbuf, colorbuf, end+1, yyy+k3+2, iemgui_vu_scale_str[i/4],
+                i, x1, y1);
+        }
         led_col = iemgui_vu_col[i];
-        if (i<=IEM_VU_STEPS) sys_vgui(".x%lx.c create polyline %d %d %d %d "
+        if (i<=IEM_VU_STEPS)
+        {
+            sys_vgui(".x%lx.c create polyline %d %d %d %d "
             "-strokewidth %d -stroke #%6.6x "
             "-tags {%lxRLED%d x%lx text iemgui}\n",
             canvas, quad1+1, yyy+2, quad3, yyy+2,
             x->x_led_size, iemgui_color_hex[led_col], x, i, x);
+            char tagbuf[MAXPDSTRING];
+            sprintf(tagbuf, "x%lx", (long unsigned int)x);
+            char colorbuf[MAXPDSTRING];
+            sprintf(colorbuf, "#%6.6x", iemgui_color_hex[led_col]);
+            gui_vmess("gui_create_vumeter_steps", "sssiiiiiiii",
+                canvas_string(canvas), tagbuf, colorbuf, quad1+1,
+                yyy+2, quad3, yyy+2, x->x_led_size, index, x1, y1);
+        }
     }
     sys_vgui(".x%lx.c create prect %d %d %d %d -fill #%6.6x "
         "-stroke #%6.6x -tags {%lxRCOVER x%lx text iemgui}\n",
         canvas, quad1+1, y1+1, quad3, y1+1 + k1*IEM_VU_STEPS,
         x->x_gui.x_bcol, x->x_gui.x_bcol, x, x);
+    char tagbuf[MAXPDSTRING];
+    sprintf(tagbuf, "x%lx", (long unsigned int)x);
+    char colorbuf[MAXPDSTRING];
+    sprintf(colorbuf, "#%6.6x", x->x_gui.x_bcol);
+    gui_vmess("gui_create_vumeter_rect", "sssiiiiii", canvas_string(canvas),
+        tagbuf, colorbuf, quad1+1, y1+1, quad3, y1+1 + k1*IEM_VU_STEPS, x1, y1);
     sys_vgui(".x%lx.c create polyline %d %d %d %d "
         "-strokewidth %d -fill #%6.6x "
         "-tags {%lxPLED x%lx text iemgui}\n",
         canvas, mid+1, y1+12,
         mid+1, y1+12, x->x_led_size, x->x_gui.x_bcol, x, x);
+    sprintf(colorbuf, "#%6.6x", x->x_gui.x_bcol);
+    gui_vmess("gui_create_vumeter_peak", "sssiiiiiii", canvas_string(canvas),
+        tagbuf, colorbuf, mid+1, y1+12, mid+1, y1+12, x->x_led_size, x1, y1);
     x->x_updaterms = x->x_updatepeak = 1;
     sys_queuegui(x, x->x_gui.x_glist, vu_draw_update);
 }
diff --git a/pd/src/m_pd.h b/pd/src/m_pd.h
index d36eee4e08578466b17b73495a0a1f5efa136ec4..1d939d6c49dc3ee28cc8ea6d435f8df0328ecb49 100644
--- a/pd/src/m_pd.h
+++ b/pd/src/m_pd.h
@@ -667,6 +667,15 @@ EXTERN void sys_vvguid(const char *file, int line, const char *fmt, va_list);
 EXTERN void sys_gui(const char *s);
 #define sys_vgui(args...) sys_vguid(__FILE__,__LINE__,args)
 #define sys_gui(s)        sys_vguid(__FILE__,__LINE__,"%s",s)
+EXTERN void gui_vmess(const char *sel, char *fmt, ...);
+/* some more gui interfaces for building incremental messages */
+EXTERN void gui_start_vmess(const char *sel, char *fmt, ...);
+EXTERN void gui_start_array(void);
+EXTERN void gui_float_elem(t_float f);
+EXTERN void gui_int_elem(int i);
+EXTERN void gui_string_elem(const char *s);
+EXTERN void gui_end_array(void);
+EXTERN void gui_end_vmess(void);
 
 EXTERN void sys_pretendguibytes(int n);
 EXTERN void sys_queuegui(void *client, t_glist *glist, t_guicallbackfn f);
diff --git a/pd/src/s_audio.c b/pd/src/s_audio.c
index 1d658800025f4d158c6c16ad1423cc4cc128ec54..33586c2b20c6229d1f743124678c8f697c0a7783 100644
--- a/pd/src/s_audio.c
+++ b/pd/src/s_audio.c
@@ -501,7 +501,7 @@ void sys_reopen_audio( void)
         sys_audioapiopened = sys_audioapi;
         audio_callback_is_open = callback;
     }
-    sys_vgui("set pd_whichapi %d\n",  (outcome == 0 ? sys_audioapi : 0));
+    gui_vmess("set_audioapi", "i",  (outcome == 0 ? sys_audioapi : 0));
 }
 
 int sys_send_dacs(void)
diff --git a/pd/src/s_inter.c b/pd/src/s_inter.c
index 78f115ee842ad9ebc4b3fa479689c912126bb5e6..2ae0a991218d0a63f220c1a6a35dee4ca7e0de13 100644
--- a/pd/src/s_inter.c
+++ b/pd/src/s_inter.c
@@ -752,6 +752,124 @@ void sys_gui(const char *s)
     sys_vgui("%s", s);
 }
 
+char *escape_double_quotes(const char *src) {
+    static char ret[MAXPDSTRING*2+1];
+    char *escaped = ret; 
+    int i, len = MAXPDSTRING*2;
+    for (i = 0; i < len-1 && src[i] != 0; i++)
+    {
+        if (src[i] == '\"')
+            *(escaped++) = '\\'; 
+        *(escaped++) = src[i];
+    }
+    *escaped = '\0';
+    if (i >= len) fprintf(stderr, "oops, you caught me statically allocating a string "
+                       "in a lazy attempt to escape double-quotes for the gui. "
+                       "Please call me out publicly for this regression, and/or revise this "
+                       "so it doesn't arbitrarily limit the size of the strings "
+                       "we can send to the gui.");
+    return ret;
+}
+
+/* quick hack to send a parameter list for use as a function call in
+   Node-Webkit */
+void gui_do_vmess(const char *sel, char *fmt, int end, va_list ap)
+{
+    //va_list ap;
+    int nargs = 0;
+    char *fp = fmt;
+
+    //va_start(ap, end);
+    sys_vgui("nn %s ", sel);
+    while (*fp)
+    {
+        // stop-gap-- increase to 20 until we find a way to pass a list or array
+        if (nargs >= 20)
+        {
+            error("sys_gui_vmess: only 10 named parameters allowed");
+            break;
+        }
+        if (nargs > 0) sys_gui(",");
+        switch(*fp++)
+        {
+        case 'f': sys_vgui("%g", va_arg(ap, double)); break;
+        case 's': sys_vgui("\"%s\"", escape_double_quotes(va_arg(ap, const char *))); break;
+        case 'i': sys_vgui("%d", va_arg(ap, t_int)); break;
+        //case 'p': SETPOINTER(at, va_arg(ap, t_gpointer *)); break;
+        default: goto done;
+        }
+        nargs++;
+    }
+done:
+    va_end(ap);
+    if (end)
+        sys_gui(";\n");
+}
+
+void gui_vmess(const char *sel, char *fmt, ...)
+{
+    va_list ap;
+    va_start(ap, fmt);
+    gui_do_vmess(sel, fmt, 1, ap);
+}
+
+void gui_start_vmess(const char *sel, char *fmt, ...)
+{
+    va_list ap;
+    va_start(ap, fmt);
+    gui_do_vmess(sel, fmt, 0, ap);
+}
+
+static int gui_array_head;
+void gui_start_array(void)
+{
+    gui_array_head = 1;
+    sys_gui(",[");
+}
+
+void gui_float_elem(t_float f)
+{
+    if (gui_array_head)
+    {
+        gui_array_head = 0;
+        sys_vgui("%g", f);
+    }
+    else
+        sys_vgui(",%g", f);
+}
+
+void gui_int_elem(int i)
+{
+    if (gui_array_head)
+    {
+        gui_array_head = 0;
+        sys_vgui("%d", i);
+    }
+    else
+        sys_vgui(",%d", i);
+}
+
+void gui_string_elem(const char *s) 
+{
+    if (gui_array_head)
+    {
+        gui_array_head = 0;
+        sys_vgui("\"%s\"", escape_double_quotes(s));
+    }
+    else
+        sys_vgui(",\"%s\"", escape_double_quotes(s));
+}
+
+void gui_end_array(void)
+{
+    sys_gui("]");
+}
+
+void gui_end_vmess(void)
+{
+    sys_gui(";\n");
+}
+
 int sys_flushtogui( void)
 {
     int writesize = sys_guibufhead - sys_guibuftail, nwrote = 0;
@@ -805,6 +923,7 @@ static int sys_flushqueue(void )
         if (sys_bytessincelastping >= GUI_BYTESPERPING)
         {
             sys_gui("pdtk_ping\n");
+            gui_vmess("gui_ping", "");
             sys_bytessincelastping = 0;
             sys_waitingforping = 1;
             return (1);
@@ -1145,6 +1264,16 @@ int sys_startgui(const char *guidir)
                 "TCL_LIBRARY=\"%s/tcl/library\" TK_LIBRARY=\"%s/tk/library\" \
                  \"%s/pd-gui\" %d localhost %s\n",
                  sys_libdir->s_name, sys_libdir->s_name, guidir, portno, (sys_k12_mode ? "pd-l2ork-k12" : "pd-l2ork"));
+
+/* SUPERHACK - let's just load node-webkit and see what happens */
+            sprintf(cmdbuf,
+                  "/home/nu/Downloads/nwjs-v0.12.0-alpha2-linux-ia32/nw "
+//                "/home/user/Downloads/node-webkit-v0.11.6-linux-ia32/nw "
+//                  "/home/user/Downloads/atom/atom/atom "
+                "/home/nu/Downloads/test/ %d localhost %s\n",
+                portno,
+                (sys_k12_mode ? "pd-l2ork-k12" : "pd-l2ork"));
+
 #endif
             sys_guicmd = cmdbuf;
         }
@@ -1179,6 +1308,9 @@ int sys_startgui(const char *guidir)
                 }
             }
 #endif
+
+            fprintf(stderr, "fuck %s", sys_guicmd);
+
             execl("/bin/sh", "sh", "-c", sys_guicmd, (char*)0);
             perror("pd: exec");
             _exit(1);
@@ -1332,12 +1464,19 @@ int sys_startgui(const char *guidir)
             /* here is where we start the pinging. */
 #if defined(__linux__) || defined(IRIX)
          if (sys_hipriority)
-             sys_gui("pdtk_watchdog\n");
+             gui_vmess("gui_watchdog", "");
 #endif
          sys_get_audio_apis(buf);
          sys_get_midi_apis(buf2);
-         sys_vgui("pdtk_pd_startup {%s} %s %s {%s} %s\n", pd_version, buf, buf2, 
-                  sys_font, sys_fontweight); 
+//         sys_vgui("pdtk_pd_startup {%s} %s %s {%s} %s\n", pd_version, buf, buf2, 
+//                  sys_font, sys_fontweight); 
+
+        gui_vmess("gui_startup", "sssss",
+		  pd_version,
+		  buf,
+		  buf2,
+		  sys_font,
+		  sys_fontweight);
     }
     return (0);
 
@@ -1387,7 +1526,8 @@ void glob_quit(void *dummy)
     canvas_suspend_dsp();
     do_not_redraw = 1;
     glob_closeall(0, 1);
-    sys_vgui("exit\n");
+    //sys_vgui("exit\n");
+    gui_vmess("app_quit", "");
     if (!sys_nogui)
     {
         sys_closesocket(sys_guisock);
diff --git a/pd/src/s_main.c b/pd/src/s_main.c
index 1949d76b1d0db431c0b8165dfbd07bb5e9205d06..1336b0a01994f3553253a865d312f28bf7aa22b8 100644
--- a/pd/src/s_main.c
+++ b/pd/src/s_main.c
@@ -309,36 +309,38 @@ int sys_main(int argc, char **argv)
         // let's create one continuous string from all files
         int length = 0;
         t_namelist *nl;
-        for (nl = sys_openlist; nl; nl = nl->nl_next)
-        {
+//        for (nl = sys_openlist; nl; nl = nl->nl_next)
+//        {
             // for starting and ending quotes plus a space or null terminating
             // character, we add 3 additional characters per entry
-            length = length + strlen(nl->nl_string) + 3;
-        }
-        if(length && (filenames = (char*) calloc(length, sizeof(char*)) ) != NULL)
+//            length = length + strlen(nl->nl_string) + 3;
+//        }
+//        if(length && (filenames = (char*) calloc(length, sizeof(char*)) ) != NULL)
+        if (1)
         {
-            strcat(filenames,"\"");
+//            strcat(filenames,"\\\"");
             if (sys_openlist)
             {
                 for (nl = sys_openlist; nl; nl = nl->nl_next)
                 {
-                    strcat(filenames,nl->nl_string);
-                    if (nl->nl_next)
-                        strcat(filenames,"\" \"");
-                    else strcat(filenames,"\"\0"); // ensures proper termination
+                    gui_vmess("gui_build_filelist", "s", nl->nl_string);
+//                    strcat(filenames,nl->nl_string);
+//                    if (nl->nl_next)
+//                        strcat(filenames,"\\\" \\\"");
+//                    else strcat(filenames,"\\\"\\\0"); // ensures proper termination
                 }
             }
             //fprintf(stderr,"final list: <%s> <%c> %d\n", filenames, filenames[0], length);
         }
         else
         {
-            error("filelist malloc failed!\n");
-            return(1);
+//            error("filelist malloc failed!\n");
+//            return(1);
         }
     }
-    sys_vgui("pdtk_check_unique %d {%s}\n", sys_unique,
-        (filenames ? filenames : "0"));
-    if (filenames != NULL) free(filenames);
+    gui_vmess("gui_check_unique", "i", sys_unique);
+//        (filenames ? filenames : "0"));
+//    if (filenames != NULL) free(filenames);
     if (sys_externalschedlib)
         return (sys_run_scheduler(sys_externalschedlibname,
             sys_extraflagsstring));
diff --git a/pd/src/s_midi.c b/pd/src/s_midi.c
index c850097c264e7354180dc7ace12db93dbca9f686..e9e6ac0bda46c3a3fbfac69182243fbbf57386f4 100644
--- a/pd/src/s_midi.c
+++ b/pd/src/s_midi.c
@@ -565,7 +565,7 @@ void sys_open_midi(int nmidiindev, int *midiindev,
     sys_save_midi_params(nmidiindev, midiindev,
         nmidioutdev, midioutdev);
 
-    sys_vgui("set pd_whichmidiapi %d\n", sys_midiapi);
+    gui_vmess("set_midiapi", "i", sys_midiapi);
 
 }
 
diff --git a/pd/src/s_print.c b/pd/src/s_print.c
index 8df706ae0bd6c5e472361de99efe1c17303f3bd2..620711a5198d1459d398577af34c506f14d3f284 100644
--- a/pd/src/s_print.c
+++ b/pd/src/s_print.c
@@ -65,6 +65,10 @@ static void doerror(const void *object, const char *s)
         sys_vgui("pdtk_posterror {%s} 1 {%s}\n",
             strnpointerid(obuf, object, MAXPDSTRING),
             strnescape(upbuf, s, MAXPDSTRING));
+        gui_vmess("gui_post_error", "sis",
+            strnpointerid(obuf, object, MAXPDSTRING),
+            1,
+            strnescape(upbuf, s, MAXPDSTRING));
     }
 }
 
@@ -118,7 +122,8 @@ static void dopost(const char *s)
         //if (ptout && upbuf[ptout-1] == '\n')
         //    upbuf[--ptout] = 0, heldcr = 1;
         upbuf[ptout] = 0;
-        sys_vgui("pdtk_post {%s}\n", upbuf);
+//        sys_vgui("pdtk_post {%s}\n", upbuf);
+        gui_vmess("gui_post", "s", upbuf);
     }
 }