diff --git a/pd/nw/locales/de/translation.json b/pd/nw/locales/de/translation.json
index 0dc5423ab061e1139b73bb28c72cbf5a75fc405d..6e8998428214359ee4791de05c7e4236ceee8457 100644
--- a/pd/nw/locales/de/translation.json
+++ b/pd/nw/locales/de/translation.json
@@ -167,6 +167,10 @@
     "zoomreset_tt": "Setze die Anzeige des Patches auf die Originalgröße zurück",
     "zoomoptimal": "Optimale Größe",
     "zoomoptimal_tt": "Wählt die optimale Größe, mit der der gesamte Patch angezeigt werden kann (soweit möglich)",
+    "zoomhoriz": "An Breite anpassen",
+    "zoomhoriz_tt": "Wählt die optimale Größe, um den Patch in voller Breite anzuzeigen",
+    "zoomvert": "An Höhe anpassen",
+    "zoomvert_tt": "Wählt die optimale Größe, um den Patch in voller Höhe anzuzeigen",
     "fullscreen": "Vollbild",
 
     "put": "Hinzufügen",
diff --git a/pd/nw/locales/en/translation.json b/pd/nw/locales/en/translation.json
index 7f4cf14fa6f8afc83354e79dacbcf5e1b2d10461..ea02b5377863b589fe99a76a14c9e7bb30106446 100644
--- a/pd/nw/locales/en/translation.json
+++ b/pd/nw/locales/en/translation.json
@@ -167,6 +167,10 @@
     "zoomreset_tt": "Reset the zoom to the original level",
     "zoomoptimal": "Optimal Zoom",
     "zoomoptimal_tt": "Change the zoom to the optimal level which makes the entire patch visible (as far as possible)",
+    "zoomhoriz": "Fit to Width",
+    "zoomhoriz_tt": "Change the zoom level to make the patch fit horizontally",
+    "zoomvert": "Fit to Height",
+    "zoomvert_tt": "Change the zoom level to make the patch fit vertically",
     "fullscreen": "Fullscreen",
 
     "put": "Put",
diff --git a/pd/nw/pd_canvas.js b/pd/nw/pd_canvas.js
index 261b0eb4356e6748a801715fad37fcac0014005f..0b582fd9bbe201c9cc5ff49346f786ee7787659b 100644
--- a/pd/nw/pd_canvas.js
+++ b/pd/nw/pd_canvas.js
@@ -1189,7 +1189,21 @@ function nw_create_patch_window_menus(gui, w, name) {
     minit(m.view.optimalzoom, {
         enabled: true,
         click: function () {
-            pdgui.gui_canvas_optimal_zoom(name);
+            pdgui.gui_canvas_optimal_zoom(name, 1, 1);
+            pdgui.gui_canvas_get_scroll(name);
+        }
+    });
+    minit(m.view.horizzoom, {
+        enabled: true,
+        click: function () {
+            pdgui.gui_canvas_optimal_zoom(name, 1, 0);
+            pdgui.gui_canvas_get_scroll(name);
+        }
+    });
+    minit(m.view.vertzoom, {
+        enabled: true,
+        click: function () {
+            pdgui.gui_canvas_optimal_zoom(name, 0, 1);
             pdgui.gui_canvas_get_scroll(name);
         }
     });
diff --git a/pd/nw/pd_menus.js b/pd/nw/pd_menus.js
index 0f87785895fff4e17c7c50265ba592ca02156187..9342be9849df80e3be09685f87e0b719565b00d8 100644
--- a/pd/nw/pd_menus.js
+++ b/pd/nw/pd_menus.js
@@ -332,6 +332,18 @@ function create_menu(gui, type) {
             modifiers: cmd_or_ctrl + "+alt",
             tooltip: l("menu.zoomoptimal_tt")
 	}));
+	view_menu.append(m.view.horizzoom = new gui.MenuItem({
+            label: l("menu.zoomhoriz"),
+            key: "w",
+            modifiers: cmd_or_ctrl + "+alt",
+            tooltip: l("menu.zoomhoriz_tt")
+	}));
+	view_menu.append(m.view.vertzoom = new gui.MenuItem({
+            label: l("menu.zoomvert"),
+            key: "h",
+            modifiers: cmd_or_ctrl + "+alt",
+            tooltip: l("menu.zoomvert_tt")
+	}));
     }
     view_menu.append(new gui.MenuItem({ type: "separator" }));
     view_menu.append(m.view.fullscreen = new gui.MenuItem({
diff --git a/pd/nw/pdgui.js b/pd/nw/pdgui.js
index 171241810d8c36c669525634e6a201c16e4b27b9..f88d4c8b63e712d659c022503ac6cbc7ca14f482 100644
--- a/pd/nw/pdgui.js
+++ b/pd/nw/pdgui.js
@@ -4647,7 +4647,7 @@ function gui_canvas_get_scroll(cid) {
 
 exports.gui_canvas_get_scroll = gui_canvas_get_scroll;
 
-function do_optimalzoom(cid) {
+function do_optimalzoom(cid, hflag, vflag) {
     // determine an optimal zoom level that makes the entire patch fit within
     // the window
     if (!patchwin[cid]) { return; }
@@ -4662,11 +4662,18 @@ function do_optimalzoom(cid) {
     var zx = 0, zy = 0;
     if (width>0) zx = Math.floor(Math.log(min_width/width)/Math.log(1.2));
     if (height>0) zy = Math.floor(Math.log(min_height/height)/Math.log(1.2));
-    // Optimal zoom is the minimum of the horizontal and vertical zoom
-    // values. This gives us the offset to the current zoom level. We
-    // then need to clamp the resulting new zoom level to the valid
-    // zoom level range of -8..+7.
-    var actz = patchwin[cid].zoomLevel, z = actz+Math.min(zx, zy);
+    // Optimal zoom is the minimum of the horizontal and/or the vertical zoom
+    // values, depending on the h and v flags. This gives us the offset to the
+    // current zoom level. We then need to clamp the resulting new zoom level
+    // to the valid zoom level range of -8..+7.
+    var actz = patchwin[cid].zoomLevel, z = 0;
+    if (hflag && vflag)
+	z = Math.min(zx, zy);
+    else if (hflag)
+	z = zx;
+    else if (vflag)
+	z = zy;
+    z += actz;
     if (z < -8) z = -8; if (z > 7) z = 7;
     //post("bbox: "+width+"x"+height+"+"+x+"+"+y+" window size: "+min_width+"x"+min_height+" current zoom level: "+actz+" optimal zoom level: "+z);
     if (z != actz) {
@@ -4680,9 +4687,9 @@ var optimalzoom_var = {};
 // use a smaller value here, so that we're done before a subsequent
 // call to do_getscroll updates the viewport. XXXREVIEW: Hopefully
 // 100 msec are enough for do_optimalzoom to finish.
-function gui_canvas_optimal_zoom(cid) {
+function gui_canvas_optimal_zoom(cid, h, v) {
     clearTimeout(optimalzoom_var[cid]);
-    optimalzoom_var[cid] = setTimeout(do_optimalzoom, 150, cid);
+    optimalzoom_var[cid] = setTimeout(do_optimalzoom, 150, cid, h, v);
 }
 
 exports.gui_canvas_optimal_zoom = gui_canvas_optimal_zoom;