diff --git a/src/U8glib-HAL.h b/src/U8glib-HAL.h
index 527c34e..b850ea6 100755
--- a/src/U8glib-HAL.h
+++ b/src/U8glib-HAL.h
@@ -935,6 +935,17 @@ class U8GLIB_SSD1309_128X64 : public U8GLIB {
   void init(uint8_t options = U8G_I2C_OPT_NONE) { U8GLIB::init(&u8g_dev_ssd1309_128x64_i2c, options); }
 };
 
+class U8GLIB_SSD1309_128X64_F : public U8GLIB {
+public:
+  U8GLIB_SSD1309_128X64_F() : U8GLIB() { }
+  U8GLIB_SSD1309_128X64_F(uint8_t sck, uint8_t mosi, uint8_t cs, uint8_t a0, uint8_t reset = U8G_PIN_NONE) { init(sck, mosi, cs, a0, reset); }
+  U8GLIB_SSD1309_128X64_F(uint8_t cs, uint8_t a0, uint8_t reset = U8G_PIN_NONE) { init(cs, a0, reset); }
+  U8GLIB_SSD1309_128X64_F(uint8_t options) { init(options); }
+  void init(uint8_t sck, uint8_t mosi, uint8_t cs, uint8_t a0, uint8_t reset = U8G_PIN_NONE) { U8GLIB::init(&u8g_dev_ssd1309_128x64_f_sw_spi, sck, mosi, cs, a0, reset); }
+  void init(uint8_t cs, uint8_t a0, uint8_t reset = U8G_PIN_NONE) { U8GLIB::init(&u8g_dev_ssd1309_128x64_f_hw_spi, cs, a0, reset); }
+  void init(uint8_t options = U8G_I2C_OPT_NONE) { U8GLIB::init(&u8g_dev_ssd1309_128x64_f_i2c, options); }
+};
+
 class U8GLIB_SSD1306_128X32 : public U8GLIB {
 public:
   U8GLIB_SSD1306_128X32() : U8GLIB() { }
diff --git a/src/clib/u8g_dev_ssd1309_128x64.c b/src/clib/u8g_dev_ssd1309_128x64.c
index ac73ccc..0de8c92 100755
--- a/src/clib/u8g_dev_ssd1309_128x64.c
+++ b/src/clib/u8g_dev_ssd1309_128x64.c
@@ -31,100 +31,95 @@
   ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
   ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
-
 */
 
 #include "u8g.h"
 
+#include <string.h>
+#include <stdbool.h>
+
 #define WIDTH 128
 #define HEIGHT 64
 #define PAGE_HEIGHT 8
 
-
-/* ssd1309 ini sequence*/
-static const uint8_t u8g_dev_ssd1309_128x64_init_seq[] PROGMEM={
-	U8G_ESC_CS(0),             /* disable chip */
-	U8G_ESC_ADR(0),           /* instruction mode */
-	U8G_ESC_RST(1),           /* do reset low pulse with (1*16)+2 milliseconds */
-	U8G_ESC_CS(1),             /* enable chip */
-
-	0xfd,0x12,		/*Command Lock */
-	0xae,			/*Set Display Off */
-	0xd5,0xa0,		/*set Display Clock Divide Ratio/Oscillator Frequency */
-	0xa8,0x3f,		/*Set Multiplex Ratio */
-	0x3d,0x00,		/*Set Display Offset*/
-	0x40,			/*Set Display Start Line*/
-	0xa1,			/*Set Segment Re-Map*/
-	0xc8,			/*Set COM Output Scan Direction*/
-	0xda,0x12,		/*Set COM Pins Hardware Configuration*/
-	0x81,0xdf,		/*Set Current Control */
-	0xd9,0x82,		/*Set Pre-Charge Period */
-	0xdb,0x34,		/*Set VCOMH Deselect Level */
-	0xa4,			/*Set Entire Display On/Off */
-	0xa6,			/*Set Normal/Inverse Display*/
-	U8G_ESC_VCC(1),	/*Power up VCC & Stabilized */
-	U8G_ESC_DLY(50),
-	0xaf,			/*Set Display On */
-	U8G_ESC_DLY(50),
-	U8G_ESC_CS(0),             /* disable chip */
-	U8G_ESC_END                /* end of sequence */
+/* ssd1309 ini sequence */
+static const uint8_t u8g_dev_ssd1309_128x64_init_seq[] PROGMEM = {
+  U8G_ESC_CS(0),      /* disable chip */
+  U8G_ESC_ADR(0),     /* instruction mode */
+  U8G_ESC_RST(1),     /* do reset low pulse with (1*16)+2 milliseconds */
+  U8G_ESC_CS(1),      /* enable chip */
+
+  0xFD, 0x12,         /* Command Lock */
+  0xAE,               /* Set Display Off */
+  0xD5, 0xA0,         /* set Display Clock Divide Ratio/Oscillator Frequency */
+  0xA8, 0x3F,         /* Set Multiplex Ratio */
+  0x3D, 0x00,         /* Set Display Offset */
+  0x40,               /* Set Display Start Line */
+  0xA1,               /* Set Segment Re-Map */
+  0xC8,               /* Set COM Output Scan Direction */
+  0xDA, 0x12,         /* Set COM Pins Hardware Configuration */
+  0x81, 0xDF,         /* Set Current Control */
+  0xD9, 0x82,         /* Set Pre-Charge Period */
+  0xDB, 0x34,         /* Set VCOMH Deselect Level */
+  0xA4,               /* Set Entire Display On/Off */
+  0xA6,               /* Set Normal/Inverse Display */
+  U8G_ESC_VCC(1),     /* Power up VCC & Stabilized */
+  U8G_ESC_DLY(50),
+  0xAF,               /* Set Display On */
+  U8G_ESC_DLY(50),
+  U8G_ESC_CS(0),      /* disable chip */
+  U8G_ESC_END         /* end of sequence */
 };
 
 /* select one init sequence here */
-  #define u8g_dev_ssd1309_128x64_init_seq u8g_dev_ssd1309_128x64_init_seq
-
-
- static const uint8_t u8g_dev_ssd1309_128x64_data_start[] PROGMEM = {
-  U8G_ESC_ADR(0),           /* instruction mode */
-  U8G_ESC_CS(1),             /* enable chip */
-  0x010,		/* set upper 4 bit of the col adr to 0 */
-  0x000,		/* set lower 4 bit of the col adr to 4  */
-  U8G_ESC_END                /* end of sequence */
+#define u8g_dev_ssd1309_128x64_init_seq u8g_dev_ssd1309_128x64_init_seq
+
+static const uint8_t u8g_dev_ssd1309_128x64_data_start[] PROGMEM = {
+  U8G_ESC_ADR(0),     /* instruction mode */
+  U8G_ESC_CS(1),      /* enable chip */
+  0x10,               /* set upper 4 bit of the col adr to 0 */
+  0x00,               /* set lower 4 bit of the col adr to 4 */
+  U8G_ESC_END         /* end of sequence */
 };
 
 static const uint8_t u8g_dev_ssd13xx_sleep_on[] PROGMEM = {
-  U8G_ESC_ADR(0),           /* instruction mode */
-  U8G_ESC_CS(1),             /* enable chip */
-  0x0ae,		/* display off */
-  U8G_ESC_CS(0),             /* disable chip */
-  U8G_ESC_END                /* end of sequence */
+  U8G_ESC_ADR(0),     /* instruction mode */
+  U8G_ESC_CS(1),      /* enable chip */
+  0xAE,               /* display off */
+  U8G_ESC_CS(0),      /* disable chip */
+  U8G_ESC_END         /* end of sequence */
 };
 
 static const uint8_t u8g_dev_ssd13xx_sleep_off[] PROGMEM = {
-  U8G_ESC_ADR(0),           /* instruction mode */
-  U8G_ESC_CS(1),             /* enable chip */
-  0x0af,		/* display on */
-  U8G_ESC_DLY(50),       /* delay 50 ms */
-  U8G_ESC_CS(0),             /* disable chip */
-  U8G_ESC_END                /* end of sequence */
+  U8G_ESC_ADR(0),     /* instruction mode */
+  U8G_ESC_CS(1),      /* enable chip */
+  0xAF,               /* display on */
+  U8G_ESC_DLY(50),    /* delay 50 ms */
+  U8G_ESC_CS(0),      /* disable chip */
+  U8G_ESC_END         /* end of sequence */
 };
 
-uint8_t u8g_dev_ssd1309_128x64_fn(u8g_t *u8g, u8g_dev_t *dev, uint8_t msg, void *arg)
-{
-  switch(msg)
-  {
+uint8_t u8g_dev_ssd1309_128x64_fn(u8g_t *u8g, u8g_dev_t *dev, uint8_t msg, void *arg) {
+  switch(msg) {
     case U8G_DEV_MSG_INIT:
       u8g_InitCom(u8g, dev, U8G_SPI_CLK_CYCLE_300NS);
       u8g_WriteEscSeqP(u8g, dev, u8g_dev_ssd1309_128x64_init_seq);
       break;
     case U8G_DEV_MSG_STOP:
       break;
-    case U8G_DEV_MSG_PAGE_NEXT:
-      {
-        u8g_pb_t *pb = (u8g_pb_t *)(dev->dev_mem);
-        u8g_WriteEscSeqP(u8g, dev, u8g_dev_ssd1309_128x64_data_start);
-        u8g_WriteByte(u8g, dev, 0x0b0 | pb->p.page); /* select current page (SSD1306) */
-        u8g_SetAddress(u8g, dev, 1);           /* data mode */
-        if ( u8g_pb_WriteBuffer(pb, u8g, dev) == 0 )
-          return 0;
-        u8g_SetChipSelect(u8g, dev, 0);
-      }
-      break;
+    case U8G_DEV_MSG_PAGE_NEXT: {
+      u8g_pb_t *pb = (u8g_pb_t *)(dev->dev_mem);
+      u8g_WriteEscSeqP(u8g, dev, u8g_dev_ssd1309_128x64_data_start);
+      u8g_WriteByte(u8g, dev, 0xB0 | pb->p.page);   /* select current page (SSD1306) */
+      u8g_SetAddress(u8g, dev, 1);                  /* data mode */
+      if (u8g_pb_WriteBuffer(pb, u8g, dev) == 0) return 0;
+      u8g_SetChipSelect(u8g, dev, 0);
+    } break;
     case U8G_DEV_MSG_CONTRAST:
       u8g_SetChipSelect(u8g, dev, 1);
-      u8g_SetAddress(u8g, dev, 0);          /* instruction mode */
-      u8g_WriteByte(u8g, dev, 0x081);
-      u8g_WriteByte(u8g, dev, (*(uint8_t *)arg) ); /* 11 Jul 2015: fixed contrast calculation */
+      u8g_SetAddress(u8g, dev, 0);                  /* instruction mode */
+      u8g_WriteByte(u8g, dev, 0x81);
+      u8g_WriteByte(u8g, dev, (*(uint8_t *)arg) );  /* 11 Jul 2015: fixed contrast calculation */
       u8g_SetChipSelect(u8g, dev, 0);
       return 1;
     case U8G_DEV_MSG_SLEEP_ON:
@@ -137,6 +132,67 @@ uint8_t u8g_dev_ssd1309_128x64_fn(u8g_t *u8g, u8g_dev_t *dev, uint8_t msg, void
   return u8g_dev_pb8v1_base_fn(u8g, dev, msg, arg);
 }
 
+#define PAGE_COUNT (HEIGHT / PAGE_HEIGHT)
+
+/* The _f_ variant keeps an the contents of the full screen in an extra buffer,
+ * and sends it all at once after all pages were drawn.
+ * It also skips unchanged pages, and within those it skips unchanged columns at the beggining and end.
+ * All of this improves fps by reducing both total transferred data and screen rerenders.
+ * It requires an extra 1041 bytes of RAM.
+ */
+uint8_t u8g_dev_ssd1309_128x64_f_fn(u8g_t *u8g, u8g_dev_t *dev, uint8_t msg, void *arg) {
+  if (msg == U8G_DEV_MSG_PAGE_NEXT) {
+    u8g_pb_t *pb = (u8g_pb_t *)(dev->dev_mem);
+    uint8_t *buf = (uint8_t *)pb->buf;
+    uint8_t page = pb->p.page;
+    static uint8_t full_buffer[PAGE_COUNT][WIDTH] U8G_NOCOMMON;
+    static uint8_t column_start[PAGE_COUNT] = {},
+                   column_count[PAGE_COUNT] = {};
+
+    uint8_t start = 0;
+    while (start < WIDTH && full_buffer[page][start] == buf[start]) start++;
+    column_start[page] = start;
+
+    const bool page_changed = start < WIDTH;
+    if (page_changed) {
+      uint8_t end = WIDTH - 1;
+      while (end > start && full_buffer[page][end] == buf[end]) end--;
+      const uint8_t count = column_count[page] = end - start + 1;
+      memcpy(full_buffer[page] + start, buf + start, count);
+    }
+
+    /* Only send buffer after the last page has been cached */
+    const bool is_last_page = page == PAGE_COUNT - 1;
+    if (is_last_page) {
+      /* Full render to clear noise in LCD RAM on start */
+      static bool has_rendered = false;
+      for (uint8_t page = 0; page < PAGE_COUNT; page++) {
+        const uint8_t start = has_rendered ? column_start[page] : 0,
+                      count = has_rendered ? column_count[page] : WIDTH;
+        const bool page_changed = start < WIDTH;
+        if (page_changed) {
+          u8g_WriteEscSeqP(u8g, dev, u8g_dev_ssd1309_128x64_data_start);
+          u8g_WriteByte(u8g, dev, 0xB0 | page);               /* Select current page (SSD1306) */
+          if (start > 0) {
+            u8g_WriteByte(u8g, dev, 0x00 | (start & 0b1111)); /* Start column low nybble */
+            u8g_WriteByte(u8g, dev, 0x10 | (start >> 4));     /* Start column high nybble */
+          }
+          u8g_SetAddress(u8g, dev, 1);                        /* Data mode */
+          u8g_WriteSequence(u8g, dev, count, full_buffer[page] + start);
+        }
+      }
+      u8g_SetChipSelect(u8g, dev, 0);
+      has_rendered = true;
+    }
+    return u8g_dev_pb8v1_base_fn(u8g, dev, msg, arg);
+  }
+  return u8g_dev_ssd1309_128x64_fn(u8g, dev, msg, arg);
+}
+
 U8G_PB_DEV(u8g_dev_ssd1309_128x64_hw_spi, WIDTH, HEIGHT, PAGE_HEIGHT, u8g_dev_ssd1309_128x64_fn, U8G_COM_HW_SPI);
 U8G_PB_DEV(u8g_dev_ssd1309_128x64_sw_spi, WIDTH, HEIGHT, PAGE_HEIGHT, u8g_dev_ssd1309_128x64_fn, U8G_COM_SW_SPI);
 U8G_PB_DEV(u8g_dev_ssd1309_128x64_i2c, WIDTH, HEIGHT, PAGE_HEIGHT, u8g_dev_ssd1309_128x64_fn, U8G_COM_SSD_I2C);
+
+U8G_PB_DEV(u8g_dev_ssd1309_128x64_f_hw_spi, WIDTH, HEIGHT, PAGE_HEIGHT, u8g_dev_ssd1309_128x64_f_fn, U8G_COM_HW_SPI);
+U8G_PB_DEV(u8g_dev_ssd1309_128x64_f_sw_spi, WIDTH, HEIGHT, PAGE_HEIGHT, u8g_dev_ssd1309_128x64_f_fn, U8G_COM_SW_SPI);
+U8G_PB_DEV(u8g_dev_ssd1309_128x64_f_i2c, WIDTH, HEIGHT, PAGE_HEIGHT, u8g_dev_ssd1309_128x64_f_fn, U8G_COM_SSD_I2C);