diff --git a/pd/nw/css/footgun.css b/pd/nw/css/footgun.css
new file mode 100644
index 0000000000000000000000000000000000000000..d3cb2f1f726e19499514bcf34895afffff54d518
--- /dev/null
+++ b/pd/nw/css/footgun.css
@@ -0,0 +1,761 @@
+/* Global CSS */
+
+/* Unfortunately we can't simply include a ttf font of our choice with our
+   project. Chrome has some subtle, nasty rendering bug that ends up making
+   the pd object text invisible. It seems to get triggered when opening a
+   new window-- like a dialog-- if it happens not to use the @font-face font
+   in the body. This ends up somehow breaking the display for the original
+   window. The text will turn invisible _sometimes_ when changing font size,
+   zooming, creating an array (the label might be invisible), or showing
+   the "Save before quitting" dialog. */
+/* 
+@font-face {
+    font-family: "DejaVu Sans Mono";
+    src: url("../DejaVuSansMono.ttf");
+}
+*/
+
+/* This is just a copy of default.css with cords and xlets hidden. */
+
+.cord, .xlet_control, .xlet_signal, .xlet_iemgui {
+    display: none;
+}
+
+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;
+}
+
+::selection {
+    background: #c3c3c3;
+    color: black;
+}
+
+/* The Pd Console Window */
+
+#console_controls {
+    background-color: LightGray;
+    height: 40px;
+} 
+
+#control_frame {
+    padding: 12px;
+}
+
+/* The DSP toggle */
+
+.dsp_toggle {
+    display: inline-block;
+    position: relative;
+    cursor: pointer;
+    -webkit-user-select: none;
+    -moz-user-select: none;
+    -ms-user-select: none;
+    user-select: none;
+    -webkit-tap-highlight-color: transparent;
+    tap-highlight-color: transparent;
+}
+
+.dsp_toggle input {
+    opacity: 0;
+    position: absolute;
+}
+
+.dsp_toggle input + span {
+    position: relative;
+    display: inline-block;
+    width: 1.65em;
+    height: 1em;
+    background: white;
+    box-shadow: inset 0 0 0 0.0625em #e9e9e9;
+    border-radius: 0.5em;
+    vertical-align: -0.15em;
+    transition: all 0.40s cubic-bezier(.17,.67,.43,.98);
+}
+
+.dsp_toggle:active input + span,
+.dsp_toggle input + span:active {
+    box-shadow: inset 0 0 0 0.73em #e9e9e9;
+}
+
+.dsp_toggle input + span:after {
+    position: absolute;
+    display: block;
+    content: '';
+    width: 0.875em;
+    height: 0.875em;
+    border-radius: 0.4375em;
+    top: 0.0625em;
+    left: 0.0625em;
+    background: white;
+    box-shadow: inset 0 0 0 0.03em rgba(0,0,0,0.1),
+                0 0 0.05em rgba(0,0,0,0.05),
+                0 0.1em 0.2em rgba(0,0,0,0.2);
+    transition: all 0.25s ease-out;
+}
+
+.dsp_toggle:active input + span:after,
+.dsp_toggle input + span:active:after {
+    width: 1.15em;
+}
+
+.dsp_toggle input:checked + span {
+    box-shadow: inset 0 0 0 0.73em #4cd964;
+}
+
+.dsp_toggle input:checked + span:after {
+    left: 0.7125em;
+}
+
+.dsp_toggle:active input:checked + span:after,
+.dsp_toggle input:checked + span:active:after {
+    left: 0.4375em;
+}
+
+/* accessibility styles */
+.dsp_toggle input:focus + span:after {
+    box-shadow: inset 0 0 0 0.03em rgba(0,0,0,0.15),
+                0 0 0.05em rgba(0,0,0,0.08),
+                0 0.1em 0.2em rgba(0,0,0,0.3);
+    background: #fff;
+}
+
+.dsp_toggle input:focus + span {
+    box-shadow: inset 0 0 0 0.0625em #dadada;
+}
+
+.dsp_toggle input:focus:checked + span {
+    box-shadow: inset 0 0 0 0.73em #33be4b;
+}
+
+/* reset accessibility style on hover */
+.dsp_toggle:hover input:focus + span:after {
+    box-shadow: inset 0 0 0 0.03em rgba(0,0,0,0.1),
+                0 0 0.05em rgba(0,0,0,0.05),
+                0 0.1em 0.2em rgba(0,0,0,0.2);
+    background: #fff;
+}
+
+.dsp_toggle:hover input:focus + span {
+    box-shadow: inset 0 0 0 0.0625em #e9e9e9;
+}
+
+.dsp_toggle:hover input:focus:checked + span {
+    box-shadow: inset 0 0 0 0.73em #4cd964;
+}
+
+#printout {
+    margin: 8px;
+}
+
+/* This needs to be renamed, since the "Find" bar is actually at the bottom */
+#console_bottom {
+    position: absolute;
+    top: 40px;
+    left: 0px;
+    right: 0px;
+    bottom: 0px;
+    overflow-y: scroll;    
+}
+
+/* The console API allows classes for different types of messages to print.
+   Currently the only class is "error". More may be added, especially once 
+   we port the "loglevel" functionality that was available in Pd Extended. */
+#console_bottom .error {
+    color: red;
+}
+
+/* Find bar */
+
+#console_find label, #canvas_find label {
+    font-family: "DejaVu Sans", sans-serif;
+    font-size: 10pt;
+}
+
+/* marks for matches to console_find */
+mark {
+    background: white;
+}
+
+mark.console_find_current.console_find_highlighted,
+mark.console_find_current {
+    background: yellow;
+}
+
+mark.console_find_highlighted {
+    background: red;
+}
+
+#console_find {
+    width: 100%;
+    height: 1em;
+    padding: 0.2em;
+    padding-left:8px;
+    background: silver;
+    position: fixed;
+    bottom: 0;
+    left: 0;
+}
+
+/* Pure Data Patch Window (aka canvas) */
+
+/* patch font and background color. (Note: margin needs to stay at zero.) */
+.patch_body {
+    background-color: white;
+}
+
+#selection_rectangle {
+    stroke: #e87216;
+}
+
+/* The outline to show the visible area for a Graph-On-Parent canvas,
+   i.e., the "red rectangle" */
+.gop_rect {
+    fill: none;
+    stroke: red; 
+}
+
+.cord.signal {
+    stroke-width: 2;
+    stroke: #808095;
+}
+
+.cord.control {
+    stroke-width: 1;
+    stroke: #565;
+}
+
+/* selected connection between objects */
+.cord.signal.selected_line,
+.cord.control.selected_line {
+    stroke: #e87216;
+}
+
+#cord_inspector_rect {
+    fill: black;
+    stroke: black;
+}
+
+#cord_inspector_text {
+    fill: white;
+}
+
+#cord_inspector_text.flash {
+    fill: #e87216;
+}
+
+/* text inside boxes: message boxes, object boxes, graphs, comments, etc. */
+.box_text {
+    fill: black;
+}
+
+/* hyperlinks: for now, just pddplink and helplink */
+.pd_link text {
+    fill: blue;
+}
+
+.pd_link text:hover {
+    fill: red;
+}
+
+.pd_link.selected text {
+    fill: #e87216 !important;
+}
+
+/* text inside a box that is being edited */
+#new_object_textentry {
+    /* max-width: 10ch; */
+    min-width: 3ch;
+    position: absolute;
+    display: table-cell;
+    padding: 3px 2px 3px 2px;
+    /* box-shadow: inset 1px 0px 0px 1px #000; */
+    color: black; /* text color */
+    background-color: transparent;
+    white-space: pre-wrap;
+    overflow-wrap: break-word;
+    -webkit-margin-before: 0px;
+}
+
+#new_object_textentry.obj {
+    outline: 1px solid #e87216;
+}
+
+/* We're dynamically creating the svg background data in javascript
+   (in pdgui.js) so that we can change the stroke color of the svg msg box.
+   We store the color as the third argument to "outline" below. Since the
+   outline is 0px it won't show up-- this allows us specify the color here
+   in the style sheet and retrieve it in javascript when we build the svg
+   background. */
+#new_object_textentry.msg {
+    outline: 0px solid #e87216;
+    /* background-image: url(../msg-box.svg); */
+}
+
+p.msg::after {
+    content: "";
+    height: 100%;
+    width: 5px;
+    /* background-image: url(../msg-box-flag.svg); */
+    position: absolute;
+    top: 0%;
+    left: 100%;
+}
+
+/* not sure what this is doing here... */
+text {
+    // fill: red;
+    //cursor: default;
+}
+
+/* not sure if this is still needed */
+.selected_border {
+    stroke: blue;
+    stroke-dasharray: none;
+    stroke-width: 1;
+}
+
+.msg .border {
+    stroke: #ccc;
+    fill: #f8f8f6;
+}
+
+/* state of msg box when clicking it */
+.msg.flashed .border {
+    stroke-width: 4;
+}
+
+/* atom box */
+.atom .border {
+    stroke: #ccc;
+    fill: #eee;
+}
+
+/* for dropdown box we want to visually distinguish boxes that output
+   the index from boxes that output the value. For now we do that by
+   stroking the arrow for boxes that output an index. For boxes that
+   output the value we don't need a CSS rule, as the arrow will be filled
+   black by default */
+.atom .index_arrow {
+    stroke: black;
+    stroke-width: 1;
+    fill: none;
+}
+
+/* gatom "activated" text (i.e., when it has the keyboard focus) */
+.atom.activated text {
+    fill: red;
+}
+
+#dropdown_list {
+    position: absolute;
+    border-width: 1px;
+    border-style: solid;
+    border-color: #c3c3c3;
+    cursor: pointer;
+    box-shadow: 5px 0 5px -5px #aaa, 0 5px 5px -5px #aaa, -5px 0 5px -5px #aaa;
+    overflow-y: auto;
+}
+
+#dropdown_list ol {
+    list-style-position: inside;
+    margin: 0;
+    padding: 0;
+    background: #eee;
+}
+
+#dropdown_list li {
+    list-style-type: none;
+    padding: 5px;
+}
+
+#dropdown_list li.highlighted {
+    background-color: #c3c3c3;
+}
+
+.obj .border {
+    fill: #f6f8f8;
+    stroke: #ccc;
+}
+
+.comment .border {
+    fill: none;
+}
+
+#patchsvg.editmode .comment .border {
+    stroke: #aaa;
+    stroke-dasharray: 8 4;
+}
+
+/* A little hack for special case of [cnv].
+   All other iemguis have a black border, but
+   [cnv] sets its selection rectangle to the
+   user-supplied fill color when the object
+   isn't selected */
+.iemgui .border:not(.mycanvas_border) {
+    stroke: black;
+}
+
+.graph .border {
+    stroke: black;
+    fill: none;
+}
+
+/* Graph (or subpatch) that has been opened to inspect its contents */
+.graph.has_window .border {
+    stroke: black;
+    fill: gray;
+}
+
+/* border color for selected objects
+      * an element with the class "border"
+      * the element is contained within a parent element of class "selected"
+      * that parent element is not in class "gop"
+   in plain English:
+      This lets us highlight an object's border, unless it is inside a gop
+      canvas.
+*/
+:not(.gop).selected .border {
+    stroke: #e87216;
+    display: inline;
+}
+
+/* text inside selected objects */
+:not(.gop).selected text {
+    fill: blue;
+}
+
+/* for an object that didn't create */
+.obj .border.broken_border {
+    fill: #f7f7f7;
+    stroke: #f00;
+    stroke-dasharray: 3 2;
+}
+
+/* control inlet */
+.xlet_control {
+    stroke: #777;
+    fill: white;
+//    stroke-width: 1;
+}
+
+/* signal inlet */
+.xlet_signal {
+    stroke: #777;
+    fill: #808095;
+    stroke-width: 1;
+}
+
+/* iemgui inlet or outlet */
+.xlet_iemgui {
+    stroke: black;
+    fill: black;
+    stroke-width: 1;
+}
+
+/* text label for an iemgui */
+.iemgui_label_selected {
+    fill: blue;
+}
+
+/* test of xlet hover animation... this should 
+   probably use the web animation API instead. That
+   way the animation won't get cut off when you
+   move off the object. We can't do that currently
+   because Pd just floods us with pairs of messages 
+   for every pixel we move inside an xlet. */
+@-webkit-keyframes fizzle {
+    0% {
+        stroke-width: 1;
+        stroke-opacity: 1;
+        rx: 1;
+        ry: 1;
+    }
+    100% {
+        stroke-width: 20;
+        stroke-opacity: 0.2;
+        rx: 50;
+        ry: 50;
+    }
+}
+
+/* can't remember why this was tagged !important */
+.xlet_selected {
+    stroke: orange !important;
+    fill: orange;
+    -webkit-animation: fizzle 0.4s linear 1;
+}
+
+#canvas_find {
+    width: 100%;
+    height: 1em;
+    padding-top: 4px;
+    padding-left: 8px;
+    padding-bottom: 8px;
+    background: silver;
+    position: fixed;
+    bottom: 0;
+    left: 0;
+}
+
+/* Dialog to ask to save the patch before quitting */
+#save_before_quit {
+    background-color: #f3f3f3;
+    border:1px solid #bbb;
+    padding: 12px;
+    margin: 12px;
+    box-shadow: 7px 7px 5px grey;
+}
+
+/* Search dialog */
+
+.search_body {
+    font-family: "DejaVu Sans", sans-serif;
+    font-size: 10pt;
+    padding: 8px;
+}
+
+/* Common to all dialogs */
+
+.dialog_body {
+    font-family: "DejaVu Sans", sans-serif;
+    font-size: 10pt;
+    background-color: #f3f3f3;
+}
+
+.submit_buttons {
+    text-align: center;
+    padding: 8px;
+}
+
+fieldset {
+/*    font-family:Georgia; */
+    background-color:#f3f3f3;
+    border-radius:3px;
+    border:1px solid #ddd;
+    margin-left:auto;
+    margin-right:auto;
+    padding: 8px;
+}
+
+legend {
+    font-size: 1.2em;
+}
+
+.hidden {
+    display: none;
+}
+
+.container{
+    display: none;
+}
+
+/* Iemgui dialog */
+
+input[type="text"]{
+    width:3em;
+}
+
+input[type="number"]{
+    width:3em;
+}
+
+label {
+    text-align: right;
+}
+
+/* Pair of properties that are related */
+.pair {
+    width: 75%;
+    text-align: left;
+    align: left;
+}
+
+.item1 {
+    width: 50%;
+}
+
+.item2 {
+    width: 50%;
+}
+
+input[name="x_offset"] {
+    width: 2em;
+}
+
+input[name="y_offset"] {
+    width: 2em;
+}
+
+input[name="send_symbol"] {
+    width: 8em;
+}
+
+input[name="receive_symbol"] {
+    width: 8em;
+}
+
+input[name="label"] {
+    width: 8em;
+}
+
+input[name="font_size"] {
+    width: 3em;
+}
+
+input[name="startup_flags"] {
+    width: 16em;
+}
+
+/* Canvas dialog */
+
+div.x-scale {
+    padding: 3px;
+    text-align: center;
+}
+
+div.gop-range {
+}
+
+div.y1 {
+    text-align: center;
+    padding: 3px;
+}
+
+div.x1 {
+    text-align: center;
+    padding: 3px;
+    white-space: nowrap;
+}
+
+div.y2 {
+    text-align: center;
+    padding: 3px;
+}
+
+.x-margin {
+    white-space: nowrap;
+}
+
+.array_style {
+    white-space: nowrap;
+}
+
+#array_name_input {
+    width: 6em;
+}
+
+.disabled {
+    color: #aaa;
+}
+
+/* Preferences dialog */
+
+#prefs_html_element {
+/*    height: 100%; */
+    margin: 0px;
+    padding: 0px;
+    height: 100vh;
+}
+
+.prefs_body {
+    padding: 0px;
+}
+
+#prefs_container {
+    display: table;
+}
+
+/* Main tab widget */
+
+/* All the display, width, and height settings below are a house of cards.
+   I don't have the schooling to actually predict how all these CSS elements
+   work together to create the whole. I just fudged around until I found a
+   way to get the buttons anchored at the bottom of the dialog without
+   triggering scrollbars to appear. If someone knows a way to do it "right"
+   without becoming an order of magnitude more complex, do feel free... */
+.prefs_tab_group {
+    display: table;
+    width: 90%;
+}
+
+/* Configure the radio buttons to hide off-screen */
+.prefs_tab {
+    position: absolute;
+    left:-100px;
+    top:-100px;
+}
+
+/* Configure labels to look like tabs */
+.prefs_tab + label {
+    /* inline-block such that the label can be given dimensions */
+    display: inline-block;
+    /* A nice curved border around the tab */
+    border: 1px solid #bbb;
+    border-top-left-radius: 5px;
+    border-top-right-radius: 5px;
+    /* the bottom border is handled by the tab content div */
+    border-bottom: 0;
+    /* Padding around tab text */
+    padding: 5px 10px;
+    /* put a small margin to the left to make the first tab clear */
+    margin-left: 4px;
+    margin-top: 8px;
+    margin-bottom: 0px;
+    /* Set the background color to default gray (non-selected tab) */
+    background-color:#ececec;
+}
+
+/* Focused tabs */
+.prefs_tab:focus + label {
+    border: 1px dashed #bbb;
+}
+
+/* Checked tabs must be white with the bottom border removed */
+.prefs_tab:checked + label {
+    background-color: #f3f3f3;
+    text-shadow: 1px 0px 0px; /* substitute for "bold" to retain div width */
+    border-bottom: 1px solid #f3f3f3;
+    margin-bottom: -1px;
+}
+
+/* The tab content must fill the widgets size and have a nice border */
+.prefs_tab_group > div {
+    display: none;
+    border-top: 1px solid #ddd;
+    padding: 0px;
+    margin: 0px;
+    height: 100%;
+}
+
+/* This matches tabs displaying to their associated radio inputs */
+.tab1:checked ~ .tab1, .tab2:checked ~ .tab2, .tab3:checked ~ .tab3, .tab4:checked ~ .tab4 {
+    display: table;
+    padding: 8px;
+    line-height: 20px;
+    width: 100%;
+    height: 78vh;
+}
+
+.tab_settings {
+    padding-top: 8px;
+}
+
+#prefs_buttons {
+    display: table;
+    height: 10vh;
+    padding: 0px;
+    margin: 0px;
+    margin-top: -10px;
+    margin-bottom: -10px;
+    padding: 30px;
+}
diff --git a/pd/nw/dialog_prefs.html b/pd/nw/dialog_prefs.html
index 44b95fce4dbb9779d14673b487aa98159335cf7a..00c13be4979e15cc1f1c006318c171a2f26a21cc 100644
--- a/pd/nw/dialog_prefs.html
+++ b/pd/nw/dialog_prefs.html
@@ -362,6 +362,8 @@
               <option data-i18n="prefs.gui.presets.solarized_inverted"
                       value="solarized_inverted">
               </option>
+              <option data-i18n="prefs.gui.presets.footgun" value="footgun">
+              </option>
             </select>
             <br/>
             <label data-i18n="[title]prefs.gui.zoom.save_zoom_tt">
diff --git a/pd/nw/locales/de/translation.json b/pd/nw/locales/de/translation.json
index f382fa17c9889c466db4412024d44700bf72664e..9eb94414ee09df0fc9c37ff09bdfbd1a05001a01 100644
--- a/pd/nw/locales/de/translation.json
+++ b/pd/nw/locales/de/translation.json
@@ -402,7 +402,8 @@
         "solarized": "Solarized",
         "solarized_inverted": "Solarized (Inverted)",
         "extended": "Extended",
-        "c64": "C64"
+        "c64": "C64",
+        "footgun": "Fusspistole"
       },
       "zoom": {
         "save_zoom": "Speichern/Laden der Vergrößerung im Patch",
diff --git a/pd/nw/locales/en/translation.json b/pd/nw/locales/en/translation.json
index c779742a70f17f466865b89090416169b593fd64..57f658c2214feb87f7533ce404b43e83dcec6e6d 100644
--- a/pd/nw/locales/en/translation.json
+++ b/pd/nw/locales/en/translation.json
@@ -403,7 +403,8 @@
         "solarized": "Solarized",
         "solarized_inverted": "Solarized (Inverted)",
         "extended": "Extended",
-        "c64": "C64"
+        "c64": "C64",
+        "footgun": "Footgun"
       },
       "zoom": {
         "save_zoom": "save/load zoom level with patch",