diff --git a/Gruntfile.js b/Gruntfile.js
index a8d3e742..a16936a5 100644
--- a/Gruntfile.js
+++ b/Gruntfile.js
@@ -95,6 +95,7 @@ module.exports = function (grunt) {
'src/gameobjects/Button.js',
'src/gameobjects/Graphics.js',
'src/gameobjects/RenderTexture.js',
+ 'src/gameobjects/SpriteBatch.js',
'src/system/Canvas.js',
'src/system/StageScaleMode.js',
diff --git a/build/config.php b/build/config.php
index 121e0388..e17e2568 100644
--- a/build/config.php
+++ b/build/config.php
@@ -141,6 +141,7 @@
+
diff --git a/examples/assets/sprites/maggot.png b/examples/assets/sprites/maggot.png
new file mode 100644
index 00000000..c0b1c000
Binary files /dev/null and b/examples/assets/sprites/maggot.png differ
diff --git a/examples/wip/spritebatch1.js b/examples/wip/spritebatch1.js
new file mode 100644
index 00000000..07968109
--- /dev/null
+++ b/examples/wip/spritebatch1.js
@@ -0,0 +1,62 @@
+
+var game = new Phaser.Game(800, 600, Phaser.AUTO, 'phaser-example', { preload: preload, create: create, update: update });
+
+function preload() {
+
+ game.load.image('maggot', 'assets/sprites/maggot.png');
+
+}
+
+var batch;
+var dudeBoundsPadding = 100;
+var dudeBounds = new Phaser.Rectangle(-dudeBoundsPadding, -dudeBoundsPadding, 800 + dudeBoundsPadding * 2, 600 + dudeBoundsPadding * 2);
+var tick = 0;
+
+function create() {
+
+ batch = game.add.spriteBatch();
+
+ var total = (game.renderType === Phaser.WEBGL) ? 10000 : 100;
+
+ for (var i = 0; i < total; i++)
+ {
+ var dude = batch.create(game.world.randomX, game.world.randomY, 'maggot');
+
+ dude.anchor.set(0.5);
+ dude.scale.set(0.8 + Math.random() * 0.3);
+ dude.direction = Math.random() * Math.PI * 2;
+ dude.turningSpeed = Math.random() - 0.8;
+ dude.speed = (2 + Math.random() * 2) * 0.2;
+ dude.offset = Math.random() * 100;
+ }
+
+}
+
+function update() {
+
+ batch.forEach(updateMaggot, this, false);
+
+ tick += 0.1;
+
+}
+
+function updateMaggot(dude) {
+
+ dude.scale.y = 0.95 + Math.sin(tick + dude.offset) * 0.05
+ dude.direction += dude.turningSpeed * 0.01;
+ dude.position.x += Math.sin(dude.direction) * (dude.speed * dude.scale.y);
+ dude.position.y += Math.cos(dude.direction) * (dude.speed * dude.scale.y);
+ dude.rotation = -dude.direction + Math.PI;
+
+ // wrap the dudes by testing their bounds..
+ if (dude.position.x < dudeBounds.x)
+ dude.position.x += dudeBounds.width;
+ else if (dude.position.x > dudeBounds.x + dudeBounds.width)
+ dude.position.x -= dudeBounds.width;
+
+ if (dude.position.y < dudeBounds.y)
+ dude.position.y += dudeBounds.height;
+ else if (dude.position.y > dudeBounds.y + dudeBounds.height)
+ dude.position.y -= dudeBounds.height;
+
+}
diff --git a/labs/code/004 maggot batch.js b/labs/code/004 maggot batch.js
new file mode 100644
index 00000000..07968109
--- /dev/null
+++ b/labs/code/004 maggot batch.js
@@ -0,0 +1,62 @@
+
+var game = new Phaser.Game(800, 600, Phaser.AUTO, 'phaser-example', { preload: preload, create: create, update: update });
+
+function preload() {
+
+ game.load.image('maggot', 'assets/sprites/maggot.png');
+
+}
+
+var batch;
+var dudeBoundsPadding = 100;
+var dudeBounds = new Phaser.Rectangle(-dudeBoundsPadding, -dudeBoundsPadding, 800 + dudeBoundsPadding * 2, 600 + dudeBoundsPadding * 2);
+var tick = 0;
+
+function create() {
+
+ batch = game.add.spriteBatch();
+
+ var total = (game.renderType === Phaser.WEBGL) ? 10000 : 100;
+
+ for (var i = 0; i < total; i++)
+ {
+ var dude = batch.create(game.world.randomX, game.world.randomY, 'maggot');
+
+ dude.anchor.set(0.5);
+ dude.scale.set(0.8 + Math.random() * 0.3);
+ dude.direction = Math.random() * Math.PI * 2;
+ dude.turningSpeed = Math.random() - 0.8;
+ dude.speed = (2 + Math.random() * 2) * 0.2;
+ dude.offset = Math.random() * 100;
+ }
+
+}
+
+function update() {
+
+ batch.forEach(updateMaggot, this, false);
+
+ tick += 0.1;
+
+}
+
+function updateMaggot(dude) {
+
+ dude.scale.y = 0.95 + Math.sin(tick + dude.offset) * 0.05
+ dude.direction += dude.turningSpeed * 0.01;
+ dude.position.x += Math.sin(dude.direction) * (dude.speed * dude.scale.y);
+ dude.position.y += Math.cos(dude.direction) * (dude.speed * dude.scale.y);
+ dude.rotation = -dude.direction + Math.PI;
+
+ // wrap the dudes by testing their bounds..
+ if (dude.position.x < dudeBounds.x)
+ dude.position.x += dudeBounds.width;
+ else if (dude.position.x > dudeBounds.x + dudeBounds.width)
+ dude.position.x -= dudeBounds.width;
+
+ if (dude.position.y < dudeBounds.y)
+ dude.position.y += dudeBounds.height;
+ else if (dude.position.y > dudeBounds.y + dudeBounds.height)
+ dude.position.y -= dudeBounds.height;
+
+}
diff --git a/src/Phaser.js b/src/Phaser.js
index 68456755..14074f21 100644
--- a/src/Phaser.js
+++ b/src/Phaser.js
@@ -35,6 +35,7 @@ var Phaser = Phaser || {
CANVAS_FILTER: 14,
WEBGL_FILTER: 15,
ELLIPSE: 16,
+ SPRITEBATCH: 16,
NONE: 0,
LEFT: 1,
diff --git a/src/gameobjects/GameObjectFactory.js b/src/gameobjects/GameObjectFactory.js
index e07f6777..39787fc2 100644
--- a/src/gameobjects/GameObjectFactory.js
+++ b/src/gameobjects/GameObjectFactory.js
@@ -109,6 +109,24 @@ Phaser.GameObjectFactory.prototype = {
},
+ /**
+ * A Group is a container for display objects that allows for fast pooling, recycling and collision checks.
+ *
+ * @method Phaser.GameObjectFactory#spriteBatch
+ * @param {any} parent - The parent Group or DisplayObjectContainer that will hold this group, if any.
+ * @param {string} [name='group'] - A name for this Group. Not used internally but useful for debugging.
+ * @param {boolean} [addToStage=false] - If set to true this Group will be added directly to the Game.Stage instead of Game.World.
+ * @return {Phaser.Group} The newly created group.
+ */
+ spriteBatch: function (parent, name, addToStage) {
+
+ if (typeof name === 'undefined') { name = 'group'; }
+ if (typeof addToStage === 'undefined') { addToStage = false; }
+
+ return new Phaser.SpriteBatch(this.game, parent, name, addToStage);
+
+ },
+
/**
* Creates a new Sound object.
*
diff --git a/src/gameobjects/SpriteBatch.js b/src/gameobjects/SpriteBatch.js
new file mode 100644
index 00000000..3eda688e
--- /dev/null
+++ b/src/gameobjects/SpriteBatch.js
@@ -0,0 +1,35 @@
+/**
+* @author Richard Davey
+* @copyright 2014 Photon Storm Ltd.
+* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
+*/
+
+/**
+* Phaser SpriteBatch constructor.
+* @class Phaser.SpriteBatch
+* @classdesc A Group is a container for display objects that allows for fast pooling and object recycling. Groups can be nested within other Groups and have their own local transforms.
+* @constructor
+* @param {Phaser.Game} game - A reference to the currently running game.
+* @param {Phaser.Group|Phaser.Sprite} parent - The parent Group, DisplayObject or DisplayObjectContainer that this Group will be added to. If undefined or null it will use game.world.
+* @param {string} [name=group] - A name for this Group. Not used internally but useful for debugging.
+* @param {boolean} [addToStage=false] - If set to true this Group will be added directly to the Game.Stage instead of Game.World.
+*/
+Phaser.SpriteBatch = function (game, parent, name, addToStage) {
+
+ PIXI.SpriteBatch.call(this);
+
+ Phaser.Group.call(this, game, parent, name, addToStage);
+
+ /**
+ * @property {number} type - Internal Phaser Type value.
+ * @protected
+ */
+ this.type = Phaser.SPRITEBATCH;
+
+};
+
+// Phaser.SpriteBatch.prototype = Object.create(Phaser.Group.prototype);
+
+Phaser.SpriteBatch.prototype = Phaser.Utils.extend(true, Phaser.SpriteBatch.prototype, Phaser.Group.prototype, PIXI.SpriteBatch.prototype);
+
+Phaser.SpriteBatch.prototype.constructor = Phaser.SpriteBatch;