diff --git examples/google-ng.js examples/google-ng.js
index fa2ba35..9359b77 100644
--- examples/google-ng.js
+++ examples/google-ng.js
@@ -5,16 +5,17 @@ function init() {
     map.addControl(new OpenLayers.Control.LayerSwitcher());
     
     var gphy = new OpenLayers.Layer.GoogleNG(
-        {type: google.maps.MapTypeId.TERRAIN}
+        {type: google.maps.MapTypeId.TERRAIN, transitionEffect: "resize"}
     );
     var gmap = new OpenLayers.Layer.GoogleNG(
-        // ROADMAP, the default
+        {/*type: google.maps.MapTypeId.ROADMAP,*/ transitionEffect: "resize"}
+        
     );
     var ghyb = new OpenLayers.Layer.GoogleNG(
-        {type: google.maps.MapTypeId.HYBRID}
+        {type: google.maps.MapTypeId.HYBRID, transitionEffect: "resize"}
     );
     var gsat = new OpenLayers.Layer.GoogleNG(
-        {type: google.maps.MapTypeId.SATELLITE}
+        {type: google.maps.MapTypeId.SATELLITE, transitionEffect: "resize"}
     );
 
     map.addLayers([gphy, gmap, ghyb, gsat]);
diff --git lib/OpenLayers/Layer/GoogleNG.js lib/OpenLayers/Layer/GoogleNG.js
index f380b72..ec8f8e0 100644
--- lib/OpenLayers/Layer/GoogleNG.js
+++ lib/OpenLayers/Layer/GoogleNG.js
@@ -19,13 +19,6 @@
 OpenLayers.Layer.GoogleNG = OpenLayers.Class(OpenLayers.Layer.XYZ, {
 
     /**
-     * Property: SUPPORTED_TRANSITIONS
-     * {Array} An immutable (that means don't change it!) list of supported 
-     *     transitionEffect values. This layer type supports none.
-     */
-    SUPPORTED_TRANSITIONS: [],
-    
-    /**
      * Property: serverResolutions
      * {Array} the resolutions provided by the Google API.
      */
@@ -193,7 +186,7 @@ OpenLayers.Layer.GoogleNG = OpenLayers.Class(OpenLayers.Layer.XYZ, {
             "Map Data &copy;" + new Date().getFullYear() + " " +
             myCopyrights.join(", ") + " - ";
         var center = this.map.getCenter();
-        center && center.transform(
+        center.transform(
             this.map.getProjectionObject(),
             new OpenLayers.Projection("EPSG:4326")
         );
diff --git lib/OpenLayers/Tile/Google.js lib/OpenLayers/Tile/Google.js
index b96f2d0..ae3248d 100644
--- lib/OpenLayers/Tile/Google.js
+++ lib/OpenLayers/Tile/Google.js
@@ -5,9 +5,7 @@
 
 
 /**
- * @requires OpenLayers/BaseTypes/Class.js
- * @requires OpenLayers/Util.js
- * @requires OpenLayers/Tile.js
+ * @requires OpenLayers/Tile/BackBufferable.js
  */
 
 /*
@@ -17,16 +15,23 @@
  * http://code.google.com/apis/maps/documentation/javascript/reference.html#MapType).
  *
  * Inherits from:
- *  - <OpenLayers.Tile>
+ *  - <OpenLayers.Tile.BackBufferable>
  */
-OpenLayers.Tile.Google = OpenLayers.Class(OpenLayers.Tile, {
+OpenLayers.Tile.Google = OpenLayers.Class(OpenLayers.Tile.BackBufferable, {
     
     /**
-     * Property: node
-     * {DOMElement} The tile node from the MapType's getTile method
+     * Property: transparentImgRegEx
+     * {RegExp} Regular expression to check if the image src is a transparent
+     * image.
      */
-    node: null,
+    transparentImgRegEx: (/\/transparent\.png$/),
         
+    /**
+     * Property: frame
+     * {DOMElement} The Google tile is a div which contains one or more images. 
+     */ 
+    frame: null, 
+
     /** 
      * Constructor: OpenLayers.Tile.Google
      * Constructor for a new <OpenLayers.Tile.Google> instance.
@@ -38,7 +43,7 @@ OpenLayers.Tile.Google = OpenLayers.Class(OpenLayers.Tile, {
      * options - {Object}
      */   
     initialize: function(layer, position, bounds, options) {
-        OpenLayers.Tile.prototype.initialize.apply(this, [
+        OpenLayers.Tile.BackBufferable.prototype.initialize.apply(this, [
             layer, position, bounds, null, null, options
         ]);
     },
@@ -47,108 +52,150 @@ OpenLayers.Tile.Google = OpenLayers.Class(OpenLayers.Tile, {
      * APIMethod: destroy
      * Nullify references to prevent circular references and memory leaks.
      */
-    destroy:function() {
-        this.node && this.clear();
-        this.node = null;
-        OpenLayers.Tile.prototype.destroy.apply(this, arguments);
+    destroy: function() {
+        if (this.frame) {
+            this.releaseTile(this.frame);
+            this.frame = null;
+        }
+        OpenLayers.Tile.BackBufferable.prototype.destroy.apply(this, arguments);
     },
     
     /**
-     * Method: clone
-     *
-     * Parameters:
-     * obj - {<OpenLayers.Tile>} The tile to be cloned
-     *
-     * Returns:
-     * {<OpenLayers.Tile>} An exact clone of this <OpenLayers.Tile.Google>
-     */
-    clone: function (obj) {
-        if (obj == null) {
-            obj = new OpenLayers.Tile.Google(this.layer, 
-                                      this.position, 
-                                      this.bounds);
-        } 
-        
-        // catch any randomly tagged-on properties
-        OpenLayers.Util.applyDefaults(obj, this);
-        
-        obj.node = null;
-        
-        return obj;
-    },
-
-    /**
      * Method: draw
      * Check that a tile should be drawn, and draw it.
      * 
      * Returns:
-     * {Boolean} Always returns true.
+     * {Boolean} true when the tile needed to be drawn, false otherwise.
      */
     draw: function() {
         var layerType = OpenLayers.Layer.GoogleNG.mapObject.mapTypes[
             this.layer.type
         ];
-        if (layerType && OpenLayers.Tile.prototype.draw.apply(this, arguments)) {
+        var drawn = layerType &&
+            OpenLayers.Tile.BackBufferable.prototype.draw.apply(this, arguments);
+        if (drawn) {
             var xyz = this.layer.getXYZ(this.bounds);
             var point = new google.maps.Point(xyz.x, xyz.y);
 
-            // The hybrid tile consists of two images. For some reason, we have
-            // to make sure that the satellite image loads first, otherwise we
-            // occasionally get blank tiles for one of the two images. This is
-            // done by requesting the tile for just the satellite mapType
-            // first, before requesting the hybrid one.
-            //TODO revisit this - it may be a temporary issue with GMaps
-            var tempTile;
-            if (this.layer.type === google.maps.MapTypeId.HYBRID) {
-                tempTile = layerType.getTile(point, xyz.z, document);
+            if (this.isLoading) {
+                //if we're already loading, send 'reload' instead of 'loadstart'.
+                this.events.triggerEvent("reload"); 
+            } else {
+                this.isLoading = true;
+                this.events.triggerEvent("loadstart");
             }
+            this.frame = layerType.getTile(point, xyz.z, document);
+            var style = this.frame.style;
+            style.position = "absolute";
+            style.left = this.position.x + "px";
+            style.top = this.position.y + "px";
             
-            this.node = layerType.getTile(point, xyz.z, document);
-                        
-            this.isLoading = true;
-            this.events.triggerEvent("loadstart");
-
-            this.layer.div.appendChild(this.node);
-            
-            // We only modify what we need to - we expect the size to be set
-            // by getTile, and we have a test that will fail if this changes.
-            OpenLayers.Util.modifyDOMElement(
-                this.node, null, this.position, null, "absolute"
-            );  
-            
-            // The images inside the node returned from getTile seem to be
-            // preloaded already, so registering onload events on these images
-            // won't work. Instead, we trigger the loadend event immediately
-            // in the next cycle.
-            window.setTimeout(OpenLayers.Function.bind(function() {
-                this.isLoading = false;
-                // check for this.events - we may be destroyed already
-                this.events && this.events.triggerEvent("loadend"); 
-
-                // see hybrid tile issue above
-                //TODO revisit this - it may be a temporary issue with GMaps
-                if (tempTile) {
-                    layerType.releaseTile(tempTile);
-                }
-            }, this), 0);
+            var images = this.frame.getElementsByTagName("img");
+            var numLoadingImages = images.length;
+            var me = this;
+            function loaded(evt) {
+                var img = OpenLayers.Event.element(evt);
+                numLoadingImages = me.onImageLoad(img, numLoadingImages);
+            }
+            for (var i=images.length-1; i>=0; --i) {
+                OpenLayers.Event.observe(images[i], "load", loaded);
+            }
+            // check for layer div - layer may be destroyed already
+            this.layer.div && this.layer.div.appendChild(this.frame);
         }
-        return true;
+        return drawn;
     },
     
     /** 
      * Method: clear
      * Clear the tile of any bounds/position-related data so that it can 
-     *     be reused in a new location. To be implemented by subclasses.
+     *     be reused in a new location.
      */
     clear: function() {
-        if (this.node) {
-            this.node.parentNode &&
-                this.node.parentNode.removeChild(this.node);
-            OpenLayers.Layer.GoogleNG.mapObject.mapTypes[
-                this.layer.type
-            ].releaseTile(this.node);
+        var frame = this.frame, layer = this.layer;
+        if (frame) {
+            if (frame.parentNode === layer.div) {
+                layer.div.removeChild(frame);
+            }
+            window.setTimeout(OpenLayers.Function.bind(function() {
+                this.releaseTile(frame);
+            }, this), 0);
+            this.frame = null;
+        }
+    },
+    
+    /**
+     * Method: releaseTile
+     * Unregisters load listeners and releases the tile on the GMaps API
+     *
+     * Parameters:
+     * tile - {HTMLDivElement} a tile created by the GMaps API's getTile method
+     */
+    releaseTile: function(tile) {
+        var images = tile.getElementsByTagName("img");
+        for (var i=images.length-1; i>=0; --i) {
+            OpenLayers.Event.stopObservingElement(images[i]);
+        }
+        var type = OpenLayers.Layer.GoogleNG.mapObject.mapTypes[this.layer.type];
+        type.releaseTile(tile);
+    },
+    
+    /**
+     * Method: getTile
+     * Get the tile's markup.
+     *
+     * Returns:
+     * {DOMElement} The tile's markup
+     */
+    getTile: function() {
+        return this.frame;
+    },
+
+    /**
+     * Method: createBackBuffer
+     * Get this tile's markup for the <layer>'s backBufferDiv
+     *
+     * Returns:
+     * {DOMElement} the current tile content
+     */
+    createBackBuffer: function() {
+        var frame = this.frame;
+        this.layer.events.register("loadend", this, function() {
+            this.layer.events.unregister("loadend", this, arguments.callee);
+            this.releaseTile(frame);
+        });
+        this.frame = null;
+        var style, images = frame.getElementsByTagName("img");
+        for (var i=images.length-1; i>=0; --i) {
+            // make images scaleable
+            style = images[i].style;
+            style.width = "100%";
+            style.height = "100%";
+        }
+        return frame;
+    },
+    
+    /**
+     * Method: onImageLoad
+     * Handler for loading/loaded images
+     *
+     * Parameters:
+     * img - {HTMLImageElement} the loaded image
+     * numLoadingImages - {Integer}
+     *
+     * Returns:
+     * {Integer} the new number of loading images
+     */
+    onImageLoad: function(img, numLoadingImages) {
+        if (!this.transparentImgRegEx.test(img.src)) {
+            numLoadingImages--;
+            if (numLoadingImages == 0) {
+                this.isLoading = false;
+                this.events.triggerEvent("loadend");
+            }
         }
+        return numLoadingImages;
     },
     
     CLASS_NAME: "OpenLayers.Tile.Google"
-});
+});
\ No newline at end of file
diff --git tests/Tile/Google.html tests/Tile/Google.html
index e171084..467c5cc 100644
--- tests/Tile/Google.html
+++ tests/Tile/Google.html
@@ -20,20 +20,6 @@
         t.ok( tile.bounds.equals(bounds), "tile.bounds is set correctly");
     }
 
-    function test_clone (t) {
-        t.plan( 5 );
-        
-        tile = new OpenLayers.Tile.Google(layer, position, bounds);
-        tile.node = document.createElement("div");
-        var clone = tile.clone();
-    
-        t.ok( clone instanceof OpenLayers.Tile.Google, "OpenLayers.Tile.Google.clone returns Tile.Google object" );
-        t.ok( clone.layer == layer, "clone.layer is set correctly");
-        t.ok( clone.position.equals(position), "clone.position is set correctly");
-        t.ok( clone.bounds.equals(bounds), "clone.bounds is set correctly");
-        t.ok( !clone.node, "node not cloned");
-    }
-    
     function test_draw (t) {
         t.plan( 5 );
 
@@ -48,22 +34,40 @@
         tile.events.register("loadend", this, function() { 
             t.ok(true, "loadend triggered");
         });
-
+        // wait until GMaps API is available
         t.delay_call(1, function() {
             //this should trigger a "loadstart" event
             tile.draw();
 
-            t.ok( tile.node, "tile.draw creates a node");
-            // The two tests below will fail when getTile doesn't set the tile size
-            t.eq( tile.node.style.width, layer.tileSize.w+"px", "Image width is correct" );
-            t.eq( tile.node.style.height, layer.tileSize.h+"px", "Image height is correct" );
+            var frame = tile.frame;
+            t.ok(frame, "tile.draw creates a node");
+            t.eq(frame.style.left, tile.position.x+"px", "frame has correct left style");
+            t.eq(frame.style.width, tile.size.w+"px", "frame has correct width");
+            map.removeLayer(layer);
+            map.destroy();
         });       
+    }
+    
+    function test_getBackBuffer(t) {
+        t.plan(3);
+
+        var map = new OpenLayers.Map('map');
+        map.addLayer(layer);  
+
+        tile = new OpenLayers.Tile.Google(layer, position, bounds);
+        // wait until GMaps API is available
+        t.delay_call(1, function() {
+            tile.draw();
+        });
+        // wait until loadend fires
         t.delay_call(2, function() {
-            map.removeLayer(layer);
+            var frame = tile.frame;
+            var backBuffer = tile.getBackBuffer();
+            t.ok(backBuffer === frame, "frame returned as backBuffer");
+            t.eq(tile.frame, null, "reference to frame nullified");
+            t.eq(frame.firstChild.style.width, "100%", "backBuffer image width set to 100%");
             map.destroy();
-            /* wait until "loadend" fires */
-        });        
-        
+        });       
     }
         
   </script>
