diff --git a/pd/nw/css/c64.css b/pd/nw/css/c64.css
index abb0cc816834f4a6a55b48206d91739753f5db97..62c3c54d9926626dc0ae3039d3d5b5413f16b338 100644
--- a/pd/nw/css/c64.css
+++ b/pd/nw/css/c64.css
@@ -124,7 +124,8 @@ mark.console_find_highlighted {
 
 /* selected connection between objects */
 .cord.signal.selected_line,
-.cord.control.selected_line {
+.cord.control.selected_line,
+#newcord {
     stroke: #cc9933;
 }
 
diff --git a/pd/nw/css/default.css b/pd/nw/css/default.css
index 0864425328cea236a724256ab68e61932c1757b5..da5b7ad6d899dfb3713ab621aa5f5db4802aa9d4 100644
--- a/pd/nw/css/default.css
+++ b/pd/nw/css/default.css
@@ -20,6 +20,13 @@ body {
     font-family: "DejaVu Sans Mono";
 }
 
+/* delete the default scrollbars, and let's make our own */
+body::-webkit-scrollbar {
+    width: 0px;
+    height: 0px;
+    background: transparent;
+}
+
 .noselect {
     -webkit-touch-callout: none;
     -webkit-user-select: none;
@@ -228,9 +235,10 @@ mark.console_find_highlighted {
     stroke: #565;
 }
 
-/* selected connection between objects */
+/* selected connection between objects, or a new cord not yet connected */
 .cord.signal.selected_line,
-.cord.control.selected_line {
+.cord.control.selected_line,
+#newcord {
     stroke: #e87216;
 }
 
@@ -387,7 +395,7 @@ text {
 
 #patchsvg.editmode .comment .border {
     stroke: #aaa;
-    stroke-dasharray: 8 4;
+    stroke-dasharray: 3 2;
 }
 
 /* A little hack for special case of [cnv].
@@ -425,21 +433,22 @@ text {
 
 /* text inside selected objects */
 :not(.gop).selected text {
-    fill: blue;
+    fill: #e87216;
 }
 
 /* for an object that didn't create */
 .obj .border.broken_border {
-    fill: #f7f7f7;
+    fill: #f7d7d7;
     stroke: #f00;
-    stroke-dasharray: 3 2;
+    stroke-width: 2;
+    stroke-dasharray: 6 2;
 }
 
 /* control inlet */
 .xlet_control {
     stroke: #777;
     fill: white;
-//    stroke-width: 1;
+    stroke-width: 1;
 }
 
 /* signal inlet */
@@ -458,7 +467,7 @@ text {
 
 /* text label for an iemgui */
 .iemgui_label_selected {
-    fill: blue;
+    fill: #e87216;
 }
 
 /* test of xlet hover animation... this should 
@@ -474,19 +483,26 @@ text {
         rx: 1;
         ry: 1;
     }
-    100% {
-        stroke-width: 20;
-        stroke-opacity: 0.2;
-        rx: 50;
-        ry: 50;
+    33% {
+        stroke-width: 12;
+        stroke-opacity: 1;
+        rx: 1;
+        ry: 1;
+    }
+    66% {
+        stroke-width: 5;
+        stroke-opacity: 1;
+        rx: 1;
+        ry: 1;
     }
 }
 
 /* can't remember why this was tagged !important */
 .xlet_selected {
-    stroke: orange !important;
-    fill: orange;
-    -webkit-animation: fizzle 0.4s linear 1;
+    stroke: #e87216;
+    fill: #e87216;
+    stroke-width: 5;
+    -webkit-animation: fizzle 0.4s ease-in 1;
 }
 
 #canvas_find {
diff --git a/pd/nw/css/inverted.css b/pd/nw/css/inverted.css
index cfd662a755dd5ecc0a3c447b074dc08a4a236e0e..8e3510bd04f062cd2e495a99e72c3c83398e4a1c 100644
--- a/pd/nw/css/inverted.css
+++ b/pd/nw/css/inverted.css
@@ -135,7 +135,8 @@ mark.console_find_highlighted {
 
 /* selected connection between objects */
 .cord.signal.selected_line,
-.cord.control.selected_line {
+.cord.control.selected_line,
+#newcord {
     stroke: blue;
 }
 
diff --git a/pd/nw/css/solarized.css b/pd/nw/css/solarized.css
index 3b2e4b836c902baad81e6f0f690e86fc39a398ab..45aff43aa09cf773089ca76bb2d8aedffa2ce2e1 100644
--- a/pd/nw/css/solarized.css
+++ b/pd/nw/css/solarized.css
@@ -128,7 +128,8 @@ mark.console_find_highlighted {
 
 /* selected connection between objects */
 .cord.signal.selected_line,
-.cord.control.selected_line {
+.cord.control.selected_line,
+#newcord {
     stroke: #268bd2;
 }
 
diff --git a/pd/nw/css/solarized_inverted.css b/pd/nw/css/solarized_inverted.css
index 63a64fb063cbf54d561c878c4981fced8e903c5b..834fff642ad4f11ba266b941b2d5e9714dee905f 100644
--- a/pd/nw/css/solarized_inverted.css
+++ b/pd/nw/css/solarized_inverted.css
@@ -128,7 +128,8 @@ mark.console_find_highlighted {
 
 /* selected connection between objects */
 .cord.signal.selected_line,
-.cord.control.selected_line {
+.cord.control.selected_line,
+#newcord {
     stroke: #b58900;
 }
 
diff --git a/pd/nw/css/strongbad.css b/pd/nw/css/strongbad.css
index dbc601349ce4641c889a8cb717c7ba5b1d487111..3251fc431bf73951e65dac358b5919790ddf93d5 100644
--- a/pd/nw/css/strongbad.css
+++ b/pd/nw/css/strongbad.css
@@ -125,7 +125,8 @@ mark.console_find_highlighted {
 
 /* selected connection between objects */
 .cord.signal.selected_line,
-.cord.control.selected_line {
+.cord.control.selected_line,
+#newcord {
     stroke: #53b83b;
 }
 
diff --git a/pd/nw/css/subdued.css b/pd/nw/css/subdued.css
index 5a364bbc17c075e3cf2836d6780ea6644663113d..a0a9e3dd69f6764eacd98224834816c516d43a35 100644
--- a/pd/nw/css/subdued.css
+++ b/pd/nw/css/subdued.css
@@ -125,7 +125,8 @@ mark.console_find_highlighted {
 
 /* selected connection between objects */
 .cord.signal.selected_line,
-.cord.control.selected_line {
+.cord.control.selected_line,
+#newcord {
     stroke: blue;
 }
 
diff --git a/pd/nw/index.js b/pd/nw/index.js
index 6e9da613c3d48c96ffb10ddc1ee07eee7a9542da..0361f595a9f7488770f1f81f4ffc2654d448a438 100644
--- a/pd/nw/index.js
+++ b/pd/nw/index.js
@@ -408,7 +408,8 @@ function nw_create_window(cid, type, width, height, xpos, ypos, attr_array) {
         // the window.  Ideally we would just get rid of the canvas menu
         // altogether to simplify things. But we'd have to add some kind of
         // widget for the "Put" menu.
-        height: height + 23,
+        // ico@vt.edu: on 0.46.2 this is now 25, go figure...
+        height: height + 25,
         x: xpos,
         y: ypos
     }, function (new_win) {
diff --git a/pd/nw/pd_canvas.html b/pd/nw/pd_canvas.html
index 5b6da1ff2d362252e9e38dbd5d5a75b087490679..6c2753b8775a52ec9c7360ae64f55b05aefb0956 100644
--- a/pd/nw/pd_canvas.html
+++ b/pd/nw/pd_canvas.html
@@ -76,6 +76,8 @@
         </button>
       </div>
     </dialog>
+	<div id="hscroll" style="background-color: #00000044; position: fixed; left: 2px; bottom: 2px; border-radius: 0px; width: 10px; height: 5px; visibility: hidden;"></div>
+	<div id="vscroll" style="background-color: #00000044; position: fixed; right: 2px; top: 2px; border-radius: 0px; width: 5px; height: 10px; visibility: hidden;"></div>
     <script type="text/javascript" src="./pd_canvas.js"></script>
   </body>
 </html>
diff --git a/pd/nw/pd_canvas.js b/pd/nw/pd_canvas.js
index d2fb09295342f414dc8ab45e2d84e14cc51c0cad..6009838e427c97d726d8208a2125975d93411035 100644
--- a/pd/nw/pd_canvas.js
+++ b/pd/nw/pd_canvas.js
@@ -233,6 +233,13 @@ var canvas_events = (function() {
                 return false;
             },
             mousedown: function(evt) {
+                // ico@vt.edu capture middle click for a different type of scroll
+                // currently disabled due to problem with scrollBy and zoom
+                /*if (evt.which == 2)
+                {
+                    evt.stopPropagation();
+                    evt.preventDefault();                
+                }*/
                 var target_id, resize_type;
                 if (target_is_scrollbar(evt)) {
                     return;
@@ -521,6 +528,64 @@ var canvas_events = (function() {
                 }
                 canvas_events[canvas_events.get_previous_state()]();
             },
+            hscroll_mouseup: function(evt) {
+                canvas_events[canvas_events.get_previous_state()]();
+            },
+            hscroll_mousemove: function(evt) {
+                if (evt.movementX != 0) {
+                    //console.log("move: " + e.movementX);
+                    
+                    var hscroll = document.getElementById("hscroll");
+                    var svg_elem = document.getElementById("patchsvg");
+                    
+                    var min_width = document.body.clientWidth + 3;
+                    var width = svg_elem.getAttribute('width');
+                    var xScrollSize;
+                    
+                    xScrollSize = hscroll.offsetWidth;
+                  
+                    var xTranslate = evt.movementX *
+                        ((width - min_width)/(min_width - xScrollSize)) *
+                        (evt.movementX > 0 ? 1 : 0.75);
+                    if (xTranslate > 0 && xTranslate < 1) {
+                        xTranslate = 1;
+                    }
+                    if (xTranslate < 0 && xTranslate > -1) {
+                        xTranslate = -1;
+                    }
+                    //console.log(xTranslate);
+                    window.scrollBy(xTranslate, 0);
+                }
+            },
+            vscroll_mouseup: function(evt) {
+                canvas_events[canvas_events.get_previous_state()]();
+            },
+            vscroll_mousemove: function(evt) {
+                if (evt.movementY != 0) {
+                    //console.log("move: " + e.movementY);
+                    
+                    var vscroll = document.getElementById("vscroll");
+                    var svg_elem = document.getElementById("patchsvg");
+                    
+                    var min_height = document.body.clientHeight + 3;
+                    var height = svg_elem.getAttribute('height');
+                    var yScrollSize;
+                    
+                    yScrollSize = vscroll.offsetHeight;
+                  
+                    var yTranslate = evt.movementY *
+                        ((height - min_height)/(min_height - yScrollSize)) *
+                        (evt.movementY > 0 ? 2 : 1.5);
+                    if (yTranslate > 0 && yTranslate < 1) {
+                        yTranslate = 1;
+                    }
+                    if (yTranslate < 0 && yTranslate > -1) {
+                        yTranslate = -1;
+                    }
+                    //console.log(yTranslate);
+                    window.scrollBy(0, yTranslate);
+                }
+            },
             dropdown_menu_keydown: function(evt) {
                 var select_elem = document.querySelector("#dropdown_list"),
                     li;
@@ -729,6 +794,16 @@ var canvas_events = (function() {
             document.addEventListener("mouseup",
                 events.iemgui_label_mouseup, false);
         },
+        hscroll_drag: function() {
+            canvas_events.none();
+            document.addEventListener("mouseup", events.hscroll_mouseup, false);
+            document.addEventListener("mousemove", events.hscroll_mousemove, false);
+        },
+        vscroll_drag: function() {
+            canvas_events.none();
+            document.addEventListener("mouseup", events.vscroll_mouseup, false);
+            document.addEventListener("mousemove", events.vscroll_mousemove, false);
+        },
         text: function() {
             canvas_events.none();
 
@@ -770,6 +845,9 @@ var canvas_events = (function() {
             document.addEventListener("keydown", events.find_keydown, false);
             state = "search";
         },
+        update_scrollbars: function() {
+            pdgui.gui_update_scrollbars(name);
+        },
         register: function(n) {
             name = n;
         },
@@ -887,6 +965,13 @@ var canvas_events = (function() {
                     console.log("tried to save something");
                 }, false
             );
+                       
+            // add listener for the scrollbars
+            document.getElementById("hscroll").
+                addEventListener("mousedown", canvas_events.hscroll_drag, false);
+            document.getElementById("vscroll").
+                addEventListener("mousedown", canvas_events.vscroll_drag, false);
+            
             // Whoa-- huge workaround! Right now we're getting
             // the popup menu the way Pd Vanilla does it:
             // 1) send a mouse(down) message to Pd
@@ -1030,15 +1115,16 @@ var canvas_events = (function() {
             gui.Window.get().on("maximize", function() {
                 pdgui.gui_canvas_get_scroll(name);
             });
-            gui.Window.get().on("unmaximize", function() {
+            gui.Window.get().on("restore", function() {
                 pdgui.gui_canvas_get_scroll(name);
             });
             gui.Window.get().on("resize", function() {
+                pdgui.post("resize");
                 pdgui.gui_canvas_get_scroll(name);
             });
             gui.Window.get().on("focus", function() {
                 nw_window_focus_callback(name);
-            });
+            });            
             gui.Window.get().on("blur", function() {
                 nw_window_blur_callback(name);
             });
@@ -1047,6 +1133,12 @@ var canvas_events = (function() {
                 pdgui.pdsend(name, "setbounds", x, y,
                     x + w.width, y + w.height);
             });
+            
+            // map onscroll event
+            document.addEventListener("scroll", function() {
+                pdgui.gui_update_scrollbars(name);
+            });
+            
             // set minimum window size
             gui.Window.get().setMinimumSize(150, 100);
         }
@@ -1514,6 +1606,7 @@ function nw_create_patch_window_menus(gui, w, name) {
         click: function() {
             var win = gui.Window.get();
             win.toggleFullscreen();
+            pdgui.gui_canvas_get_scroll(name);
         }
     });
 
diff --git a/pd/nw/pdgui.js b/pd/nw/pdgui.js
index c9caafb9da4d6f105f7c0b71c0a527c54542888c..4d6d79472e51766ebc956e5558bfae7e91a4242a 100644
--- a/pd/nw/pdgui.js
+++ b/pd/nw/pdgui.js
@@ -814,16 +814,23 @@ function canvas_check_geometry(cid) {
     var win_w = patchwin[cid].width,
         // "23" is a kludge to account for the menubar size.  See comment
         // in nw_create_window of index.js
-        win_h = patchwin[cid].height - 23,
+        // ico@vt.edu in 0.46.2 this is now 25 pixels, so I guess
+        // it is now officially kludge^2
+        win_h = patchwin[cid].height - 25,
         win_x = patchwin[cid].x,
         win_y = patchwin[cid].y,
         cnv_width = patchwin[cid].window.innerWidth,
-        cnv_height = patchwin[cid].window.innerHeight - 23;
+        cnv_height = patchwin[cid].window.innerHeight - 25;
     // We're reusing win_x and win_y below, as it
     // shouldn't make a difference to the bounds
-    // algorithm in Pd
+    // algorithm in Pd (ico@vt.edu: this is not true anymore)
+    //post("relocate " + pd_geo_string(cnv_width, cnv_height, win_x, win_y) + " " +
+    //       pd_geo_string(cnv_width, cnv_height, win_x, win_y));
+    // ico@vt.edu: replaced first pd_geo_string's first two args (originally
+    // win_x and win_y with cnv_width and cnv_height + 25 to ensure the window
+    // reopens exactly how it was saved)
     pdsend(cid, "relocate",
-           pd_geo_string(win_w, win_h, win_x, win_y),
+           pd_geo_string(cnv_width, cnv_height + 25, win_x, win_y),
            pd_geo_string(cnv_width, cnv_height, win_x, win_y)
     );
 }
@@ -1412,7 +1419,8 @@ var scroll = {},
     last_focused, // last focused canvas (doesn't include Pd window or dialogs)
     loading = {},
     title_queue= {}, // ugly kluge to work around an ugly race condition
-    popup_menu = {};
+    popup_menu = {},
+    toplevel_scalars = {};
 
     var patchwin = {}; // object filled with cid: [Window object] pairs
     var dialogwin = {}; // object filled with did: [Window object] pairs
@@ -1574,9 +1582,13 @@ function create_window(cid, type, width, height, xpos, ypos, attr_array) {
 }
 
 // create a new canvas
-function gui_canvas_new(cid, width, height, geometry, zoom, editmode, name, dir, dirty_flag, hide_scroll, hide_menu, cargs) {
+function gui_canvas_new(cid, width, height, geometry, zoom, editmode, name, dir, dirty_flag, hide_scroll, hide_menu, has_toplevel_scalars, cargs) {
     // hack for buggy tcl popups... should go away for node-webkit
     //reset_ctrl_on_popup_window
+    
+    // ico@vt.edu: added has_toplevel_scalars, which is primarily
+    // being used for detecting toplevel garrays but may need to be
+    // expanded to also deal with scalars
 
     // local vars for window-specific behavior
     // visibility of menu and scrollbars, plus canvas background
@@ -1597,6 +1609,7 @@ function gui_canvas_new(cid, width, height, geometry, zoom, editmode, name, dir,
     redo[cid] = false;
     font[cid] = 10;
     doscroll[cid] = 0;
+    toplevel_scalars[cid] = has_toplevel_scalars;
     // 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)
@@ -2604,6 +2617,10 @@ function gui_gobj_select(cid, tag) {
 function gui_gobj_deselect(cid, tag) {
     gui(cid).get_gobj(tag, function(e) {
         e.classList.remove("selected");
+        // ico@vt.edu: check for scroll in case the handle disappears
+        // during deselect. LATER: make handles always fit inside the
+        // object, so this won't be necessary
+        gui_canvas_get_scroll(cid);
     });
 }
 
@@ -3430,15 +3447,88 @@ function gui_mycanvas_coords(cid, tag, vis_width, vis_height, select_width, sele
     });
 }
 
+/* this creates a group immediately below the patchsvg object */
 function gui_scalar_new(cid, tag, isselected, t1, t2, t3, t4, t5, t6,
-    is_toplevel) {
+    is_toplevel, plot_style) {
     var g;
     // we should probably use gui_gobj_new here, but we"re doing some initial
     // scaling that normal gobjs don't need...
+    //post("gui_scalar_new " + t1 + " " + t2 +
+    //    " " + t3 + " " + t4 + " " + t5 + " " + t6);
+        
+    /* ico@vt.edu HACKTASCTIC: calculating scrollbars is throwing 0.997 for
+       plots drawn inside the subpatch and it is a result of the -1 in the 
+       (min_width - 1) / width call inside canvas_params. Yet, if we don't
+       call this, we don't have nice flush scrollbars with the regular edges.
+       This is why here we make a hacklicious hack and simply hide hscrollbar
+       since the scroll is not doing anything anyhow.
+       
+       After further testing, it seems that the aforesaid margin is a hit'n'miss
+       depending on the patch, so we will disable this and make the aforesaid
+       canvas_params equation min_width / width.
+
+    if (is_toplevel === 1) {
+        gui(cid).get_elem("hscroll", function(elem) {
+            elem.style.setProperty("display", "none");
+        });
+        gui(cid).get_elem("vscroll", function(elem) {
+            elem.style.setProperty("display", "none");
+        });
+    }*/
+      
     gui(cid).get_elem("patchsvg", function(svg_elem) {
         var matrix, transform_string, selection_rect;
-        matrix = [t1,t2,t3,t4,t5,t6];
-        transform_string = "matrix(" + matrix.join() + ")";
+        if (is_toplevel === 1) {
+            // here we deal with weird scrollbar offsets and
+            // inconsistencies for the various plot styles.
+            // the matrix format is xscale, 0, 0, yscale, width, height
+            // we don't use the matrix for the bar graph since it is
+            // difficult to get the right ratio, so we do the manual
+            // translate and scale instead.
+            // cases are: 0=points, 1=plot, 2=bezier, 3=bars
+            switch (plot_style) {
+                case 0:
+                    matrix = [t1,t2,t3,t4,t5,t6+0.5];
+                    break;
+                case 1:
+                    matrix = [t1,t2,t3,t4,t5,t6+1.5];
+                    break;
+                case 2:
+                    matrix = [t1,t2,t3,t4,t5,t6+1.5];
+                    break;
+                case 3:
+                    //matrix = [t1*.995,t2,t3,t4+1,t5+0.5,t6-2];
+                    matrix = 0;
+                    transform_string = "translate(" + 0 +
+                        "," + (t6+1) + ") scale(" + t1 + "," + t4 + ")";
+                    //post("transform_string = " + transform_string);
+                    break;                  
+            }
+        }        
+        else {
+            switch (plot_style) {
+                case 0:
+                    matrix = [t1,t2,t3,t4,t5,t6+0.5];
+                    break;
+                case 1:
+                    matrix = [t1,t2,t3,t4,t5,t6+1.5];
+                    break;
+                case 2:
+                    matrix = [t1,t2,t3,t4,t5,t6+1.5];
+                    break;
+                case 3:
+                    //matrix = [t1,t2,t3,t4+1,t5+0.5,t6+0.5];
+                    matrix = 0;
+                    transform_string = "translate(" + (t5+(t1 < 1 ? 0.5 : 1.5)) +
+                        "," + (t6+1) + ") scale(" + t1 + "," + t4 + ")";
+                    //post("transform_string = " + transform_string);
+                    break;
+            }
+        }
+        
+        if (matrix !== 0) {
+            transform_string = "matrix(" + matrix.join() + ")";
+        }
         g = create_item(cid, "g", {
             id: tag + "gobj",
             transform: transform_string,
@@ -3520,6 +3610,7 @@ function gui_draw_vis(cid, type, attr_array, tag_array) {
     gui(cid).get_elem(tag_array[0])
     .append(function(frag) {
         var item;
+        post("gui_draw_vis cid=" + cid + " type=" + type + " attr_array=" + attr_array);
         attr_array.push("id", tag_array[1]);
         item = create_item(cid, type, attr_array);
         frag.appendChild(item);
@@ -3621,6 +3712,7 @@ function gui_draw_configure(cid, tag, attr, val) {
 // the default behavior.
 function gui_draw_viewbox(cid, tag, attr, val) {
     // Value will be an empty array if the user provided no values
+    post("gui_draw_viewbox cid=" + cid + " tag=" + tag + " attr=" + attr + " val=" + val);
     gui(cid).get_elem("patchsvg", function(svg_elem) {
         if (val.length) {
             gui_draw_configure(cid, tag, attr, val)
@@ -4680,8 +4772,9 @@ function zoom_kludge(zoom_level) {
 function gui_canvas_popup(cid, xpos, ypos, canprop, canopen, isobject) {
     // Get page coords for top of window, in case we're scrolled
     gui(cid).get_nw_window(function(nw_win) {
-        var win_left = nw_win.window.document.body.scrollLeft,
-            win_top = nw_win.window.document.body.scrollTop,
+        // ico@vt.edu updated win_left and win_top for the 0.46.2
+        var win_left = nw_win.window.scrollX,
+            win_top = nw_win.window.scrollY,
             zoom_level = nw_win.zoomLevel, // these were used to work
             zfactor,                       // around an old nw.js popup pos
                                            // bug. Now it's only necessary
@@ -5746,11 +5839,64 @@ function gui_undo_menu(cid, undo_text, redo_text) {
     });
 }
 
+function zoom_level_to_chrome_percent(nw_win) {
+    var zoom = nw_win.zoomLevel;
+    switch (zoom) {
+        case -7:
+            zoom = 4;
+            break;
+        case -6:
+            zoom = 100/33;
+            break;
+        case -5:
+            zoom = 2;
+            break;
+        case -4:
+            zoom = 100/67;
+            break;
+        case -3:
+            zoom = 100/75;
+            break;
+        case -2:
+            zoom = 100/80;
+            break;
+        case -1:
+            zoom = 100/90;
+            break;
+        case 0:
+            zoom = 1;
+            break;
+        case 1:
+            zoom = 100/110;
+            break;
+        case 2:
+            zoom = 100/125;
+            break;
+        case 3:
+            zoom = 100/150;
+            break;
+        case 4:
+            zoom = 100/175;
+            break;
+        case 5:
+            zoom = 100/200;
+            break;
+        case 6:
+            zoom = 100/250;
+            break;
+        case 7:
+            zoom = 100/300;
+            break;  
+    }
+    return zoom;
+}
+
 // leverages the get_nw_window method in the callers...
 function canvas_params(nw_win)
 {
     // calculate the canvas parameters (svg bounding box and window geometry)
     // for do_getscroll and do_optimalzoom
+    //post("nw_win=" + nw_win + " " + nw_win.window + " " + nw_win.window.document);
     var bbox, width, height, min_width, min_height, x, y, svg_elem;
     svg_elem = nw_win.window.document.getElementById("patchsvg");
     bbox = svg_elem.getBBox();
@@ -5764,22 +5910,18 @@ function canvas_params(nw_win)
     x = bbox.x > 0 ? 0 : bbox.x,
     y = bbox.y > 0 ? 0 : bbox.y;
 
-    // The svg "overflow" attribute on an <svg> seems to be buggy-- for example,
-    // you can't trigger a mouseover event for a <rect> that is outside of the
-    // explicit bounds of the svg.
-    // To deal with this, we want to set the svg width/height to always be
-    // at least as large as the browser's viewport. There are a few ways to
-    // do this this, like documentElement.clientWidth, but window.innerWidth
-    // seems to give the best results.
-    // However, there is either a bug or some strange behavior regarding
-    // the viewport size: setting both the height and width of an <svg> to
-    // the viewport height/width will display the scrollbars. The height or
-    // width must be set to 4 less than the viewport size in order to keep
-    // the scrollbars from appearing. Here, we just subtract 4 from both
-    // of them. This could lead to some problems with event handlers but I
-    // haven't had a problem with it yet.
-    min_width = nw_win.window.innerWidth - 4;
-    min_height = nw_win.window.innerHeight - 4;
+    // ico@vt.edu: adjust body width and height to match patchsvg to ensure
+    // scrollbars only come up when we are indeed inside svg and not before
+    // with extra margins around. This is accurate to a pixel on nw 0.47.0.
+    // This is also needed when maximizing and restoring the window in order
+    // to trigger resizing of scrollbars.
+    min_width = nw_win.window.innerWidth;
+    min_height = nw_win.window.innerHeight;
+    
+    var body_elem = nw_win.window.document.body;
+    body_elem.style.width = min_width + "px";
+    body_elem.style.height = min_height + "px";
+
     // Since we don't do any transformations on the patchsvg,
     // let's try just using ints for the height/width/viewBox
     // to keep things simple.
@@ -5789,11 +5931,78 @@ function canvas_params(nw_win)
     min_height |= 0;
     x |= 0;
     y |= 0;
+
+    /* ico@vt.edu: now let's draw/update our own scrollbars, so that we
+       don't have to deal with the window size nonsense caused by the
+       built-in ones... */  
+    // zoom var is used to compensate for the zoom level and keep
+    // the scrollbars the same height
+    var zoom = zoom_level_to_chrome_percent(nw_win);
+    var yScrollSize, yScrollTopOffset;
+    var vscroll = nw_win.window.document.getElementById("vscroll");
+    yScrollSize = min_height / height; // used to be (min_height - 1) / height
+    yScrollTopOffset = Math.floor((nw_win.window.scrollY / height) * (min_height + 3));
+    
+    // yScrollSize reflects the amount of the patch we currently see,
+    // so if it drops below 1, that means we need our scrollbars 
+    if (yScrollSize < 1) {
+        var yHeight = Math.floor(yScrollSize * (min_height + 3));
+        vscroll.style.setProperty("height", (yHeight - 6) + "px");
+        vscroll.style.setProperty("top", (yScrollTopOffset + 2) + "px");
+        vscroll.style.setProperty("-webkit-clip-path",
+            "polygon(0px 0px, 5px 0px, 5px " + (yHeight - 6) +
+            "px, 0px " + (yHeight - 11) + "px, 0px 5px)");
+        vscroll.style.setProperty("width", (5 * zoom) + "px");
+        vscroll.style.setProperty("right", (2 * zoom) + "px");
+        vscroll.style.setProperty("visibility", "visible");
+    } else {
+        vscroll.style.setProperty("visibility", "hidden");
+    }
+    
+    var xScrollSize, xScrollLeftOffset;
+    var hscroll = nw_win.window.document.getElementById("hscroll");
+    xScrollSize = min_width / width; // used to be (min_width - 1) / width
+    xScrollLeftOffset = Math.floor((nw_win.window.scrollX / width) * (min_width + 3));
+
+    if (xScrollSize < 1) {
+        var xWidth = Math.floor(xScrollSize * (min_width + 3));
+        hscroll.style.setProperty("width", (xWidth - 6) + "px");
+        hscroll.style.setProperty("left", (xScrollLeftOffset + 2) + "px");
+        hscroll.style.setProperty("-webkit-clip-path",
+            "polygon(0px 0px, " + (xWidth - 11) + "px 0px, " +
+            (xWidth - 6) + "px 5px, 0px 5px)");
+        hscroll.style.setProperty("height", (5 * zoom) + "px");
+        hscroll.style.setProperty("bottom", (2 * zoom) + "px");
+        hscroll.style.setProperty("visibility", "visible");
+    } else {
+        hscroll.style.setProperty("visibility", "hidden");    
+    }
+    
+    //post("x=" + xScrollSize + " y=" + yScrollSize);
+    
     return { x: x, y: y, w: width, h: height,
              mw: min_width, mh: min_height };
 }
 
-function do_getscroll(cid) {
+
+// ico@vt.edu:
+// the timeout is a bad hack and does not solve the problem consistently
+// even on a fast computer, while also slowing down the overall user 
+// experience. As such, ti is disabled and left here for reference.
+/*var pd_getscroll_var = {};
+
+function pd_do_getscroll(cid) {
+    if (!pd_getscroll_var[cid]) {
+        pd_getscroll_var[cid] = setTimeout(function() {
+            do_getscroll(cid, 0);
+            pd_getscroll_var[cid] = null;
+        }, 250);
+    }
+}
+
+exports.pd_do_getscroll = pd_do_getscroll;*/
+
+function do_getscroll(cid, checkgeom) {
     // Since we're throttling these getscroll calls, they can happen after
     // the patch has been closed. We remove the cid from the patchwin
     // object on close, so we can just check to see if our Window object has
@@ -5801,6 +6010,11 @@ function do_getscroll(cid) {
     // This is an awfully bad pattern. The whole scroll-checking mechanism
     // needs to be rethought, but in the meantime this should prevent any
     // errors wrt the rendering context disappearing.
+    //post("do_getscroll " + checkgeom);
+    if (checkgeom == 1) {
+        canvas_check_geometry(cid);
+        return;
+    }
     gui(cid).get_nw_window(function(nw_win) {
         var svg_elem = nw_win.window.document.getElementById("patchsvg");
         var { x: x, y: y, w: width, h: height,
@@ -5824,7 +6038,11 @@ function do_getscroll(cid) {
     });
 }
 
+exports.do_getscroll = do_getscroll;
+
 var getscroll_var = {};
+var checkgeom_and_getscroll_var = {};
+var overriding_getscroll_var = {};
 
 // We use a setTimeout here for two reasons:
 // 1. nw.js has a nasty Renderer bug  when you try to modify the
@@ -5839,16 +6057,51 @@ var getscroll_var = {};
 //    graphics from displaying until the user releases the mouse,
 //    which would be a buggy UI
 function gui_canvas_get_scroll(cid) {
+    //post("win=" + cid);
+    //win_width = win.style.width;
+    //win_height = win.style.height;
+    if (toplevel_scalars[cid]) {
+        // we have scalars, so let's override the previous call
+        // because this will be a cpu intensive redraw, so we
+        // should do it only when the action that requested it
+        // is either done or stopped for long enough for the
+        // recalculation to happen
+        if (getscroll_var[cid]) {
+            clearTimeout(getscroll_var[cid]);
+            getscroll_var[cid] = null;
+        }
+    }
     if (!getscroll_var[cid]) {
-        getscroll_var[cid] = setTimeout(function() {
-            do_getscroll(cid);
+        getscroll_var[cid] = setTimeout(function() {              
+            do_getscroll(cid, toplevel_scalars[cid]);
             getscroll_var[cid] = null;
-        }, 250);
+        }, 50);
     }
 }
 
 exports.gui_canvas_get_scroll = gui_canvas_get_scroll;
 
+/* ico@vt.edu: here is one alternative getscroll call, it focuses on
+   overriding the previous call, so the getscroll is more delayed. This
+   is useful when manipulating a plot with a mouse, for instance, so that
+   we prevent excessive getscroll calls which can be rather cpu intensive.
+*/
+
+function gui_canvas_get_overriding_scroll(cid) {
+    //post("win=" + cid);
+    //win_width = win.style.width;
+    //win_height = win.style.height;
+    if (overriding_getscroll_var[cid]) {
+        clearTimeout(overriding_getscroll_var[cid]);
+    }
+    overriding_getscroll_var[cid] = setTimeout(function() {
+        do_getscroll(cid, 0);
+        overriding_getscroll_var[cid] = null;
+    }, 100);
+}
+
+exports.gui_canvas_get_overriding_scroll = gui_canvas_get_overriding_scroll;
+
 function do_optimalzoom(cid, hflag, vflag) {
     // determine an optimal zoom level that makes the entire patch fit within
     // the window
@@ -6012,3 +6265,73 @@ function gui_pddplink_open(filename, dir) {
         post("pddplink: error: file not found: " + filename);
     }
 }
+
+
+/* ico@vt.edu: this function is run when we scroll with a mouse wheel,
+   a touchpad (e.g. two-finger scroll), or some other HID. It is
+   linked from the pd_canvas.js and called from the garray_fittograph 1
+   call when we are resizing the plot toplevel window to avoid race condition.
+*/
+function gui_update_scrollbars(cid) {
+    //post("gui_update_scrollbars " + cid);
+    gui(cid).get_nw_window(function(nw_win) {
+        var hscroll = nw_win.window.document.getElementById("hscroll");
+        var vscroll = nw_win.window.document.getElementById("vscroll");
+        var svg_elem = nw_win.window.document.getElementById("patchsvg");
+        
+        if (vscroll.style.visibility == "visible")
+        {
+            var height, min_height;  
+            min_height = nw_win.window.innerHeight + 3;
+            height = svg_elem.getAttribute('height');
+            
+            var yScrollSize, yScrollTopOffset;
+            yScrollSize = (min_height - 4) / height;
+            yScrollTopOffset = Math.floor((nw_win.window.scrollY / height) * min_height);
+            
+            if (yScrollSize < 1) {
+                var yHeight = Math.floor(yScrollSize * min_height);
+                vscroll.style.setProperty("height", (yHeight - 6) + "px");
+                vscroll.style.setProperty("top", (yScrollTopOffset + 2) + "px");
+                vscroll.style.setProperty("-webkit-clip-path",
+                    "polygon(0px 0px, 5px 0px, 5px " + (yHeight - 6) +
+                    "px, 0px " + (yHeight - 11) + "px, 0px 5px)");
+                vscroll.style.setProperty("visibility", "visible");
+            } else {
+                vscroll.style.setProperty("visibility", "hidden");    
+            }
+        }
+
+        if (hscroll.style.visibility == "visible")
+        {
+            var min_width = nw_win.window.innerWidth + 3;
+            var width = svg_elem.getAttribute('width');
+            var xScrollSize, xScrollTopOffset;
+            
+            xScrollSize = (min_width - 4) / width;
+            xScrollTopOffset = Math.floor((nw_win.window.scrollX / width) * min_width);
+            
+            /* console.log("win_width=" + min_width + " bbox=" +
+                width + " xScrollSize=" + (xScrollSize * min_width) +
+                " topOffset=" + xScrollTopOffset); */
+
+            if (xScrollSize < 1) {
+                var xWidth = Math.floor(xScrollSize * min_width);
+                hscroll.style.setProperty("width", (xWidth - 6) + "px");
+                hscroll.style.setProperty("left", (xScrollTopOffset + 2) + "px");
+                hscroll.style.setProperty("-webkit-clip-path",
+                    "polygon(0px 0px, " + (xWidth - 11) + "px 0px, " +
+                    (xWidth - 6) + "px 5px, 0px 5px)");
+                hscroll.style.setProperty("visibility", "visible");
+            } else {
+                hscroll.style.setProperty("visibility", "hidden");    
+            }
+        }
+        // for future reference
+        //nw_win.document.getElementById("hscroll").
+        //    style.setProperty("visibility", "visible");
+        //console.log("width="+width);
+    });
+}
+
+exports.gui_update_scrollbars = gui_update_scrollbars;
diff --git a/pd/src/g_all_guis.c b/pd/src/g_all_guis.c
index f5c4e9dcd3b59f7a6ebb40b24a7a27f86ff3aaa7..1723a986bde9f9b1ce6e3f3dbed473dc3b960c74 100644
--- a/pd/src/g_all_guis.c
+++ b/pd/src/g_all_guis.c
@@ -987,8 +987,8 @@ void iemgui_label_draw_new(t_iemgui *x)
 {
     char col[8];
     t_canvas *canvas=glist_getcanvas(x->x_glist);
-    int x1=text_xpix(&x->x_obj, x->x_glist)+x->legacy_x;
-    int y1=text_ypix(&x->x_obj, x->x_glist)+x->legacy_y;
+    int x1=text_xpix(&x->x_obj, x->x_glist) + (sys_legacy ? x->legacy_x : 0);
+    int y1=text_ypix(&x->x_obj, x->x_glist) + (sys_legacy ? x->legacy_y : 0);
     iemgui_getrect_legacy_label(x, &x1, &y1);
     sprintf(col, "#%6.6x", x->x_lcol);
     gui_vmess("gui_iemgui_label_new", "xxiissssi",
@@ -1018,8 +1018,8 @@ void iemgui_label_draw_move(t_iemgui *x)
     gui_vmess("gui_iemgui_label_coords", "xxii",
         canvas,
         x,
-        x->x_ldx + x->legacy_x,
-        x->x_ldy + x->legacy_y);
+        x->x_ldx + (sys_legacy ? x->legacy_x : 0),
+        x->x_ldy + (sys_legacy ? x->legacy_y : 0));
 }
 
 void iemgui_label_draw_config(t_iemgui *x)
diff --git a/pd/src/g_canvas.c b/pd/src/g_canvas.c
index d3ed51dab44bf74937fcc6d2a66a9f6b9094c246..1cf072df1179d239037484ed6ace020fb5da397c 100644
--- a/pd/src/g_canvas.c
+++ b/pd/src/g_canvas.c
@@ -713,16 +713,38 @@ void canvas_args_to_string(char *namebuf, t_canvas *x)
         strcpy(namebuf, " (");
         for (i = 0; i < env->ce_argc; i++)
         {
-            if (strlen(namebuf) > MAXPDSTRING/2 - 5)
+            if (strlen(namebuf) > MAXPDSTRING / 2 - 5)
                 break;
             if (i != 0)
                 strcat(namebuf, " ");
-            atom_string(&env->ce_argv[i], namebuf + strlen(namebuf), 
-                MAXPDSTRING/2);
+            atom_string(&env->ce_argv[i], namebuf + strlen(namebuf),
+                MAXPDSTRING / 2);
         }
         strcat(namebuf, ")");
     }
-    else namebuf[0] = 0;
+    else
+    {
+        namebuf[0] = 0;
+        t_gobj *g = NULL;
+        t_garray *a = NULL;
+        t_symbol *arrayname;
+        int found = 0, res;
+        for (g = x->gl_list; g; g = g->g_next)
+        {
+
+            if (pd_class(&g->g_pd) == garray_class)
+            {
+                res = garray_getname((t_garray *)g, &arrayname);
+                if (found)
+                {
+                    strcat(namebuf, " ");
+                }
+                strcat(namebuf, arrayname->s_name);
+                found++;
+                //post("found=%d %s %s", found, arrayname->s_name, namebuf);
+            }
+        }
+    }
 }
 
 void canvas_reflecttitle(t_canvas *x)
@@ -1314,8 +1336,8 @@ static void canvas_relocate(t_canvas *x, t_symbol *canvasgeom,
         < 4 ||
         sscanf(topgeom->s_name, "%dx%d+%d+%d", &tw, &th, &txpix, &typix) < 4)
         bug("canvas_relocate");
-            /* for some reason this is initially called with cw=ch=1 so
-            we just suppress that here. */
+    /* for some reason this is initially called with cw=ch=1 so
+    we just suppress that here. */
     if (cw > 5 && ch > 5)
         canvas_dosetbounds(x, txpix, typix,
             txpix + cw, typix + ch);
@@ -1325,24 +1347,44 @@ static void canvas_relocate(t_canvas *x, t_symbol *canvasgeom,
     t_array *a = NULL;
     int  num_elem = 0;
 
+    //int found_garray = 0;
+
     for (g = x->gl_list; g; g = g->g_next)
     {
         //fprintf(stderr, "searching\n");
-
+        //post("searching");
         //for subpatch garrays
         if (pd_class(&g->g_pd) == garray_class)
         {
             //fprintf(stderr,"found ya\n");
+            //post("found ya");
             ga = (t_garray *)g;
             if (ga)
             {
                 a = garray_getarray(ga);
                 num_elem = a->a_n;
                 garray_fittograph(ga, num_elem, 1);
+                //found_garray = 1;
             }
         }
     }
     canvas_checkconfig(x);
+
+    // ico@vt.edu:
+    // Here we update only scrollbars to avoid race condition
+    // caused by doing gui_canvas_get_scroll which in turn
+    // calls canvas_relocate to ensure garrays in subpatches
+    // are properly updated when those windows are resized.
+    // given that the scroll update will happen likely faster
+    // than the return value from the backend, we do this to
+    // get rid of the stale scrollbars, e.g. when making the
+    // window smaller (at first the scrollbars are there because
+    // the garray has not been redrawn yet, and then we update
+    // scrollbars once again here below.
+    //if (found_garray == 1) {
+        //post("found garray");
+    gui_vmess("do_getscroll", "xi", x, 0);
+    //}
 }
 
 void canvas_popabstraction(t_canvas *x)
diff --git a/pd/src/g_editor.c b/pd/src/g_editor.c
index 92f03ded89ea76c2a5548cf642d390e52e35b28d..4258618a15b8734d44d8161909cf814af658c5b7 100644
--- a/pd/src/g_editor.c
+++ b/pd/src/g_editor.c
@@ -172,7 +172,7 @@ static void canvas_nlet_conf (t_canvas *x, int type) {
 
 void canvas_getscroll (t_canvas *x) {
     //sys_vgui("pdtk_canvas_getscroll .x%lx.c\n",(long)x);
-    gui_vmess("gui_canvas_get_scroll", "x", x);
+    gui_vmess("gui_canvas_get_overriding_scroll", "x", x);
 }
 
 /* ---------------- generic widget behavior ------------------------- */
@@ -2299,7 +2299,11 @@ static void canvas_rightclick(t_canvas *x, int xpos, int ypos, t_gobj *y_sel)
        otherwise they end-up being dirty without visible notification
        besides, why would one mess with their properties without
        seeing what is inside them? CURRENTLY DISABLED */
+    //post("canvas_rightclick %d", (y && pd_class(&y->g_pd) == garray_class ? 1 : 0));
     canprop = (!y || (y && class_getpropertiesfn(pd_class(&y->g_pd)))
+               // ico@vt.edu: the following ensures that even if we are clicking on
+               // a garray, we should still be able to get the canvas properties
+                || (y && pd_class(&y->g_pd) == garray_class)
                /*&& !canvas_isabstraction( ((t_glist*)y) )*/ );
     canopen = (y && zgetfn(&y->g_pd, gensym("menu-open")));
     /* we add an extra check for scalars to enable the "Open" button
@@ -2478,9 +2482,12 @@ void canvas_vis(t_canvas *x, t_floatarg f)
                 canvas_destroy_editor(x);
             }
             //fprintf(stderr,"new\n");
+            /* ico@vt.edu: here we use hasarray only for toplevel garrays
+               because they require check_config every time resize is invoked.
+               We may need to expand this to include scalars, as well. */
             canvas_create_editor(x);
             canvas_args_to_string(argsbuf, x);
-            gui_vmess("gui_canvas_new", "xiisiissiiis",
+            gui_vmess("gui_canvas_new", "xiisiissiiiis",
                 x,
                 (int)(x->gl_screenx2 - x->gl_screenx1),
                 (int)(x->gl_screeny2 - x->gl_screeny1),
@@ -2492,6 +2499,7 @@ void canvas_vis(t_canvas *x, t_floatarg f)
                 x->gl_dirty,
                 x->gl_noscroll,
                 x->gl_nomenu,
+                canvas_hasarray(x),
                 argsbuf);
 
             /* It looks like this font size call is no longer needed,
@@ -2618,13 +2626,7 @@ void canvas_setgraph(t_glist *x, int flag, int nogoprect)
 
         // check if we have array inside GOP, if so,
         // make sure hidetext is always hidden no matter what
-        t_gobj *g = x->gl_list;
-        int hasarray = 0;
-        while (g)
-        {
-            if (pd_class(&g->g_pd) == garray_class) hasarray = 1;
-            g = g->g_next;
-        }
+        int hasarray = canvas_hasarray(x);
         if (hasarray) x->gl_hidetext = 1;
 
         if (!nogoprect && !x->gl_goprect && !hasarray)
@@ -2810,13 +2812,7 @@ static void canvas_donecanvasdialog(t_glist *x,
 
     // check if we have array inside GOP, if so,
     // make sure GOP/hidetext is always enabled no matter what
-    t_gobj *g = x->gl_list;
-    int hasarray = 0;
-    while (g)
-    {
-        if (pd_class(&g->g_pd) == garray_class) hasarray = 1;
-        g = g->g_next;
-    }
+    int hasarray = canvas_hasarray(x);
     if (hasarray && graphme != 3)
     {
         graphme = 3; //gop flag + bit-shifted hidetext
@@ -3233,6 +3229,7 @@ void canvas_doclick(t_canvas *x, int xpos, int ypos, int which,
        to array_motion so that we can update corresponding send when
        the array has been changed */
     array_garray = NULL;
+    //post("canvas_doclick %d", doit);
 
     t_gobj *y;
     int shiftmod, runmode, altmod, doublemod = 0, rightclick,
@@ -3479,10 +3476,14 @@ void canvas_doclick(t_canvas *x, int xpos, int ypos, int which,
                     canvas_check_nlet_highlights(x);
                 }
             }
-                /* look for an outlet */
-                // if object is valid, has outlets,
-                // and we are within the bottom area of an object
-            else if (ob && (noutlet = obj_noutlets(ob)) && ypos >= y2-4)
+                /* look for an outlet
+                   if object is valid, has outlets,
+                   and we are within the bottom area of an object
+                   ico@vt.edu: 2020-06-05 added expanded hotspot for
+                   nlets for easier pinpointing
+                */
+            else if (ob && (noutlet = obj_noutlets(ob)) &&
+                ypos >= y2-4-(x->gl_editor->canvas_cnct_inlet_tag[0] != 0 ? 2 : 0))
             {
                 int width = x2 - x1;
                 int nout1 = (noutlet > 1 ? noutlet - 1 : 1);
@@ -3490,8 +3491,13 @@ void canvas_doclick(t_canvas *x, int xpos, int ypos, int which,
                 int hotspot = x1 +
                     (width - IOWIDTH) * closest / (nout1);
                 // if we are within the boundaries of an nlet
+                /* ico@vt.edu: account for enlarged nlet when already
+                   highlighted to make it easier to "hit" the nlet */
+                int enlarged = 0;
+                if (x->gl_editor->canvas_cnct_inlet_tag[0] == closest)
+                    enlarged = 5;
                 if (closest < noutlet &&
-                    xpos >= (hotspot-1) && xpos <= hotspot + (IOWIDTH+1))
+                    xpos >= (hotspot-1-enlarged) && xpos <= hotspot + (IOWIDTH+1+enlarged))
                 {
                     if (doit)
                     {
@@ -3566,8 +3572,12 @@ void canvas_doclick(t_canvas *x, int xpos, int ypos, int which,
                 }
             }
                 /* look for an inlet (these are colored differently
-                   since they are not connectable) */
-            else if (ob && (ninlet = obj_ninlets(ob)) && ypos <= y1+4)
+                   since they are not connectable)
+                   ico@vt.edu: 2020-06-05 added expanded hotspot for
+                   nlets for easier pinpointing
+                */
+            else if (ob && (ninlet = obj_ninlets(ob))
+                && ypos <= y1+4+(x->gl_editor->canvas_cnct_inlet_tag[0] != 0 ? 2 : 0))
             {
                 canvas_setcursor(x, CURSOR_EDITMODE_NOTHING);
                 int width = x2 - x1;
@@ -3575,8 +3585,13 @@ void canvas_doclick(t_canvas *x, int xpos, int ypos, int which,
                 int closest = ((xpos-x1) * (nin1) + width/2)/width;
                 int hotspot = x1 +
                     (width - IOWIDTH) * closest / (nin1);
+                /* ico@vt.edu: account for enlarged nlet when already
+                   highlighted to make it easier to "hit" the nlet */
+                int enlarged = 0;
+                if (x->gl_editor->canvas_cnct_inlet_tag[0] == closest)
+                    enlarged = 5;
                 if (closest < ninlet &&
-                    xpos >= (hotspot-1) && xpos <= hotspot + (IOWIDTH+1))
+                    xpos >= (hotspot-1-enlarged) && xpos <= hotspot + (IOWIDTH+1+enlarged))
                 {
                        t_rtext *yr = glist_findrtext(x, (t_text *)&ob->ob_g);
 
diff --git a/pd/src/g_graph.c b/pd/src/g_graph.c
index b4a37cb9be3f4e958ff76475389025f261f722da..fcee84bb7fee6ab97f6e6a22a38ae09edc6a7dc4 100644
--- a/pd/src/g_graph.c
+++ b/pd/src/g_graph.c
@@ -729,21 +729,32 @@ extern int garray_get_style(t_garray *x);
     /* convert an x coordinate value to an x pixel location in window */
 t_float glist_xtopixels(t_glist *x, t_float xval)
 {
+    // ico@vt.edu: used to deal with the bar graph
+    t_float plot_offset = 0;
+    t_gobj *g = x->gl_list;
+
     if (!x->gl_isgraph)
         return ((xval - x->gl_x1) / (x->gl_x2 - x->gl_x1));
     else if (x->gl_isgraph && x->gl_havewindow)
-        return (x->gl_screenx2 - x->gl_screenx1) * 
-            (xval - x->gl_x1) / (x->gl_x2 - x->gl_x1);
+    {
+        if (g != NULL && g->g_pd == garray_class)
+        {
+            t_garray *g_a = (t_garray *)g;
+            if (garray_get_style(g_a) == PLOTSTYLE_BARS)
+                plot_offset = 10;
+        }
+        //fprintf(stderr, "xtopixels xval=%f gl_x1=%f gl_x2=%f screenx1=%d screenx2=%d\n",
+        //    xval, x->gl_x1, x->gl_x2, x->gl_screenx1, x->gl_screenx2);
+        return (x->gl_screenx2 - x->gl_screenx1) *
+            (xval - x->gl_x1 - plot_offset) / (x->gl_x2 - x->gl_x1);
+    }
     else
     {
         /* ico@vt.edu: some really stupid code to compensate for the fact
            that the svg stroke featue adds unaccounted width to the bars */
-        t_float plot_offset = 0;
-        t_gobj *g = x->gl_list;
         if (g != NULL && g->g_pd == garray_class)
         {
-            t_garray *g_a = (t_garray *)g;
-            if (garray_get_style(g_a) == PLOTSTYLE_BARS)
+            if (garray_get_style((t_garray *)g) == PLOTSTYLE_BARS)
                 plot_offset = 2;
         }
         int x1, y1, x2, y2;
@@ -756,23 +767,61 @@ t_float glist_xtopixels(t_glist *x, t_float xval)
 
 t_float glist_ytopixels(t_glist *x, t_float yval)
 {
+    t_float plot_offset = 0;
+    t_gobj *g = x->gl_list;
+
     if (!x->gl_isgraph)
         return ((yval - x->gl_y1) / (x->gl_y2 - x->gl_y1));
     else if (x->gl_isgraph && x->gl_havewindow)
-        return (x->gl_screeny2 - x->gl_screeny1) * 
-                (yval - x->gl_y1) / (x->gl_y2 - x->gl_y1);
+    {
+        if (g != NULL && g->g_pd == garray_class)
+        {
+            /*t_garray *g_a = (t_garray *)g;
+            if (garray_get_style((t_garray *)g) == PLOTSTYLE_POLY ||
+                    garray_get_style((t_garray *)g) == PLOTSTYLE_BEZ)*/
+            switch (garray_get_style((t_garray *)g))
+            {
+            case PLOTSTYLE_POINTS:
+                plot_offset = 2;
+                break;
+            case PLOTSTYLE_POLY:
+                plot_offset = 2;
+                break;
+            case PLOTSTYLE_BEZ:
+                plot_offset = 2;
+                break;
+            case PLOTSTYLE_BARS:
+                plot_offset = 2;
+                break;
+            }
+        }
+        return (x->gl_screeny2 - x->gl_screeny1 - plot_offset) *
+            (yval - x->gl_y1) / (x->gl_y2 - x->gl_y1);
+    }
     else 
     {
         /* ico@vt.edu: some really stupid code to compensate for the fact
            that the poly and bezier tend to overlap the GOP edges */
-        t_float plot_offset = 0;
-        t_gobj *g = x->gl_list;
         if (g != NULL && g->g_pd == garray_class)
         {
-            t_garray *g_a = (t_garray *)g;
-            if (garray_get_style(g_a) == PLOTSTYLE_POLY ||
-                    garray_get_style(g_a) == PLOTSTYLE_BEZ)
-                plot_offset = 2;
+            /*t_garray *g_a = (t_garray *)g;
+            if (garray_get_style((t_garray *)g) == PLOTSTYLE_POLY ||
+                    garray_get_style((t_garray *)g) == PLOTSTYLE_BEZ)*/
+            switch (garray_get_style((t_garray *)g))
+            {
+                case PLOTSTYLE_POINTS:
+                    plot_offset = 2;
+                    break;
+                case PLOTSTYLE_POLY:
+                    plot_offset = 2;
+                    break;
+                case PLOTSTYLE_BEZ:
+                    plot_offset = 2;
+                    break;
+                case PLOTSTYLE_BARS:
+                    plot_offset = 2;
+                    break;
+            }
         }
         int x1, y1, x2, y2;
         if (!x->gl_owner)
@@ -974,17 +1023,6 @@ static void graph_vis(t_gobj *gr, t_glist *parent_glist, int vis)
         }
         else if (gobj_shouldvis(gr, parent_glist))
         {
-            /* erase contents of glist. We need to do this because
-               scalar_vis is currently using pd_bind/unbind to handle
-               scalar events. */
-            for (g = x->gl_list; g; g = g->g_next)
-            {
-                gop_redraw = 1;
-                //fprintf(stderr,"drawing gop objects\n");
-                gobj_vis(g, x, 0);
-                //fprintf(stderr,"done\n");
-                gop_redraw = 0;
-            }
             gui_vmess("gui_gobj_erase", "xs",
                 glist_getcanvas(x->gl_owner),
                 tag);
diff --git a/pd/src/g_scalar.c b/pd/src/g_scalar.c
index a1f778fe710130da9f1e46fa7f4e60be4eef8355..7d8f9e0deb5f6519aaf8c290c3be2b0355e40a80 100644
--- a/pd/src/g_scalar.c
+++ b/pd/src/g_scalar.c
@@ -1056,8 +1056,10 @@ static void scalar_vis(t_gobj *z, t_glist *owner, int vis)
         }
         t_float xscale = ((glist_xtopixels(owner, 1) - glist_xtopixels(owner, 0)));
         t_float yscale = glist_ytopixels(owner, 1) - glist_ytopixels(owner, 0);
-        t_float nw_yoffset = 0;
-        switch (plot_style)
+        // this has been moved into pdgui.js gui_scalar_new, leaving here just
+        // in case the other implementation proves problematic
+        //t_float nw_yoffset = 0;
+        /*switch (plot_style)
         {
             case 0:
                 nw_yoffset = -0.5;
@@ -1073,7 +1075,7 @@ static void scalar_vis(t_gobj *z, t_glist *owner, int vis)
                 nw_yoffset = 0.5;
                 yscale += 1;
                 break;
-        }
+        }*/
         /* we translate the .scalar%lx group to displace it on the tk side.
            This is the outermost group for the scalar, something like a
            poor man's viewport.
@@ -1086,14 +1088,15 @@ static void scalar_vis(t_gobj *z, t_glist *owner, int vis)
            understand "None"-- instead we must send an empty symbol.) */
         char tagbuf[MAXPDSTRING];
         sprintf(tagbuf, "scalar%lx", (long unsigned int)x->sc_vec);
-        gui_vmess("gui_scalar_new", "xsiffffffi",
+        gui_vmess("gui_scalar_new", "xsiffffffii",
             glist_getcanvas(owner),
             tagbuf,
             glist_isselected(owner, &x->sc_gobj),
             xscale, 0.0, 0.0, yscale,
-            glist_xtopixels(owner, basex) + (plot_style == 3 ? 0.5 : 0),
-            glist_ytopixels(owner, basey) + nw_yoffset,
-            glist_istoplevel(owner));
+            glist_xtopixels(owner, basex),
+            glist_ytopixels(owner, basey),
+            glist_istoplevel(owner),
+            plot_style);
         char groupbuf[MAXPDSTRING];
         // Quick hack to make gui_scalar_draw_group more general (so we
         // don't have to tack on "gobj" manually)
@@ -1313,7 +1316,7 @@ int scalar_doclick(t_word *data, t_template *template, t_scalar *sc,
 static int scalar_click(t_gobj *z, struct _glist *owner,
     int xpix, int ypix, int shift, int alt, int dbl, int doit)
 {
-    //fprintf(stderr,"scalar_click %d %d\n", xpix, ypix);
+    //post("scalar_click %d %d %d", xpix, ypix, doit);
     t_scalar *x = (t_scalar *)z;
 
     x->sc_bboxcache = 0;
diff --git a/pd/src/g_template.c b/pd/src/g_template.c
index e177742bf538f80bdc36010321889e053e55d7bf..4bdd67f3ca9558c2f9185fd44cb0671700958abf 100644
--- a/pd/src/g_template.c
+++ b/pd/src/g_template.c
@@ -6148,8 +6148,10 @@ static void plot_vis(t_gobj *z, t_glist *glist, t_glist *parentglist,
                         }
                         else
                         {
-                            /* case for open window displaying the array */
-                            /* tbd */
+                            /* case for open window displaying the array,
+                               apparently we use the same value for the bottom
+                               of the bar */
+                            py2 = glist->gl_y2;
                         }
                     }
                     int mex1 = fielddesc_cvttocoord(xfielddesc, usexloc);
diff --git a/pd/src/g_text.c b/pd/src/g_text.c
index 40460727b4a7c706985406df76888d289c5b171c..4eae176faf5ab4d41d05110fdea158b946211b36 100644
--- a/pd/src/g_text.c
+++ b/pd/src/g_text.c
@@ -855,7 +855,7 @@ void canvas_msg(t_glist *gl, t_symbol *s, int argc, t_atom *argv)
 
 /* ---------------------- the "atom" text item ------------------------ */
 
-#define ATOMBUFSIZE 40
+#define ATOMBUFSIZE 160
 #define ATOM_LABELLEFT 0
 #define ATOM_LABELRIGHT 1
 #define ATOM_LABELUP 2
@@ -1190,8 +1190,8 @@ static void gatom_param(t_gatom *x, t_symbol *sel, int argc, t_atom *argv)
     x->a_draghi = draghi;
     if (width < 0)
         width = 4;
-    else if (width > 80)
-        width = 80;
+    else if (width > ATOMBUFSIZE)
+        width = ATOMBUFSIZE;
     x->a_text.te_width = width;
     x->a_wherelabel = ((int)wherelabel & 3);
     x->a_label = label;
diff --git a/pd/src/m_class.c b/pd/src/m_class.c
index 0656d38f82e76e139482f4ce373c275787e0ad97..cf0ab0721bc4626ab3479b60def17aa093f51793 100644
--- a/pd/src/m_class.c
+++ b/pd/src/m_class.c
@@ -705,7 +705,7 @@ extern t_pd *pd_mess_from_responder(t_pd *x);
    Pd Vanilla apparently made a special case to handle this-- instead of using
    the comma after the "restore" message it starts a new "#X f 12;" which
    ensures that the #X is bound to the correct canvas. But that means it fails
-   for the old the old syntax, and there are patches in the wild that use it.
+   for the old syntax, and there are patches in the wild that use it.
 
    So we support _both_ of these styles in Purr Data by doing the following:
    1. Checking if the last typedmess was "restore" selector.