diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/FastRGridExternalLookup.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/FastRGridExternalLookup.java
index 5863de72a47db700e3e773967266d68c1312da39..b2d0f365a4671906ea50704e74880d4f987328ed 100644
--- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/FastRGridExternalLookup.java
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/FastRGridExternalLookup.java
@@ -22,6 +22,7 @@
  */
 package com.oracle.truffle.r.library.fastrGrid;
 
+import com.oracle.truffle.r.library.fastrGrid.grDevices.DevHoldFlush;
 import com.oracle.truffle.r.library.fastrGrid.grDevices.InitWindowedDevice;
 import com.oracle.truffle.r.library.fastrGrid.graphics.CPar;
 import com.oracle.truffle.r.nodes.builtin.RExternalBuiltinNode;
@@ -42,7 +43,7 @@ public class FastRGridExternalLookup {
     public static RExternalBuiltinNode lookupDotExternal(String name) {
         switch (name) {
             case "devholdflush":
-                return new IgnoredGridExternal(RNull.instance);
+                return DevHoldFlush.create();
             case "PDF":
                 return new IgnoredGridExternal(RNull.instance);
             default:
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/GPar.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/GPar.java
index 41d785ee1fcffc9bfc4c4e39a4a719d10aadb226..6f69df073cbb3035caf1effbb0d77ef62ec23009 100644
--- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/GPar.java
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/GPar.java
@@ -17,7 +17,9 @@ import static com.oracle.truffle.r.library.fastrGrid.GridUtils.asDouble;
 import java.util.Arrays;
 
 import com.oracle.truffle.r.library.fastrGrid.device.DrawingContext;
+import com.oracle.truffle.r.library.fastrGrid.device.DrawingContextDefaults;
 import com.oracle.truffle.r.library.fastrGrid.device.GridColor;
+import com.oracle.truffle.r.library.fastrGrid.device.GridDevice;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RError.Message;
 import com.oracle.truffle.r.runtime.RRuntime;
@@ -84,11 +86,12 @@ public final class GPar {
     };
     private static final RStringVector NAMES_VECTOR = (RStringVector) RDataFactory.createStringVector(NAMES, RDataFactory.COMPLETE_VECTOR).makeSharedPermanent();
 
-    public static RList createNew() {
+    public static RList createNew(GridDevice device) {
         Object[] data = new Object[GP_LENGTH];
+        DrawingContextDefaults defaults = device.getDrawingContextDefaults();
         Arrays.fill(data, RNull.instance);
-        data[GP_FILL] = "transparent";
-        data[GP_COL] = "black";
+        data[GP_COL] = defaults.color;
+        data[GP_FILL] = defaults.fillColor;
         data[GP_GAMMA] = newDoubleVec(0);
         data[GP_LTY] = "solid";
         data[GP_LWD] = newDoubleVec(1);
@@ -102,7 +105,9 @@ public final class GPar {
         data[GP_LINEJOIN] = "round";
         data[GP_LINEMITRE] = newDoubleVec(10);
         data[GP_LEX] = newDoubleVec(1);
-        return RDataFactory.createList(data, NAMES_VECTOR);
+        RList result = RDataFactory.createList(data, NAMES_VECTOR);
+        result.makeSharedPermanent();
+        return result;
     }
 
     public static double getCex(RList gpar) {
@@ -122,6 +127,7 @@ public final class GPar {
 
         private GParDrawingContext(RList list) {
             data = list.getDataWithoutCopying();
+            list.makeSharedPermanent();
         }
 
         @Override
@@ -158,11 +164,6 @@ public final class GPar {
             return getGridColor(GP_COL);
         }
 
-        @Override
-        public void setColor(GridColor color) {
-            data[GP_COL] = GridColorUtils.gridColorToRString(color);
-        }
-
         @Override
         public double getFontSize() {
             return asDouble(data[GP_FONTSIZE]) * asDouble(data[GP_CEX]);
@@ -178,11 +179,6 @@ public final class GPar {
             return getGridColor(GP_FILL);
         }
 
-        @Override
-        public void setFillColor(GridColor color) {
-            data[GP_FILL] = GridColorUtils.gridColorToRString(color);
-        }
-
         private GridColor getGridColor(int index) {
             return GridColorUtils.gridColorFromString(RRuntime.asString(data[index]));
         }
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/GridContext.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/GridContext.java
index 2980378933908efae26bb8a345024acd57714529..ec68fcff20f2af94b77f30ea667e8bb8b0164465 100644
--- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/GridContext.java
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/GridContext.java
@@ -22,6 +22,7 @@
  */
 package com.oracle.truffle.r.library.fastrGrid;
 
+import com.oracle.truffle.r.library.fastrGrid.device.BufferedJFrameDevice;
 import com.oracle.truffle.r.library.fastrGrid.device.GridDevice;
 import com.oracle.truffle.r.library.fastrGrid.device.JFrameDevice;
 
@@ -43,7 +44,7 @@ public final class GridContext {
 
     public GridDevice getCurrentDevice() {
         if (currentDevice == null) {
-            currentDevice = new JFrameDevice();
+            currentDevice = new BufferedJFrameDevice(new JFrameDevice());
         }
         return currentDevice;
     }
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/GridState.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/GridState.java
index 5d840cbf68fcdea0a1a4b761ee232408bc0d8824..fbedb1f85323c1fcee0b6015e237fd051362ec4f 100644
--- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/GridState.java
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/GridState.java
@@ -23,6 +23,7 @@ public final class GridState {
     private REnvironment gridEnv;
     private double scale = 1;
     private boolean deviceInitialized;
+    private int devHoldCount;
 
     /**
      * Current grob being drawn (for determining the list of grobs to search when evaluating a
@@ -33,6 +34,15 @@ public final class GridState {
     GridState() {
     }
 
+    public int getDevHoldCount() {
+        return devHoldCount;
+    }
+
+    public int setDevHoldCount(int devHoldCount) {
+        this.devHoldCount = devHoldCount;
+        return devHoldCount;
+    }
+
     public void init(REnvironment gridEnv, GridDevice currentDevice) {
         this.gridEnv = gridEnv;
         this.currentGrob = RNull.instance;
@@ -40,14 +50,11 @@ public final class GridState {
     }
 
     void initGPar(GridDevice currentDevice) {
-        gpar = GPar.createNew();
-        currentDevice.initDrawingContext(GPar.asDrawingContext(gpar));
+        gpar = GPar.createNew(currentDevice);
     }
 
     public static DrawingContext getInitialGPar(GridDevice device) {
-        DrawingContext result = GPar.asDrawingContext(GPar.createNew());
-        device.initDrawingContext(result);
-        return result;
+        return GPar.asDrawingContext(GPar.createNew(device));
     }
 
     public RList getGpar() {
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LPoints.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LPoints.java
index fb8977b780450c1967a5dc81b0de4272ea14640a..d4b71e0a5baadde23ac35c280a5362ba0fe7f908 100644
--- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LPoints.java
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LPoints.java
@@ -73,11 +73,12 @@ public abstract class LPoints extends RExternalBuiltinNode.Arg4 {
 
         // Note: unlike in other drawing primitives, we only consider length of x
         int length = unitLength.execute(xVec);
+        PointDrawingContext pointDrawingCtx = new PointDrawingContext(drawingCtx, drawingCtx.getFillColor(), drawingCtx.getFillColor());
         for (int i = 0; i < length; i++) {
             Point loc = TransformMatrix.transLocation(Point.fromUnits(unitToInches, xVec, yVec, i, conversionCtx), vpTransform.transform);
             double size = unitToInches.convertWidth(sizeVec, i, conversionCtx);
             if (loc.isFinite() && Double.isFinite(size)) {
-                drawSymbol(drawingCtx, dev, cex, pchVec.getDataAt(i % pchVec.getLength()), size, loc.x, loc.y);
+                pointDrawingCtx = drawSymbol(pointDrawingCtx, dev, cex, pchVec.getDataAt(i % pchVec.getLength()), size, loc.x, loc.y);
             }
         }
         return RNull.instance;
@@ -85,38 +86,31 @@ public abstract class LPoints extends RExternalBuiltinNode.Arg4 {
 
     // transcribed from engine.c function GESymbol
 
-    private void drawSymbol(DrawingContext drawingCtx, GridDevice dev, double cex, int pch, double size, double x, double y) {
+    private PointDrawingContext drawSymbol(PointDrawingContext drawingCtx, GridDevice dev, double cex, int pch, double size, double x, double y) {
         // pch 0 - 25 are interpreted as geometrical shapes, pch from ascii code of ' ' are
         // interpreted as corresponding ascii character, which should be drawn
         switch (pch) {
             case 46:
-                drawDot(drawingCtx, dev, cex, x, y);
-                break;
+                return drawDot(drawingCtx, dev, cex, x, y);
             case 1:
-                drawOctahedron(drawingCtx, dev, GridColor.TRANSPARENT, size, x, y);
-                break;
+                return drawOctahedron(drawingCtx, dev, GridColor.TRANSPARENT, size, x, y);
             case 16:
-                drawOctahedron(drawingCtx, dev, drawingCtx.getColor(), size, x, y);
-                break;
+                return drawOctahedron(drawingCtx, dev, drawingCtx.getWrapped().getColor(), size, x, y);
             default:
                 throw RInternalError.unimplemented("grid.points unimplemented symbol " + pch);
         }
     }
 
-    private static void drawOctahedron(DrawingContext drawingCtx, GridDevice dev, GridColor fill, double size, double x, double y) {
-        GridColor originalFill = drawingCtx.getFillColor();
-        drawingCtx.setFillColor(fill);
+    private static PointDrawingContext drawOctahedron(PointDrawingContext drawingCtxIn, GridDevice dev, GridColor fill, double size, double x, double y) {
+        PointDrawingContext drawingCtx = drawingCtxIn.update(drawingCtxIn.getWrapped().getColor(), fill);
         dev.drawCircle(drawingCtx, x, y, RADIUS * size);
-        drawingCtx.setFillColor(originalFill);
+        return drawingCtx;
     }
 
-    private static void drawDot(DrawingContext drawingCtx, GridDevice dev, double cex, double x, double y) {
+    private static PointDrawingContext drawDot(PointDrawingContext drawingCtxIn, GridDevice dev, double cex, double x, double y) {
         // NOTE: we are *filling* a rect with the current colour (we are not drawing the border AND
         // we are not using the current fill colour)
-        GridColor originalFill = drawingCtx.getFillColor();
-        drawingCtx.setFillColor(drawingCtx.getColor());
-        drawingCtx.setColor(GridColor.TRANSPARENT);
-
+        PointDrawingContext drawingCtx = drawingCtxIn.update(GridColor.TRANSPARENT, drawingCtxIn.getWrapped().getColor());
         /*
          * The idea here is to use a 0.01" square, but to be of at least one device unit in each
          * direction, assuming that corresponds to pixels. That may be odd if pixels are not square,
@@ -135,8 +129,57 @@ public abstract class LPoints extends RExternalBuiltinNode.Arg4 {
             yc = 0.5;
         }
         dev.drawRect(drawingCtx, x - xc, y - yc, x + xc, y + yc);
+        return drawingCtx;
+    }
+
+    private static final class PointDrawingContext implements DrawingContext {
+        private final DrawingContext inner;
+        private final GridColor color;
+        private final GridColor fillColor;
+
+        private PointDrawingContext(DrawingContext inner, GridColor color, GridColor fillColor) {
+            this.inner = inner;
+            this.color = color;
+            this.fillColor = fillColor;
+        }
+
+        // This allows to re-use the existing instance if it would have the same parameters. The
+        // assumption is that the users will actually draw many points in a row with the same
+        // parameters.
+        private PointDrawingContext update(GridColor color, GridColor fillColor) {
+            if (this.color.equals(color) && this.fillColor.equals(fillColor)) {
+                return this;
+            }
+            return new PointDrawingContext(inner, color, fillColor);
+        }
+
+        @Override
+        public GridLineType getLineType() {
+            return inner.getLineType();
+        }
 
-        drawingCtx.setColor(drawingCtx.getFillColor());
-        drawingCtx.setFillColor(originalFill);
+        @Override
+        public GridColor getColor() {
+            return color;
+        }
+
+        @Override
+        public double getFontSize() {
+            return inner.getFontSize();
+        }
+
+        @Override
+        public double getLineHeight() {
+            return inner.getLineHeight();
+        }
+
+        @Override
+        public GridColor getFillColor() {
+            return fillColor;
+        }
+
+        private DrawingContext getWrapped() {
+            return inner;
+        }
     }
 }
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/device/BufferedJFrameDevice.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/device/BufferedJFrameDevice.java
new file mode 100644
index 0000000000000000000000000000000000000000..f85a04b4c446380eb41c4c2724692f4214e59215
--- /dev/null
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/device/BufferedJFrameDevice.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.truffle.r.library.fastrGrid.device;
+
+import java.awt.image.BufferStrategy;
+import java.util.ArrayList;
+
+/**
+ * Decorator for {@link JFrameDevice} that implements {@link #hold()} and {@link #flush()}. Those
+ * methods open/draw a 2D graphics buffer, while the buffer is open, any drawing is done in the
+ * buffer not on the screen and we also record any drawing code to be able to replay it if the
+ * buffer happens to loose contents, which is a possibility mentioned in the documentation. Note: we
+ * rely on the fact that {@linkl DrawingContext} is immutable.
+ */
+public class BufferedJFrameDevice implements GridDevice {
+    private final JFrameDevice inner;
+    private BufferStrategy buffer;
+    private ArrayList<Runnable> drawActions;
+
+    public BufferedJFrameDevice(JFrameDevice inner) {
+        this.inner = inner;
+    }
+
+    @Override
+    public void openNewPage() {
+        inner.openNewPage();
+    }
+
+    @Override
+    public void hold() {
+        if (buffer != null) {
+            return; // already buffering
+        }
+        buffer = inner.getCurrentFrame().getBufferStrategy();
+        if (buffer == null) {
+            inner.getCurrentFrame().createBufferStrategy(2);
+            buffer = inner.getCurrentFrame().getBufferStrategy();
+        }
+        if (drawActions == null) {
+            drawActions = new ArrayList<>();
+        } else {
+            drawActions.clear();
+        }
+        inner.initGraphics(buffer.getDrawGraphics());
+    }
+
+    @Override
+    public void flush() {
+        if (buffer == null) {
+            return;
+        }
+
+        buffer.show();
+        // re-draw the buffer if the contents were lost
+        while (buffer.contentsLost()) {
+            inner.initGraphics(buffer.getDrawGraphics());
+            for (Runnable drawAction : drawActions) {
+                drawAction.run();
+            }
+            buffer.show();
+        }
+
+        inner.initGraphics(inner.getCurrentFrame().getGraphics());
+        buffer.dispose();
+        buffer = null;
+    }
+
+    @Override
+    public void drawRect(DrawingContext ctx, double leftX, double topY, double width, double height) {
+        inner.drawRect(ctx, leftX, topY, width, height);
+        if (buffer != null) {
+            drawActions.add(() -> inner.drawRect(ctx, leftX, topY, width, height));
+        }
+    }
+
+    @Override
+    public void drawPolyLines(DrawingContext ctx, double[] x, double[] y, int startIndex, int length) {
+        inner.drawPolyLines(ctx, x, y, startIndex, length);
+        if (buffer != null) {
+            drawActions.add(() -> inner.drawPolyLines(ctx, x, y, startIndex, length));
+        }
+    }
+
+    @Override
+    public void drawCircle(DrawingContext ctx, double centerX, double centerY, double radius) {
+        inner.drawCircle(ctx, centerX, centerY, radius);
+        if (buffer != null) {
+            drawActions.add(() -> inner.drawCircle(ctx, centerX, centerY, radius));
+        }
+    }
+
+    @Override
+    public void drawString(DrawingContext ctx, double leftX, double bottomY, double rotationAnticlockWise, String text) {
+        inner.drawString(ctx, leftX, bottomY, rotationAnticlockWise, text);
+        if (buffer != null) {
+            drawActions.add(() -> inner.drawString(ctx, leftX, bottomY, rotationAnticlockWise, text));
+        }
+    }
+
+    @Override
+    public double getWidth() {
+        return inner.getWidth();
+    }
+
+    @Override
+    public double getHeight() {
+        return inner.getHeight();
+    }
+
+    @Override
+    public double getStringWidth(DrawingContext ctx, String text) {
+        return inner.getStringWidth(ctx, text);
+    }
+
+    @Override
+    public double getStringHeight(DrawingContext ctx, String text) {
+        return inner.getStringHeight(ctx, text);
+    }
+}
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/device/DrawingContext.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/device/DrawingContext.java
index f3d42fa703d922981c033f164f3970438fc96131..b3baf6db3cbbc86c93dfafeca313c5b5e77b581f 100644
--- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/device/DrawingContext.java
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/device/DrawingContext.java
@@ -23,7 +23,8 @@
 package com.oracle.truffle.r.library.fastrGrid.device;
 
 /**
- * Defines parameters for drawing, like color, line style etc.
+ * Defines parameters for drawing, like color, line style etc. The implementations must be
+ * immutable.
  */
 public interface DrawingContext {
     double INCH_TO_POINTS_FACTOR = 72.27;
@@ -52,12 +53,10 @@ public interface DrawingContext {
 
     GridLineType getLineType();
 
-    GridColor getColor();
-
     /**
-     * Alows to set the color drawing color of shape borders, lines and text.
+     * Drawing color of shape borders, lines and text.
      */
-    void setColor(GridColor color);
+    GridColor getColor();
 
     /**
      * Gets the font size in points.
@@ -71,10 +70,8 @@ public interface DrawingContext {
      */
     double getLineHeight();
 
-    GridColor getFillColor();
-
     /**
-     * Alows to set the fill color of shapes.
+     * The fill color of shapes.
      */
-    void setFillColor(GridColor color);
+    GridColor getFillColor();
 }
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/device/DrawingContextDefaults.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/device/DrawingContextDefaults.java
new file mode 100644
index 0000000000000000000000000000000000000000..b7043cbcd4d5266b066b79931aa0940fa78bdb0f
--- /dev/null
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/device/DrawingContextDefaults.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.truffle.r.library.fastrGrid.device;
+
+/**
+ * Allows the device to communicate the default values for its initial {@link DrawingContext}. The
+ * format of the values is the same as accepted by the {@code gpar()} function in R.
+ */
+public class DrawingContextDefaults {
+    public String fillColor = "transparent";
+    public String color = "black";
+}
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/device/GridColor.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/device/GridColor.java
index e4cdd7517aca69d5ef477e040ab81279f046e6b4..4b42cdce8cc4db3f9ff42e759f6a1f73a12f229c 100644
--- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/device/GridColor.java
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/device/GridColor.java
@@ -55,4 +55,17 @@ public class GridColor {
     public int getAlpha() {
         return (value >> 24) & 0xff;
     }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (!(obj instanceof GridColor)) {
+            return false;
+        }
+        return value == ((GridColor) obj).value;
+    }
+
+    @Override
+    public int hashCode() {
+        return value;
+    }
 }
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/device/GridDevice.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/device/GridDevice.java
index 9e37a873bb46214d7035d2fefb13467cd7df174e..91593aebf66aa54f9ccfa66126d54cfc7eb74b67 100644
--- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/device/GridDevice.java
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/device/GridDevice.java
@@ -31,6 +31,21 @@ import static com.oracle.truffle.r.library.fastrGrid.device.DrawingContext.INCH_
 public interface GridDevice {
     void openNewPage();
 
+    /**
+     * If the device is capable of buffering, calling {@code hold} should start buffering, e.g.
+     * nothing is displayed on the device, until {@link #flush()} is called.
+     */
+    default void hold() {
+    }
+
+    /**
+     * Should display the whole buffer at once.
+     *
+     * @see #hold()
+     */
+    default void flush() {
+    }
+
     void drawRect(DrawingContext ctx, double leftX, double topY, double width, double height);
 
     /**
@@ -58,12 +73,11 @@ public interface GridDevice {
     double getHeight();
 
     /**
-     * May change the default values the of the initial drawing context instance.
-     * 
-     * @param ctx instance of drawing context to be altered.
+     * May change the default values the of the initial drawing context instance. Must return
+     * non-null value.
      */
-    default void initDrawingContext(DrawingContext ctx) {
-        // nop
+    default DrawingContextDefaults getDrawingContextDefaults() {
+        return new DrawingContextDefaults();
     }
 
     double getStringWidth(DrawingContext ctx, String text);
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/device/JFrameDevice.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/device/JFrameDevice.java
index 8821b8dd0f0d05d46e9d860e594b480aebe22065..2ac24f74a69f39b470acd028cf1724c54ef24a9d 100644
--- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/device/JFrameDevice.java
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/device/JFrameDevice.java
@@ -28,6 +28,7 @@ import java.awt.BasicStroke;
 import java.awt.BorderLayout;
 import java.awt.Color;
 import java.awt.Dimension;
+import java.awt.Graphics;
 import java.awt.Graphics2D;
 import java.awt.HeadlessException;
 import java.awt.Paint;
@@ -73,12 +74,7 @@ public class JFrameDevice implements GridDevice {
         if (currentFrame == null) {
             currentFrame = new FastRFrame();
             currentFrame.setVisible(true);
-            graphics = (Graphics2D) currentFrame.getGraphics();
-            graphics.translate(0, currentFrame.getHeight());
-            graphics.scale(POINTS_IN_INCH, -POINTS_IN_INCH);
-            graphics.setStroke(new BasicStroke((float) (1d / POINTS_IN_INCH)));
-            graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
-            graphics.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
+            initGraphics(currentFrame.getGraphics());
         } else {
             noTranform(() -> {
                 graphics.clearRect(0, 0, currentFrame.getWidth(), currentFrame.getHeight());
@@ -156,6 +152,22 @@ public class JFrameDevice implements GridDevice {
         });
     }
 
+    FastRFrame getCurrentFrame() {
+        return currentFrame;
+    }
+
+    void initGraphics(Graphics newGraphics) {
+        if (graphics != null) {
+            graphics.dispose();
+        }
+        graphics = (Graphics2D) newGraphics;
+        graphics.translate(0, currentFrame.getHeight());
+        graphics.scale(POINTS_IN_INCH, -POINTS_IN_INCH);
+        graphics.setStroke(new BasicStroke((float) (1d / POINTS_IN_INCH)));
+        graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
+        graphics.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
+    }
+
     private void drawShape(DrawingContext drawingCtx, Shape shape) {
         Paint paint = graphics.getPaint();
         graphics.setPaint(fromGridColor(drawingCtx.getFillColor()));
@@ -223,7 +235,7 @@ public class JFrameDevice implements GridDevice {
         longdashedStroke = new BasicStroke(defaultWidth, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_MITER, 10f, new float[]{2f * dashSize}, 0f);
     }
 
-    private static class FastRFrame extends JFrame {
+    static class FastRFrame extends JFrame {
         private static final long serialVersionUID = 1L;
         private final Dimension framePreferredSize = new Dimension(720, 720);
         private final JPanel fastRComponent = new JPanel();
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/grDevices/DevHoldFlush.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/grDevices/DevHoldFlush.java
new file mode 100644
index 0000000000000000000000000000000000000000..853f82bcf3b07e76bb14ff163b5c5ce2c2e811cd
--- /dev/null
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/grDevices/DevHoldFlush.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.truffle.r.library.fastrGrid.grDevices;
+
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.numericValue;
+
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+import com.oracle.truffle.api.dsl.Specialization;
+import com.oracle.truffle.r.library.fastrGrid.GridContext;
+import com.oracle.truffle.r.library.fastrGrid.GridState;
+import com.oracle.truffle.r.nodes.builtin.RExternalBuiltinNode;
+
+public abstract class DevHoldFlush extends RExternalBuiltinNode.Arg1 {
+    static {
+        Casts casts = new Casts(DevHoldFlush.class);
+        casts.arg(0).mustBe(numericValue()).asIntegerVector().findFirst();
+    }
+
+    public static DevHoldFlush create() {
+        return DevHoldFlushNodeGen.create();
+    }
+
+    @Specialization
+    @TruffleBoundary
+    int doInteger(int num) {
+        GridState gridState = GridContext.getContext().getGridState();
+        int result = gridState.getDevHoldCount();
+        if (num < 0) {
+            result = gridState.setDevHoldCount(Math.max(0, result + num));
+            if (result == 0) {
+                GridContext.getContext().getCurrentDevice().flush();
+            }
+        } else if (num > 0) {
+            if (result == 0) {
+                GridContext.getContext().getCurrentDevice().hold();
+            }
+            result = gridState.setDevHoldCount(result + num);
+        }
+        return result;
+    }
+}