diff --git a/examples/declarative/animation/basics/color-animation.qml b/examples/declarative/animation/basics/color-animation.qml
index a83dbab5697d19f38fd6aa08d1438a0c2b3219fa..9e0f29c4ecbd6287356f3bb19f18fbd635ac90fc 100644
--- a/examples/declarative/animation/basics/color-animation.qml
+++ b/examples/declarative/animation/basics/color-animation.qml
@@ -86,7 +86,7 @@ Item {
             width: parent.width; height: parent.height/2
             ImageParticle {
                 source: "images/star.png"
-                particles: ["star"]
+                groups: ["star"]
                 color: "#00333333"
                 SequentialAnimation on opacity {
                     loops: Animation.Infinite
@@ -95,7 +95,7 @@ Item {
                 }
             }
             Emitter {
-                particle: "star"
+                group: "star"
                 anchors.fill: parent
                 emitRate: parent.width / 50
                 lifeSpan: 5000
diff --git a/examples/declarative/flickr/flickr.qml b/examples/declarative/flickr/flickr.qml
index 14e4fcaedf969812beff897e3ee1d17af0a2ff1a..4397d0ad7651ff04f494b45a7ec74ca3a9b0bab1 100644
--- a/examples/declarative/flickr/flickr.qml
+++ b/examples/declarative/flickr/flickr.qml
@@ -56,7 +56,7 @@ Item {
             id: bgParticles
             anchors.fill: parent
             ImageParticle {
-                particles: ["trail"]
+                groups: ["trail"]
                 source: "content/images/particle.png"
                 color: "#1A1A6F"
                 alpha: 0.1
@@ -64,7 +64,7 @@ Item {
                 blueVariation: 0.8
             }
             Emitter {
-                particle: "drops"
+                group: "drops"
                 width: parent.width
                 emitRate: 0.5
                 lifeSpan: 20000
@@ -75,7 +75,7 @@ Item {
             }
             TrailEmitter {
                 follow: "drops"
-                particle: "trail"
+                group: "trail"
                 emitRatePerParticle: 18
                 size: 32
                 endSize: 0
diff --git a/examples/declarative/minehunt/MinehuntCore/Explosion.qml b/examples/declarative/minehunt/MinehuntCore/Explosion.qml
index 225c19d8a34775622a8607c4aa09e513f557805b..af98ad130981569a26235377b5be493c49ec1a66 100644
--- a/examples/declarative/minehunt/MinehuntCore/Explosion.qml
+++ b/examples/declarative/minehunt/MinehuntCore/Explosion.qml
@@ -48,14 +48,14 @@ Item {
         width: 40
         height: 40
         ImageParticle {
-            particles: ["star"]
+            groups: ["star"]
             source: "file:MinehuntCore/pics/star.png" // TODO: Use qrc path once QTBUG-21129 is fixed
         }
         Emitter {
             id: particles
             emitting: false
             anchors.centerIn: parent
-            particle: "star"
+            group: "star"
             speed: AngledDirection { angleVariation: 360; magnitude: 150; magnitudeVariation: 50 }
             emitRate: 200
             z: 100
diff --git a/examples/declarative/particles/allsmiles/smilefactory.qml b/examples/declarative/particles/allsmiles/smilefactory.qml
index fe651491b9e621093740c64bdfcbe59955d8f483..4b01862f99c54957778dcbe2fe63a06fbc3f8e40 100644
--- a/examples/declarative/particles/allsmiles/smilefactory.qml
+++ b/examples/declarative/particles/allsmiles/smilefactory.qml
@@ -48,7 +48,7 @@ Rectangle{
     ParticleSystem{id:sys}
     ImageParticle{
         system: sys
-        particles: ["goingLeft", "goingRight"]
+        groups: ["goingLeft", "goingRight"]
         source: "content/singlesmile.png"
         rotation: 90
         rotationSpeed: 90
@@ -56,7 +56,7 @@ Rectangle{
     }
     ImageParticle{
         system: sys
-        particles: ["goingDown"]
+        groups: ["goingDown"]
         source: "content/squarefacespriteXX.png"
         yVector: PointDirection{ y: 0.5; yVariation: 0.25; xVariation: 0.25; }
         rotation: 180
@@ -85,7 +85,7 @@ Rectangle{
         y: 120
         system: sys
         enabled: false
-        particle: "goingRight"
+        group: "goingRight"
         speed: PointDirection{ x: 100 }
         lifeSpan: 4000
         emitRate: 2
@@ -97,7 +97,7 @@ Rectangle{
         y: 240
         system: sys
         enabled: false
-        particle: "goingLeft"
+        group: "goingLeft"
         speed: PointDirection{ x: -100 }
         lifeSpan: 4000
         emitRate: 2
@@ -109,7 +109,7 @@ Rectangle{
         y: 360
         system: sys
         enabled: false
-        particle: "goingDown"
+        group: "goingDown"
         speed: PointDirection{ x: 100 }
         lifeSpan: 4000
         emitRate: 2
diff --git a/examples/declarative/particles/allsmiles/spriteparticles.qml b/examples/declarative/particles/allsmiles/spriteparticles.qml
index 705016e05019c8093a68304aa40a23623aa699cc..0586dfd8db3b356c558bf4e8c3d1ff351a3a98ac 100644
--- a/examples/declarative/particles/allsmiles/spriteparticles.qml
+++ b/examples/declarative/particles/allsmiles/spriteparticles.qml
@@ -47,7 +47,7 @@ Rectangle{
     height: 400
     ImageParticle{
         id: test
-        particles: ["Test"]
+        groups: ["Test"]
         source: "content/particle.png"
         system: sys
         z: 2
@@ -57,7 +57,7 @@ Rectangle{
     }
     ImageParticle{
         id: single
-        particles: ["Face"]
+        groups: ["Face"]
         system: sys
         z: 2
         anchors.fill: parent
@@ -73,7 +73,7 @@ Rectangle{
     }
     Emitter{
         system: sys
-        particle: "Test"
+        group: "Test"
         anchors.fill: parent
         id: particles2
         emitRate: 6000
@@ -83,7 +83,7 @@ Rectangle{
     }
     Emitter{
         system: sys
-        particle: "Face"
+        group: "Face"
         anchors.fill: parent
         id: particles
         emitRate: 60
diff --git a/examples/declarative/particles/asteroid/asteroid.qml b/examples/declarative/particles/asteroid/asteroid.qml
index ea2fabd51d5b8426de3e0d13bd62abea0d82c0dc..6d556997c703c22871d90d728f98c13b4e3844d5 100644
--- a/examples/declarative/particles/asteroid/asteroid.qml
+++ b/examples/declarative/particles/asteroid/asteroid.qml
@@ -67,7 +67,7 @@ Item {
     }
     ImageParticle {
         system: sys
-        particles: ["starfield"]
+        groups: ["starfield"]
         source: "content/star.png"
         colorVariation: 0.3
         color: "white"
@@ -75,7 +75,7 @@ Item {
     Emitter {
         id: starField
         system: sys
-        particle: "starfield"
+        group: "starfield"
 
         emitRate: 80
         lifeSpan: 2500
@@ -91,7 +91,7 @@ Item {
     }
     Emitter{
         system: sys
-        particle: "meteor"
+        group: "meteor"
         emitRate: 12
         lifeSpan: 5000
         acceleration: PointDirection{ xVariation: 80; yVariation: 80; }
@@ -101,7 +101,7 @@ Item {
      }
     ImageParticle{
         system: sys
-        particles: ["meteor"]
+        groups: ["meteor"]
         sprites:[Sprite{
                 id: spinState
                 name: "spinning"
@@ -126,7 +126,7 @@ Item {
         ]
     }
     SpriteGoal{
-        particles: ["meteor"]
+        groups: ["meteor"]
         system: sys
         goalState: "explode"
         jump: true
@@ -170,7 +170,7 @@ Item {
     ImageParticle{
         z:0 
         system: sys
-        particles: ["exhaust"]
+        groups: ["exhaust"]
         source: "content/particle4.png"
 
         color: "orange"
@@ -193,7 +193,7 @@ Item {
     Emitter{
         id: trailsNormal2
         system: sys
-        particle: "exhaust"
+        group: "exhaust"
 
         emitRate: 300
         lifeSpan: 500
diff --git a/examples/declarative/particles/asteroid/blackhole.qml b/examples/declarative/particles/asteroid/blackhole.qml
index 7e8a7a9edcd12b996deb7172a5355488be8fbdb9..00fca7ef3c35fd5580f5e83c7ac079216752af5b 100644
--- a/examples/declarative/particles/asteroid/blackhole.qml
+++ b/examples/declarative/particles/asteroid/blackhole.qml
@@ -66,7 +66,7 @@ Rectangle{
     }
 
     Emitter{
-        particle: "stars"
+        group: "stars"
         system: particles
         emitRate: 40
         lifeSpan: 4000
@@ -77,7 +77,7 @@ Rectangle{
         height: parent.height
     }
     Emitter{
-        particle: "roids"
+        group: "roids"
         system: particles
         emitRate: 10
         lifeSpan: 4000
@@ -93,7 +93,7 @@ Rectangle{
     }
     ImageParticle{
         id: stars
-        particles: ["stars"]
+        groups: ["stars"]
         system: particles
         source: "content/star.png"
         color: "white"
@@ -102,7 +102,7 @@ Rectangle{
     }
     ImageParticle{
         id: roids
-        particles: ["roids"]
+        groups: ["roids"]
         system: particles
         sprites: Sprite{
             id: spinState
@@ -115,7 +115,7 @@ Rectangle{
     }
     ImageParticle{
         id: shot
-        particles: ["shot"]
+        groups: ["shot"]
         system: particles
         source: "content/star.png"
 
@@ -124,7 +124,7 @@ Rectangle{
     }
     ImageParticle{
         id: engine
-        particles: ["engine"]
+        groups: ["engine"]
         system: particles
         source: "content/particle4.png"
 
@@ -170,7 +170,7 @@ Rectangle{
             drag.target: ship
         }
         Emitter{
-            particle: "engine"
+            group: "engine"
             system: particles
             emitRate: 200
             lifeSpan: 1000
@@ -182,7 +182,7 @@ Rectangle{
             width: 20
         }
         Emitter{
-            particle: "shot"
+            group: "shot"
             system: particles
             emitRate: 32
             lifeSpan: 2000
diff --git a/examples/declarative/particles/trails/combustion.qml b/examples/declarative/particles/trails/combustion.qml
index e4a21e9beb479275e71a9ebf29566f3a31a5a554..238dbe8a79eb74fffa319352d069be559b830782 100644
--- a/examples/declarative/particles/trails/combustion.qml
+++ b/examples/declarative/particles/trails/combustion.qml
@@ -57,71 +57,67 @@ Rectangle {
     ParticleSystem{
         id: particles
         anchors.fill: parent
+        ParticleGroup{
+            name: "unlit"
+            duration: 1000
+            to: {"lighting":1, "unlit":99}
+            ImageParticle{
+                source: "content/particleA.png"
+                colorVariation: 0.1
+                color: "#2060160f"
+            }
+            SpriteGoal{
+                whenCollidingWith: ["lit"]
+                goalState: "lighting"
+                jump: true
+                systemStates: true
+            }
+        }
+        ParticleGroup{
+            name: "lighting"
+            duration: 100
+            to: {"lit":1}
+        }
+        ParticleGroup{
+            name: "lit"
+            duration: 10000
+            onEntered: score++;
+            TrailEmitter{
+                id: fireballFlame
+                group: "flame"
 
+                emitRatePerParticle: 48
+                lifeSpan: 200
+                emitWidth: 8
+                emitHeight: 8
 
-        particleStates:[
-            Sprite{
-                name: "unlit"
-                duration: 1000
-                to: {"lighting":1, "unlit":99}
-                ImageParticle{
-                    source: "content/particleA.png"
-                    colorVariation: 0.1
-                    color: "#2060160f"
-                }
-                SpriteGoal{
-                    whenCollidingWith: ["lit"]
-                    goalState: "lighting"
-                    jump: true
-                    systemStates: true
-                }
-            },
-            Sprite{
-                name: "lighting"
-                duration: 100
-                to: {"lit":1}
-            },
-            Sprite{
-                name: "lit"
-                duration: 10000
-                onEntered: score++;
-                TrailEmitter{
-                    id: fireballFlame
-                    particle: "flame"
-
-                    emitRatePerParticle: 48
-                    lifeSpan: 200
-                    emitWidth: 8
-                    emitHeight: 8
-
-                    size: 24
-                    sizeVariation: 8
-                    endSize: 4
-                }
+                size: 24
+                sizeVariation: 8
+                endSize: 4
+            }
 
-                TrailEmitter{
-                    id: fireballSmoke
-                    particle: "smoke"
+            TrailEmitter{
+                id: fireballSmoke
+                group: "smoke"
 
-                    emitRatePerParticle: 120
-                    lifeSpan: 2000
-                    emitWidth: 16
-                    emitHeight: 16
+                emitRatePerParticle: 120
+                lifeSpan: 2000
+                emitWidth: 16
+                emitHeight: 16
 
-                    speed: PointDirection{yVariation: 16; xVariation: 16}
-                    acceleration: PointDirection{y: -16}
+                speed: PointDirection{yVariation: 16; xVariation: 16}
+                acceleration: PointDirection{y: -16}
 
-                    size: 24
-                    sizeVariation: 8
-                    endSize: 8
-                }
+                size: 24
+                sizeVariation: 8
+                endSize: 8
             }
-        ]
+        }
 
         ImageParticle{
             id: smoke
             anchors.fill: parent
-            particles: ["smoke"]
+            groups: ["smoke"]
             source: "content/particle.png"
             colorVariation: 0
             color: "#00111111"
@@ -129,7 +125,7 @@ Rectangle {
         ImageParticle{
             id: pilot
             anchors.fill: parent
-            particles: ["pilot"]
+            groups: ["pilot"]
             source: "content/particle.png"
             redVariation: 0.01
             blueVariation: 0.4
@@ -138,7 +134,7 @@ Rectangle {
         ImageParticle{
             id: flame
             anchors.fill: parent
-            particles: ["flame", "lit", "lighting"]
+            groups: ["flame", "lit", "lighting"]
             source: "content/particleA.png"
             colorVariation: 0.1
             color: "#00ff400f"
@@ -152,14 +148,14 @@ Rectangle {
             sizeVariation: 4
             speed: PointDirection{x:120; xVariation: 80; yVariation: 50}
             acceleration: PointDirection{y:120}
-            particle: "unlit"
+            group: "unlit"
         }
 
         Emitter{
             id: flamer
             x: 100
             y: 300
-            particle: "pilot"
+            group: "pilot"
             emitRate: 80
             lifeSpan: 600
             size: 24
@@ -167,7 +163,7 @@ Rectangle {
             endSize: 0
             speed: PointDirection{ y:-100; yVariation: 4; xVariation: 4 }
             SpriteGoal{
-                particles: ["unlit"]
+                groups: ["unlit"]
                 goalState: "lit"
                 jump: true
                 systemStates: true
@@ -181,7 +177,7 @@ Rectangle {
         }
         //Click to enflame
         SpriteGoal{//TODO: Aux emiiters in the state definition (which allows the occasional ball to spontaneously combust)
-            particles: ["unlit"]
+            groups: ["unlit"]
             goalState: "lighting"
             jump: true
             systemStates: true
diff --git a/examples/declarative/particles/trails/fireballs.qml b/examples/declarative/particles/trails/fireballs.qml
index 97a0c0ac5f4a3f86c83359642729df616608e2fa..c7c042004927bf7286439e4e8ed51abddda23225 100644
--- a/examples/declarative/particles/trails/fireballs.qml
+++ b/examples/declarative/particles/trails/fireballs.qml
@@ -55,7 +55,7 @@ Rectangle {
     ImageParticle{
         id: fireball
         anchors.fill: parent
-        particles: ["E"]
+        groups: ["E"]
         system: particles
         source: "content/particleA.png"
         colorVariation: 0.2
@@ -66,7 +66,7 @@ Rectangle {
         id: smoke
         system: particles
         anchors.fill: parent
-        particles: ["A", "B"]
+        groups: ["A", "B"]
         source: "content/particle.png"
         colorVariation: 0
         color: "#00111111"
@@ -75,7 +75,7 @@ Rectangle {
         id: flame
         anchors.fill: parent
         system: particles
-        particles: ["C", "D"]
+        groups: ["C", "D"]
         source: "content/particle.png"
         colorVariation: 0.1
         color: "#00ff400f"
@@ -83,7 +83,7 @@ Rectangle {
     Emitter{
         id: fire
         system: particles
-        particle: "C"
+        group: "C"
 
         y: parent.height
         width: parent.width
@@ -100,7 +100,7 @@ Rectangle {
     }
     TrailEmitter{
         id: fireSmoke
-        particle: "B"
+        group: "B"
         system: particles
         follow: "C"
         width: root.width
@@ -120,7 +120,7 @@ Rectangle {
         id: fireballFlame
         anchors.fill: parent
         system: particles
-        particle: "D"
+        group: "D"
         follow: "E"
 
         emitRatePerParticle: 120
@@ -137,7 +137,7 @@ Rectangle {
         id: fireballSmoke
         anchors.fill: parent
         system: particles
-        particle: "A"
+        group: "A"
         follow: "E"
 
         emitRatePerParticle: 128
@@ -155,7 +155,7 @@ Rectangle {
     Emitter{
         id: balls
         system: particles
-        particle: "E"
+        group: "E"
 
         y: parent.height
         width: parent.width
diff --git a/examples/declarative/particles/trails/fireworks.qml b/examples/declarative/particles/trails/fireworks.qml
index 437d9ee3d6bc018326bb9ec526d53ff3758cc203..6b370b3991391216a1e6dec250688a44299891e5 100644
--- a/examples/declarative/particles/trails/fireworks.qml
+++ b/examples/declarative/particles/trails/fireworks.qml
@@ -48,36 +48,34 @@ Rectangle{
     ParticleSystem{
         anchors.fill: parent
         id: syssy
-        particleStates:[
-            Sprite{
-                name: "fire"
-                duration: 2000
-                durationVariation: 2000
-                to: {"splode":1}
-            },
-            Sprite{
-                name: "splode"
-                duration: 400
-                to: {"dead":1}
-                TrailEmitter{
-                    particle: "works"
-                    emitRatePerParticle: 100
-                    lifeSpan: 1000
-                    maximumEmitted: 1200
-                    size: 8
-                    speed: AngleDirection{angle: 270; angleVariation: 45; magnitude: 20; magnitudeVariation: 20;}
-                    acceleration: PointDirection{y:100; yVariation: 20}
-                }
-            },
-            Sprite{
-                name: "dead"
-                duration: 1000
-                Affector{
-                    once: true
-                    onAffected: worksEmitter.burst(400,x,y)
-                }
+        ParticleGroup{
+            name: "fire"
+            duration: 2000
+            durationVariation: 2000
+            to: {"splode":1}
+        }
+        ParticleGroup{
+            name: "splode"
+            duration: 400
+            to: {"dead":1}
+            TrailEmitter{
+                group: "works"
+                emitRatePerParticle: 100
+                lifeSpan: 1000
+                maximumEmitted: 1200
+                size: 8
+                speed: AngleDirection{angle: 270; angleVariation: 45; magnitude: 20; magnitudeVariation: 20;}
+                acceleration: PointDirection{y:100; yVariation: 20}
+            }
+        }
+        ParticleGroup{
+            name: "dead"
+            duration: 1000
+            Affector{
+                once: true
+                onAffected: worksEmitter.burst(400,x,y)
             }
-        ]
+        }
         Timer{
             interval: 6000
             running: true
@@ -87,7 +85,7 @@ Rectangle{
         }
         Emitter{
             id: startingEmitter
-            particle: "fire"
+            group: "fire"
             width: parent.width
             y: parent.height
             enabled: false
@@ -98,7 +96,7 @@ Rectangle{
         }
         Emitter{
             id: worksEmitter
-            particle: "works"
+            group: "works"
             enabled: false
             emitRate: 100
             lifeSpan: 1600
@@ -111,7 +109,7 @@ Rectangle{
             acceleration: PointDirection{y:100; yVariation: 20}
         }
         ImageParticle{
-            particles: ["works", "fire", "splode"]
+            groups: ["works", "fire", "splode"]
             source: "content/particle.png"
             entryEffect: ImageParticle.Scale
         }
diff --git a/examples/declarative/particles/trails/portal.qml b/examples/declarative/particles/trails/portal.qml
index 85efd9a5941e540288a792e839b8052bd93cdfc9..adf620fd9b58aba44b016c0ae381b4ee8598c109 100644
--- a/examples/declarative/particles/trails/portal.qml
+++ b/examples/declarative/particles/trails/portal.qml
@@ -54,7 +54,7 @@ Rectangle{
         id: particles 
     }
     ImageParticle{
-        particles: ["center","edge"]
+        groups: ["center","edge"]
         anchors.fill: parent
         system: particles
         source: "content/particle.png"
@@ -63,7 +63,7 @@ Rectangle{
     }
     Emitter{
         anchors.fill: parent
-        particle: "center"
+        group: "center"
         system: particles
         emitRate: 200
         lifeSpan: 2000
@@ -80,7 +80,7 @@ Rectangle{
     }
     Emitter{
         anchors.fill: parent
-        particle: "edge"
+        group: "edge"
         startTime: 2000
         system: particles
         emitRate: 4000
diff --git a/examples/declarative/particles/trails/turbulence.qml b/examples/declarative/particles/trails/turbulence.qml
index 104bb10e801fe98f51657e8feef71b40d55c6aeb..13eae162f4c2d36873140e2773f8739547ddaa01 100644
--- a/examples/declarative/particles/trails/turbulence.qml
+++ b/examples/declarative/particles/trails/turbulence.qml
@@ -71,14 +71,14 @@ Rectangle{
         strength: 32
     }
     ImageParticle{
-        particles: ["smoke"]
+        groups: ["smoke"]
         system: ps
         source: "content/particle.png"
         color: "#11111111"
         colorVariation: 0
     }
     ImageParticle{
-        particles: ["flame"]
+        groups: ["flame"]
         system: ps
         source: "content/particle.png"
         color: "#11ff400f"
@@ -87,7 +87,7 @@ Rectangle{
     Emitter{
         anchors.centerIn: parent
         system: ps
-        particle: "flame"
+        group: "flame"
         
         emitRate: 120
         lifeSpan: 1200
@@ -102,7 +102,7 @@ Rectangle{
         width: root.width
         height: root.height/2 - 20
         system: ps
-        particle: "smoke"
+        group: "smoke"
         follow: "flame"
 
         emitRatePerParticle: 1
@@ -119,7 +119,7 @@ Rectangle{
         width: root.width
         height: root.height/2 - 40
         system: ps
-        particle: "smoke"
+        group: "smoke"
         follow: "flame"
         
         emitRatePerParticle: 4
diff --git a/examples/declarative/plasmapatrol/content/BlasterHardpoint.qml b/examples/declarative/plasmapatrol/content/BlasterHardpoint.qml
index 384275fc946459cf14e3842a193edec0e676c80b..3e751f4e15b6a8a00f2f6be3012c345162173f5e 100644
--- a/examples/declarative/plasmapatrol/content/BlasterHardpoint.qml
+++ b/examples/declarative/plasmapatrol/content/BlasterHardpoint.qml
@@ -55,7 +55,7 @@ Item {
     height: 24
     Emitter{
         id: visualization
-        particle: "blaster"
+        group: "blaster"
         system: container.system
         enabled: show
         anchors.fill: parent
@@ -114,7 +114,7 @@ Item {
     }
     Emitter{
         id: emitter
-        particle: "blaster"
+        group: "blaster"
         enabled: false
         system: container.system
         anchors.centerIn: parent
diff --git a/examples/declarative/plasmapatrol/content/CannonHardpoint.qml b/examples/declarative/plasmapatrol/content/CannonHardpoint.qml
index b2c7aca1fd18201987407657020ec91d0853c86a..dc15f0cae1946b2741926493b22317ae3abb6b56 100644
--- a/examples/declarative/plasmapatrol/content/CannonHardpoint.qml
+++ b/examples/declarative/plasmapatrol/content/CannonHardpoint.qml
@@ -51,7 +51,7 @@ Item {
     height: 24
     Emitter{
         id: visualization
-        particle: "cannon"
+        group: "cannon"
         enabled: container.show
         system: container.system
         anchors.centerIn: parent
@@ -80,7 +80,7 @@ Item {
     }
     Emitter{
         id: emitter
-        particle: "cannon"
+        group: "cannon"
         enabled: false
         system: container.system
         anchors.centerIn: parent
diff --git a/examples/declarative/plasmapatrol/content/Cruiser.qml b/examples/declarative/plasmapatrol/content/Cruiser.qml
index b0d20023db934f1891581422a5a453cb03a807cf..a4983fc2a0184ea03c8cc31e33fbc475dfc77da5 100644
--- a/examples/declarative/plasmapatrol/content/Cruiser.qml
+++ b/examples/declarative/plasmapatrol/content/Cruiser.qml
@@ -58,7 +58,7 @@ Item {
         //TODO: Cooler would be an 'orbiting' affector
         //TODO: On the subject, opacity and size should be grouped type 'overLife' if we can cram that in the particles
         system: container.system
-        particle: container.shipParticle
+        group: container.shipParticle
         anchors.centerIn: parent
         width: 64
         height: 64
@@ -76,7 +76,7 @@ Item {
     }
     Emitter{
         system: container.system
-        particle: "cruiserArmor"
+        group: "cruiserArmor"
         anchors.fill: parent
         shape: EllipseShape{ fill: false }
         enabled: hp>0
@@ -92,7 +92,7 @@ Item {
             system: container.system
             enabled: container.hp <=0
             anchors.fill: parent
-            particles: ["cruiserArmor"]
+            groups: ["cruiserArmor"]
             goalState: "death"
 //            jump: true
             once: true
diff --git a/examples/declarative/plasmapatrol/content/Frigate.qml b/examples/declarative/plasmapatrol/content/Frigate.qml
index 8d493b81d5d8f66078eda21fb7e52b78938ea7c4..f26e7e881c34242180c00662a1e193a5c49c8772 100644
--- a/examples/declarative/plasmapatrol/content/Frigate.qml
+++ b/examples/declarative/plasmapatrol/content/Frigate.qml
@@ -56,7 +56,7 @@ Item {
     height: 128
     Emitter{
         system: container.system
-        particle: "frigateShield"
+        group: "frigateShield"
         anchors.centerIn: parent
         size: 92
         emitRate: 1
@@ -65,7 +65,7 @@ Item {
     }
     Emitter{
         system: container.system
-        particle: container.shipParticle
+        group: container.shipParticle
         anchors.centerIn: parent
         width: 64 
         height: 16
diff --git a/examples/declarative/plasmapatrol/content/LaserHardpoint.qml b/examples/declarative/plasmapatrol/content/LaserHardpoint.qml
index 45712bf68a729f4f9092aed0d4fee764e6b28c33..56fd91b5b3ec94a72009790baf64cdad4df3631a 100644
--- a/examples/declarative/plasmapatrol/content/LaserHardpoint.qml
+++ b/examples/declarative/plasmapatrol/content/LaserHardpoint.qml
@@ -51,7 +51,7 @@ Item {
     height: 24
     Emitter{
         id: visualization
-        particle: "laser"
+        group: "laser"
         system: container.system
         anchors.fill: parent
         enabled: container.show
@@ -86,7 +86,7 @@ Item {
     }
     Emitter{
         id: emitter
-        particle: "laser"
+        group: "laser"
         enabled: false
         system: container.system
         x: Math.min(container.width/2, target.x);
diff --git a/examples/declarative/plasmapatrol/content/PlasmaPatrolParticles.qml b/examples/declarative/plasmapatrol/content/PlasmaPatrolParticles.qml
index 792ba7ab72f7537ea8b44ec20442378e48415359..7a8c3e61b31d519e10d735fd896636eb6ad246a6 100644
--- a/examples/declarative/plasmapatrol/content/PlasmaPatrolParticles.qml
+++ b/examples/declarative/plasmapatrol/content/PlasmaPatrolParticles.qml
@@ -45,7 +45,7 @@ Item{
     property ParticleSystem sys
     ImageParticle{
         system: sys
-        particles: ["default"]
+        groups: ["default"]
         source: "pics/blur-circle3.png"
         color: "#003A3A3A"
         colorVariation: 0.1
@@ -53,7 +53,7 @@ Item{
     }
     ImageParticle{
         system: sys
-        particles: ["redTeam"]
+        groups: ["redTeam"]
         source: "pics/blur-circle3.png"
         color: "#0028060A"
         colorVariation: 0.1
@@ -61,7 +61,7 @@ Item{
     }
     ImageParticle{
         system: sys
-        particles: ["greenTeam"]
+        groups: ["greenTeam"]
         source: "pics/blur-circle3.png"
         color: "#0006280A"
         colorVariation: 0.1
@@ -69,7 +69,7 @@ Item{
     }
     ImageParticle{
         system: sys
-        particles: ["blaster"]
+        groups: ["blaster"]
         source: "pics/star2.png"
         //color: "#0F282406"
         color: "#0F484416"
@@ -78,7 +78,7 @@ Item{
     }
     ImageParticle{
         system: sys
-        particles: ["laser"]
+        groups: ["laser"]
         source: "pics/star3.png"
         //color: "#00123F68"
         color: "#00428FF8"
@@ -87,7 +87,7 @@ Item{
     }
     ImageParticle{
         system: sys
-        particles: ["cannon"]
+        groups: ["cannon"]
         source: "pics/particle.png"
         color: "#80FFAAFF"
         colorVariation: 0.1
@@ -95,7 +95,7 @@ Item{
     }
     ImageParticle{
         system: sys
-        particles: ["cannonCore"]
+        groups: ["cannonCore"]
         source: "pics/particle.png"
         color: "#00666666"
         colorVariation: 0.8
@@ -103,7 +103,7 @@ Item{
     }
     ImageParticle{
         system: sys
-        particles: ["cannonWake"]
+        groups: ["cannonWake"]
         source: "pics/star.png"
         color: "#00CCCCCC"
         colorVariation: 0.2
@@ -111,7 +111,7 @@ Item{
     }
     ImageParticle{
         system: sys
-        particles: ["frigateShield"]
+        groups: ["frigateShield"]
         source: "pics/blur-circle2.png"
         color: "#00000000"
         colorVariation: 0.05
@@ -121,7 +121,7 @@ Item{
     }
     ImageParticle{
         system: sys
-        particles: ["cruiserArmor"]
+        groups: ["cruiserArmor"]
         z: 1
         sprites:[Sprite{
                 id: spinState
@@ -146,7 +146,7 @@ Item{
     }
     TrailEmitter{
         system: sys
-        particle: "cannonWake"
+        group: "cannonWake"
         follow: "cannon"
         emitRatePerParticle: 64
         lifeSpan: 600
@@ -160,7 +160,7 @@ Item{
     }
     TrailEmitter{
         system: sys
-        particle: "cannonCore"
+        group: "cannonCore"
         follow: "cannon"
         emitRatePerParticle: 256
         lifeSpan: 128
diff --git a/examples/declarative/plasmapatrol/content/Sloop.qml b/examples/declarative/plasmapatrol/content/Sloop.qml
index 82e57f5161a69badd2f1181294a4b48ca7dad897..59678a75ea38cad42e665ec3efbf0556f37265e7 100644
--- a/examples/declarative/plasmapatrol/content/Sloop.qml
+++ b/examples/declarative/plasmapatrol/content/Sloop.qml
@@ -60,7 +60,7 @@ Item {
         //TODO: Cooler would be an 'orbiting' affector
         //TODO: On the subject, opacity and size should be grouped type 'overLife' if we can cram that in the particles
         system: container.system
-        particle: container.shipParticle
+        group: container.shipParticle
         shape: EllipseShape{}
 
         emitRate: hp > 0 ?  hp + 20 : 0 
diff --git a/examples/declarative/plasmapatrol/plasmapatrol.qml b/examples/declarative/plasmapatrol/plasmapatrol.qml
index 4ea464b9f8a51a7418888ae6dcc9bd8271a5789a..2fa9f449295b527c6639165bf2736d9cd23f10e1 100644
--- a/examples/declarative/plasmapatrol/plasmapatrol.qml
+++ b/examples/declarative/plasmapatrol/plasmapatrol.qml
@@ -97,7 +97,7 @@ Rectangle {
                         anchors.fill: parent
                         system: particles
                         enabled: true
-                        particle: "default"
+                        group: "default"
                         emitRate: 1200
                         lifeSpan: 1200
                         shape: MaskShape{source:"content/pics/TitleText.png"}
diff --git a/examples/declarative/samegame/SamegameCore/BoomBlock.qml b/examples/declarative/samegame/SamegameCore/BoomBlock.qml
index 1c84fa861475fd11d1cfe17e43329ac85e06dd4e..df3e9bd8faaa2313843828c5fa3c28e401bcc70a 100644
--- a/examples/declarative/samegame/SamegameCore/BoomBlock.qml
+++ b/examples/declarative/samegame/SamegameCore/BoomBlock.qml
@@ -75,7 +75,7 @@ Item {
     Emitter {
         id: particles
         system: particleSystem
-        particle: { 
+        group: { 
             if(type == 0){
                 "red";
             } else if (type == 1) {
diff --git a/examples/declarative/samegame/SamegameCore/GameArea.qml b/examples/declarative/samegame/SamegameCore/GameArea.qml
index 967e2995778a40767973ea4511401740f7cfd87d..9a8f68ad893dea02d63aad897452e3a6b0956c9f 100644
--- a/examples/declarative/samegame/SamegameCore/GameArea.qml
+++ b/examples/declarative/samegame/SamegameCore/GameArea.qml
@@ -65,21 +65,21 @@ Item {
         id: particleSystem;
         z:2
         ImageParticle {
-            particles: ["red"]
+            groups: ["red"]
             color: Qt.darker("red");//Actually want desaturated...
             source: "pics/particle.png"
             colorVariation: 0.4
             alpha: 0.1
         }
         ImageParticle {
-            particles: ["green"]
+            groups: ["green"]
             color: Qt.darker("green");//Actually want desaturated...
             source: "pics/particle.png"
             colorVariation: 0.4
             alpha: 0.1
         }
         ImageParticle {
-            particles: ["blue"]
+            groups: ["blue"]
             color: Qt.darker("blue");//Actually want desaturated...
             source: "pics/particle.png"
             colorVariation: 0.4
diff --git a/examples/declarative/snake/content/Cookie.qml b/examples/declarative/snake/content/Cookie.qml
index e3b3bbf3f5df4301cd086400ac919bf8d3f34276..d9fedd9436b409727956fc93f968ff45fc33875c 100644
--- a/examples/declarative/snake/content/Cookie.qml
+++ b/examples/declarative/snake/content/Cookie.qml
@@ -71,13 +71,13 @@ Item  {
     ParticleSystem {
         width:1; height:1; anchors.centerIn: parent;
         ImageParticle {
-            particles: ["star"]
+            groups: ["star"]
             source: "pics/yellowStar.png"
         }
         Emitter {
             id: particles
             anchors.fill: parent
-            particle: "star"
+            group: "star"
             emitRate: 50
             emitting: false
             lifeSpan: 700
diff --git a/examples/declarative/snake/content/Link.qml b/examples/declarative/snake/content/Link.qml
index 82e0359a1abe21e2303ebbcbfd7ca84afb99ec5f..31ad62248c7a6b66e5d767812d13766ec4c05945 100644
--- a/examples/declarative/snake/content/Link.qml
+++ b/examples/declarative/snake/content/Link.qml
@@ -96,13 +96,13 @@ Item { id:link
     ParticleSystem {
         width:1; height:1; anchors.centerIn: parent;
         ImageParticle {
-            particles: ["star"]
+            groups: ["star"]
             source: type == 1 ? "pics/blueStar.png" : "pics/redStar.png"
         }
         Emitter {
             id: particles
             anchors.fill: parent
-            particle: "star"
+            group: "star"
             emitRate: 50
             emitting: false
             lifeSpan: 700
diff --git a/examples/declarative/toys/dynamicscene/dynamicscene.qml b/examples/declarative/toys/dynamicscene/dynamicscene.qml
index 5670aacfa77ae868d71f394a18c9655352275633..179d633c97cc3f31b7dcc464b1dfe9489d41e822 100644
--- a/examples/declarative/toys/dynamicscene/dynamicscene.qml
+++ b/examples/declarative/toys/dynamicscene/dynamicscene.qml
@@ -104,7 +104,7 @@ Item {
         ImageParticle {
             id: stars
             source: "content/images/star.png"
-            particles: ["stars"]
+            groups: ["stars"]
             opacity: .5
         }
 
@@ -113,7 +113,7 @@ Item {
             anchors.fill: parent
             emitRate: parent.width / 50
             lifeSpan: 5000
-            particle: "stars"
+            group: "stars"
         }
     }
 
diff --git a/src/declarative/items/qsgsprite.cpp b/src/declarative/items/qsgsprite.cpp
index 806f7a9ad8c11f2c5772afc5e75b4f98ca1355ad..63d1951b7cd1c22d2477374b6978d84940709160 100644
--- a/src/declarative/items/qsgsprite.cpp
+++ b/src/declarative/items/qsgsprite.cpp
@@ -40,42 +40,17 @@
 ****************************************************************************/
 
 #include "qsgsprite_p.h"
-//TODO: Split out particle system dependency
-#include "qsgparticlesystem_p.h"
 #include <QDebug>
 
 QT_BEGIN_NAMESPACE
 
 QSGSprite::QSGSprite(QObject *parent) :
-    QObject(parent)
+    QSGStochasticState(parent)
     , m_generatedCount(0)
     , m_framesPerRow(0)
-    , m_frames(1)
     , m_frameHeight(0)
     , m_frameWidth(0)
-    , m_duration(1000)
 {
 }
 
-void redirectError(QDeclarativeListProperty<QObject> *prop, QObject *value)
-{
-    qWarning() << "Could not add " << value << " to state" << prop->object << "as it is not associated with a particle system.";
-}
-
-QDeclarativeListProperty<QObject> QSGSprite::particleChildren()
-{
-    QSGParticleSystem* system = qobject_cast<QSGParticleSystem*>(parent());
-    if (system)
-        return QDeclarativeListProperty<QObject>(this, 0, &QSGParticleSystem::stateRedirect);
-    else
-        return QDeclarativeListProperty<QObject>(this, 0, &redirectError);
-}
-
-int QSGSprite::variedDuration() const
-{
-    return m_duration
-            + (m_durationVariance * ((qreal)qrand()/RAND_MAX) * 2)
-            - m_durationVariance;
-}
-
 QT_END_NAMESPACE
diff --git a/src/declarative/items/qsgsprite_p.h b/src/declarative/items/qsgsprite_p.h
index c18e9b45054daa714efd2cbe236ad0a3497c718e..ed7c6c4be457689261ced96326994e9252df8da8 100644
--- a/src/declarative/items/qsgsprite_p.h
+++ b/src/declarative/items/qsgsprite_p.h
@@ -46,6 +46,7 @@
 #include <QUrl>
 #include <QVariantMap>
 #include <QDeclarativeListProperty>
+#include "qsgspriteengine_p.h"
 
 QT_BEGIN_HEADER
 
@@ -54,38 +55,23 @@ QT_BEGIN_NAMESPACE
 QT_MODULE(Declarative)
 
 
-class QSGSprite : public QObject
+class QSGSprite : public QSGStochasticState
 {
     Q_OBJECT
-    Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged)
     Q_PROPERTY(QUrl source READ source WRITE setSource NOTIFY sourceChanged)
-    Q_PROPERTY(int frames READ frames WRITE setFrames NOTIFY framesChanged)
-    //If frame height or width is not specified, it is assumed to be a single long row of frames.
+    //If frame height or width is not specified, it is assumed to be a single long row of square frames.
     //Otherwise, it can be multiple contiguous rows, when one row runs out the next will be used.
     Q_PROPERTY(int frameHeight READ frameHeight WRITE setFrameHeight NOTIFY frameHeightChanged)
     Q_PROPERTY(int frameWidth READ frameWidth WRITE setFrameWidth NOTIFY frameWidthChanged)
-    Q_PROPERTY(int duration READ duration WRITE setDuration NOTIFY durationChanged)
-    Q_PROPERTY(int durationVariation READ durationVariance WRITE setDurationVariance NOTIFY durationVarianceChanged)
-    Q_PROPERTY(qreal speedModifiesDuration READ speedModifer WRITE setSpeedModifier NOTIFY speedModifierChanged)
-    Q_PROPERTY(QVariantMap to READ to WRITE setTo NOTIFY toChanged)
 
-    Q_PROPERTY(QDeclarativeListProperty<QObject> particleChildren READ particleChildren DESIGNABLE false)//### Hidden property for in-state system definitions - ought not to be used in actual "Sprite" states
-    Q_CLASSINFO("DefaultProperty", "particleChildren")
 public:
     explicit QSGSprite(QObject *parent = 0);
 
-    QDeclarativeListProperty<QObject> particleChildren();
-
     QUrl source() const
     {
         return m_source;
     }
 
-    int frames() const
-    {
-        return m_frames;
-    }
-
     int frameHeight() const
     {
         return m_frameHeight;
@@ -96,55 +82,15 @@ public:
         return m_frameWidth;
     }
 
-    int duration() const
-    {
-        return m_duration;
-    }
-
-    QString name() const
-    {
-        return m_name;
-    }
-
-    QVariantMap to() const
-    {
-        return m_to;
-    }
-
-    qreal speedModifer() const
-    {
-        return m_speedModifier;
-    }
-
-    int durationVariance() const
-    {
-        return m_durationVariance;
-    }
-
-    int variedDuration() const;
 
 signals:
 
     void sourceChanged(QUrl arg);
 
-    void framesChanged(int arg);
-
     void frameHeightChanged(int arg);
 
     void frameWidthChanged(int arg);
 
-    void durationChanged(int arg);
-
-    void nameChanged(QString arg);
-
-    void toChanged(QVariantMap arg);
-
-    void speedModifierChanged(qreal arg);
-
-    void durationVarianceChanged(int arg);
-
-    void entered();//### Just playing around - don't expect full state API
-
 public slots:
 
     void setSource(QUrl arg)
@@ -155,14 +101,6 @@ public slots:
         }
     }
 
-    void setFrames(int arg)
-    {
-        if (m_frames != arg) {
-            m_frames = arg;
-            emit framesChanged(arg);
-        }
-    }
-
     void setFrameHeight(int arg)
     {
         if (m_frameHeight != arg) {
@@ -179,60 +117,16 @@ public slots:
         }
     }
 
-    void setDuration(int arg)
-    {
-        if (m_duration != arg) {
-            m_duration = arg;
-            emit durationChanged(arg);
-        }
-    }
-
-    void setName(QString arg)
-    {
-        if (m_name != arg) {
-            m_name = arg;
-            emit nameChanged(arg);
-        }
-    }
-
-    void setTo(QVariantMap arg)
-    {
-        if (m_to != arg) {
-            m_to = arg;
-            emit toChanged(arg);
-        }
-    }
-
-    void setSpeedModifier(qreal arg)
-    {
-        if (m_speedModifier != arg) {
-            m_speedModifier = arg;
-            emit speedModifierChanged(arg);
-        }
-    }
-
-    void setDurationVariance(int arg)
-    {
-        if (m_durationVariance != arg) {
-            m_durationVariance = arg;
-            emit durationVarianceChanged(arg);
-        }
-    }
 
 private:
     friend class QSGImageParticle;
     friend class QSGSpriteEngine;
+    friend class QSGStochasticEngine;
     int m_generatedCount;
     int m_framesPerRow;
     QUrl m_source;
-    int m_frames;
     int m_frameHeight;
     int m_frameWidth;
-    int m_duration;
-    QString m_name;
-    QVariantMap m_to;
-    qreal m_speedModifier;
-    int m_durationVariance;
 
 };
 
diff --git a/src/declarative/items/qsgspriteengine.cpp b/src/declarative/items/qsgspriteengine.cpp
index 1915db6000b7d7c15420c38f23a852735d359672..0391ce1281a7d70ae3ba9e1b119ff5304d4e1807 100644
--- a/src/declarative/items/qsgspriteengine.cpp
+++ b/src/declarative/items/qsgspriteengine.cpp
@@ -53,7 +53,7 @@ QT_BEGIN_NAMESPACE
    Also solve the state data initialization/transfer issue so as to not need to make friends
 */
 
-QSGSpriteEngine::QSGSpriteEngine(QObject *parent) :
+QSGStochasticEngine::QSGStochasticEngine(QObject *parent) :
     QObject(parent), m_timeOffset(0)
 {
     //Default size 1
@@ -61,7 +61,7 @@ QSGSpriteEngine::QSGSpriteEngine(QObject *parent) :
     m_advanceTime.start();
 }
 
-QSGSpriteEngine::QSGSpriteEngine(QList<QSGSprite*> states, QObject *parent) :
+QSGStochasticEngine::QSGStochasticEngine(QList<QSGStochasticState*> states, QObject *parent) :
     QObject(parent), m_states(states), m_timeOffset(0)
 {
     //Default size 1
@@ -69,10 +69,27 @@ QSGSpriteEngine::QSGSpriteEngine(QList<QSGSprite*> states, QObject *parent) :
     m_advanceTime.start();
 }
 
+QSGStochasticEngine::~QSGStochasticEngine()
+{
+}
+
+QSGSpriteEngine::QSGSpriteEngine(QObject *parent)
+    : QSGStochasticEngine(parent)
+{
+}
+
+QSGSpriteEngine::QSGSpriteEngine(QList<QSGSprite*> sprites, QObject *parent)
+    : QSGStochasticEngine(parent)
+{
+    foreach (QSGSprite* sprite, sprites)
+        m_states << (QSGStochasticState*)sprite;
+}
+
 QSGSpriteEngine::~QSGSpriteEngine()
 {
 }
 
+
 int QSGSpriteEngine::maxFrames()
 {
     return m_maxFrames;
@@ -87,46 +104,46 @@ TODO: Above idea needs to have the varying duration offset added to it
 */
 int QSGSpriteEngine::spriteState(int sprite)
 {
-    int state = m_sprites[sprite];
-    if (!m_states[state]->m_generatedCount)
+    int state = m_things[sprite];
+    if (!m_sprites[state]->m_generatedCount)
         return state;
-    int rowDuration = m_duration[sprite] * m_states[state]->m_framesPerRow;
+    int rowDuration = m_duration[sprite] * m_sprites[state]->m_framesPerRow;
     int extra = (m_timeOffset - m_startTimes[sprite])/rowDuration;
     return state + extra;
 }
 
 int QSGSpriteEngine::spriteStart(int sprite)
 {
-    int state = m_sprites[sprite];
-    if (!m_states[state]->m_generatedCount)
+    int state = m_things[sprite];
+    if (!m_sprites[state]->m_generatedCount)
         return m_startTimes[sprite];
-    int rowDuration = m_duration[sprite] * m_states[state]->m_framesPerRow;
+    int rowDuration = m_duration[sprite] * m_sprites[state]->m_framesPerRow;
     int extra = (m_timeOffset - m_startTimes[sprite])/rowDuration;
     return state + extra*rowDuration;
 }
 
 int QSGSpriteEngine::spriteFrames(int sprite)
 {
-    int state = m_sprites[sprite];
-    if (!m_states[state]->m_generatedCount)
-        return m_states[state]->frames();
-    int rowDuration = m_duration[sprite] * m_states[state]->m_framesPerRow;
+    int state = m_things[sprite];
+    if (!m_sprites[state]->m_generatedCount)
+        return m_sprites[state]->frames();
+    int rowDuration = m_duration[sprite] * m_sprites[state]->m_framesPerRow;
     int extra = (m_timeOffset - m_startTimes[sprite])/rowDuration;
-    if (extra == m_states[state]->m_generatedCount - 1)//last state
-        return m_states[state]->frames() % m_states[state]->m_framesPerRow;
+    if (extra == m_sprites[state]->m_generatedCount - 1)//last state
+        return m_sprites[state]->frames() % m_sprites[state]->m_framesPerRow;
     else
-        return m_states[state]->m_framesPerRow;
+        return m_sprites[state]->m_framesPerRow;
 }
 
 int QSGSpriteEngine::spriteDuration(int sprite)
 {
-    int state = m_sprites[sprite];
-    if (!m_states[state]->m_generatedCount)
+    int state = m_things[sprite];
+    if (!m_sprites[state]->m_generatedCount)
         return m_duration[sprite];
-    int rowDuration = m_duration[sprite] * m_states[state]->m_framesPerRow;
+    int rowDuration = m_duration[sprite] * m_sprites[state]->m_framesPerRow;
     int extra = (m_timeOffset - m_startTimes[sprite])/rowDuration;
-    if (extra == m_states[state]->m_generatedCount - 1)//last state
-        return (m_duration[sprite] * m_states[state]->frames()) % rowDuration;
+    if (extra == m_sprites[state]->m_generatedCount - 1)//last state
+        return (m_duration[sprite] * m_sprites[state]->frames()) % rowDuration;
     else
         return rowDuration;
 }
@@ -136,21 +153,21 @@ int QSGSpriteEngine::spriteCount()//TODO: Actually image state count, need to re
     return m_imageStateCount;
 }
 
-void QSGSpriteEngine::setGoal(int state, int sprite, bool jump)
+void QSGStochasticEngine::setGoal(int state, int sprite, bool jump)
 {
-    if (sprite >= m_sprites.count() || state >= m_states.count())
+    if (sprite >= m_things.count() || state >= m_states.count())
         return;
     if (!jump){
         m_goals[sprite] = state;
         return;
     }
 
-    if (m_sprites[sprite] == state)
+    if (m_things[sprite] == state)
         return;//Already there
-    m_sprites[sprite] = state;
+    m_things[sprite] = state;
     m_duration[sprite] = m_states[state]->variedDuration();
     m_goals[sprite] = -1;
-    restartSprite(sprite);
+    restart(sprite);
     emit stateChanged(sprite);
     emit m_states[state]->entered();
     return;
@@ -165,8 +182,15 @@ QImage QSGSpriteEngine::assembledImage()
 
     int maxSize;
     glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxSize);
+    foreach (QSGStochasticState* s, m_states){
+        QSGSprite* sprite = qobject_cast<QSGSprite*>(s);
+        if (sprite)
+            m_sprites << sprite;
+        else
+            qDebug() << "Error: Non-sprite in QSGSpriteEngine";
+    }
 
-    foreach (QSGSprite* state, m_states){
+    foreach (QSGSprite* state, m_sprites){
         if (state->frames() > m_maxFrames)
             m_maxFrames = state->frames();
 
@@ -224,7 +248,7 @@ QImage QSGSpriteEngine::assembledImage()
     image.fill(0);
     QPainter p(&image);
     int y = 0;
-    foreach (QSGSprite* state, m_states){
+    foreach (QSGSprite* state, m_sprites){
         QImage img(state->source().toLocalFile());
         if (img.height() == frameHeight && img.width() <  maxSize){//Simple case
             p.drawImage(0,y,img);
@@ -271,51 +295,51 @@ QImage QSGSpriteEngine::assembledImage()
     return image;
 }
 
-void QSGSpriteEngine::setCount(int c)
+void QSGStochasticEngine::setCount(int c)
 {
-    m_sprites.resize(c);
+    m_things.resize(c);
     m_goals.resize(c);
     m_duration.resize(c);
     m_startTimes.resize(c);
 }
 
-void QSGSpriteEngine::startSprite(int index, int state)
+void QSGStochasticEngine::start(int index, int state)
 {
-    if (index >= m_sprites.count())
+    if (index >= m_things.count())
         return;
-    m_sprites[index] = state;
+    m_things[index] = state;
     m_duration[index] = m_states[state]->variedDuration();
     m_goals[index] = -1;
-    restartSprite(index);
+    restart(index);
 }
 
-void QSGSpriteEngine::stopSprite(int index)
+void QSGStochasticEngine::stop(int index)
 {
-    if (index >= m_sprites.count())
+    if (index >= m_things.count())
         return;
     //Will never change until start is called again with a new state - this is not a 'pause'
     for (int i=0; i<m_stateUpdates.count(); i++)
         m_stateUpdates[i].second.removeAll(index);
 }
 
-void QSGSpriteEngine::restartSprite(int index)
+void QSGStochasticEngine::restart(int index)
 {
     m_startTimes[index] = m_timeOffset + m_advanceTime.elapsed();
-    int time = m_duration[index] * m_states[m_sprites[index]]->frames() + m_startTimes[index];
+    int time = m_duration[index] * m_states[m_things[index]]->frames() + m_startTimes[index];
     for (int i=0; i<m_stateUpdates.count(); i++)
         m_stateUpdates[i].second.removeAll(index);
     addToUpdateList(time, index);
 }
 
-uint QSGSpriteEngine::updateSprites(uint time)//### would returning a list of changed idxs be faster than signals?
+uint QSGStochasticEngine::updateSprites(uint time)//### would returning a list of changed idxs be faster than signals?
 {
     //Sprite State Update;
     QSet<int> changedIndexes;
     while (!m_stateUpdates.isEmpty() && time >= m_stateUpdates.first().first){
         foreach (int idx, m_stateUpdates.first().second){
-            if (idx >= m_sprites.count())
+            if (idx >= m_things.count())
                 continue;//TODO: Proper fix(because this does happen and I'm just ignoring it)
-            int stateIdx = m_sprites[idx];
+            int stateIdx = m_things[idx];
             int nextIdx = -1;
             int goalPath = goalSeek(stateIdx, idx);
             if (goalPath == -1){//Random
@@ -347,7 +371,7 @@ uint QSGSpriteEngine::updateSprites(uint time)//### would returning a list of ch
             if (nextIdx == -1)//No to states means stay here
                 nextIdx = stateIdx;
 
-            m_sprites[idx] = nextIdx;
+            m_things[idx] = nextIdx;
             m_duration[idx] = m_states[nextIdx]->variedDuration();
             m_startTimes[idx] = time;
             if (nextIdx != stateIdx){
@@ -370,7 +394,7 @@ uint QSGSpriteEngine::updateSprites(uint time)//### would returning a list of ch
     return m_stateUpdates.first().first;
 }
 
-int QSGSpriteEngine::goalSeek(int curIdx, int spriteIdx, int dist)
+int QSGStochasticEngine::goalSeek(int curIdx, int spriteIdx, int dist)
 {
     QString goalName;
     if (m_goals[spriteIdx] != -1)
@@ -386,7 +410,7 @@ int QSGSpriteEngine::goalSeek(int curIdx, int spriteIdx, int dist)
             return curIdx;
     if (dist < 0)
         dist = m_states.count();
-    QSGSprite* curState = m_states[curIdx];
+    QSGStochasticState* curState = m_states[curIdx];
     for (QVariantMap::const_iterator iter = curState->m_to.constBegin();
         iter!=curState->m_to.constEnd(); iter++){
         if (iter.key() == goalName)
@@ -445,7 +469,7 @@ int QSGSpriteEngine::goalSeek(int curIdx, int spriteIdx, int dist)
     return -1;
 }
 
-void QSGSpriteEngine::addToUpdateList(uint t, int idx)
+void QSGStochasticEngine::addToUpdateList(uint t, int idx)
 {
     for (int i=0; i<m_stateUpdates.count(); i++){
         if (m_stateUpdates[i].first==t){
diff --git a/src/declarative/items/qsgspriteengine_p.h b/src/declarative/items/qsgspriteengine_p.h
index 10860a51f137eddc74dcf32999868710711b7d8b..b2a06f2c87963ccc0b1cf140eb450c744ed55a01 100644
--- a/src/declarative/items/qsgspriteengine_p.h
+++ b/src/declarative/items/qsgspriteengine_p.h
@@ -58,48 +58,171 @@ QT_BEGIN_NAMESPACE
 QT_MODULE(Declarative)
 
 class QSGSprite;
+class QSGStochasticState : public QObject //For internal use
+{
+    Q_OBJECT
+    Q_PROPERTY(int duration READ duration WRITE setDuration NOTIFY durationChanged)
+    Q_PROPERTY(int durationVariation READ durationVariance WRITE setDurationVariance NOTIFY durationVarianceChanged)
+    Q_PROPERTY(QVariantMap to READ to WRITE setTo NOTIFY toChanged)
+    Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged)
+    Q_PROPERTY(qreal speedModifiesDuration READ speedModifer WRITE setSpeedModifier NOTIFY speedModifierChanged)
+    Q_PROPERTY(int frames READ frames WRITE setFrames NOTIFY framesChanged)
+
+public:
+    QSGStochasticState(QObject* parent = 0)
+        : QObject(parent)
+        , m_frames(1)
+        , m_duration(1000)
+    {
+    }
+
+    int duration() const
+    {
+        return m_duration;
+    }
+
+    QString name() const
+    {
+        return m_name;
+    }
+
+    QVariantMap to() const
+    {
+        return m_to;
+    }
+
+    qreal speedModifer() const
+    {
+        return m_speedModifier;
+    }
+
+    int durationVariance() const
+    {
+        return m_durationVariance;
+    }
+
+
+    int variedDuration() const
+    {
+        return m_duration
+                + (m_durationVariance * ((qreal)qrand()/RAND_MAX) * 2)
+                - m_durationVariance;
+    }
 
-class QSGSpriteEngine : public QObject
+    int frames() const
+    {
+        return m_frames;
+    }
+
+signals:
+    void durationChanged(int arg);
+
+    void nameChanged(QString arg);
+
+    void toChanged(QVariantMap arg);
+
+    void speedModifierChanged(qreal arg);
+
+    void durationVarianceChanged(int arg);
+
+    void entered();//### Just playing around - don't expect full state API
+    void framesChanged(int arg);
+
+public slots:
+    void setDuration(int arg)
+    {
+        if (m_duration != arg) {
+            m_duration = arg;
+            emit durationChanged(arg);
+        }
+    }
+
+    void setName(QString arg)
+    {
+        if (m_name != arg) {
+            m_name = arg;
+            emit nameChanged(arg);
+        }
+    }
+
+    void setTo(QVariantMap arg)
+    {
+        if (m_to != arg) {
+            m_to = arg;
+            emit toChanged(arg);
+        }
+    }
+
+    void setSpeedModifier(qreal arg)
+    {
+        if (m_speedModifier != arg) {
+            m_speedModifier = arg;
+            emit speedModifierChanged(arg);
+        }
+    }
+
+    void setDurationVariance(int arg)
+    {
+        if (m_durationVariance != arg) {
+            m_durationVariance = arg;
+            emit durationVarianceChanged(arg);
+        }
+    }
+
+    void setFrames(int arg)
+    {
+        if (m_frames != arg) {
+            m_frames = arg;
+            emit framesChanged(arg);
+        }
+    }
+
+private:
+    QString m_name;
+    int m_frames;
+    QVariantMap m_to;
+    int m_duration;
+    qreal m_speedModifier;
+    int m_durationVariance;
+
+    friend class QSGStochasticEngine;
+};
+
+class QSGStochasticEngine : public QObject
 {
     Q_OBJECT
-    //TODO: Optimize single sprite case
-    Q_PROPERTY(QDeclarativeListProperty<QSGSprite> sprites READ sprites)
+    //TODO: Optimize single state case?
     Q_PROPERTY(QString globalGoal READ globalGoal WRITE setGlobalGoal NOTIFY globalGoalChanged)
+    Q_PROPERTY(QDeclarativeListProperty<QSGStochasticState> states READ states)
 public:
-    explicit QSGSpriteEngine(QObject *parent = 0);
-    QSGSpriteEngine(QList<QSGSprite*> sprites, QObject *parent=0);
-    ~QSGSpriteEngine();
+    explicit QSGStochasticEngine(QObject *parent = 0);
+    QSGStochasticEngine(QList<QSGStochasticState*> states, QObject *parent=0);
+    ~QSGStochasticEngine();
 
-    QDeclarativeListProperty<QSGSprite> sprites()
+    QDeclarativeListProperty<QSGStochasticState> states()
     {
-        return QDeclarativeListProperty<QSGSprite>(this, m_states);
+        return QDeclarativeListProperty<QSGStochasticState>(this, m_states);
     }
+
     QString globalGoal() const
     {
         return m_globalGoal;
     }
 
-    int count() const {return m_sprites.count();}
+    int count() const {return m_things.count();}
     void setCount(int c);
 
-    int spriteState(int sprite=0);// {return m_sprites[sprite];}
-    int spriteStart(int sprite=0);// {return m_startTimes[sprite];}
-    int spriteFrames(int sprite=0);
-    int spriteDuration(int sprite=0);
-    int spriteCount();//Like state count, but for the image states
-    int maxFrames();
 
-    void setGoal(int state, int sprite=0, bool jump=false);
-    QImage assembledImage();
 
-    void startSprite(int index=0, int state=0);
-    void stopSprite(int index=0);
+    void setGoal(int state, int sprite=0, bool jump=false);
+    void start(int index=0, int state=0);
+    void stop(int index=0);
+    int curState(int index=0) {return m_things[index];}
 
-private://Nothing outside should use this?
-    friend class QSGSpriteGoalAffector;//XXX: Fix interface
+    QSGStochasticState* state(int idx){return m_states[idx];}
+    int stateIndex(QSGStochasticState* s){return m_states.indexOf(s);}
     int stateCount() {return m_states.count();}
-    int stateIndex(QSGSprite* s){return m_states.indexOf(s);}//TODO: Does this need to be hidden?
-    QSGSprite* state(int idx){return m_states[idx];}//Used by spritegoal affector
+private:
 signals:
 
     void globalGoalChanged(QString arg);
@@ -116,14 +239,14 @@ public slots:
 
     uint updateSprites(uint time);
 
-private:
+protected:
     friend class QSGParticleSystem;
-    void restartSprite(int sprite);
+    void restart(int index);
     void addToUpdateList(uint t, int idx);
-    int goalSeek(int curState, int spriteIdx, int dist=-1);
-    QList<QSGSprite*> m_states;
+    int goalSeek(int curState, int idx, int dist=-1);
+    QList<QSGStochasticState*> m_states;
     //### Consider struct or class for the four data variables?
-    QVector<int> m_sprites;//int is the index in m_states of the current state
+    QVector<int> m_things;//int is the index in m_states of the current state
     QVector<int> m_goals;
     QVector<int> m_duration;
     QVector<int> m_startTimes;
@@ -136,6 +259,31 @@ private:
     int m_imageStateCount;
 };
 
+class QSGSpriteEngine : public QSGStochasticEngine
+{
+    Q_OBJECT
+    Q_PROPERTY(QDeclarativeListProperty<QSGSprite> sprites READ sprites)
+public:
+    explicit QSGSpriteEngine(QObject *parent = 0);
+    QSGSpriteEngine(QList<QSGSprite*> sprites, QObject *parent=0);
+    ~QSGSpriteEngine();
+    QDeclarativeListProperty<QSGSprite> sprites()
+    {
+        return QDeclarativeListProperty<QSGSprite>(this, m_sprites);
+    }
+
+
+    int spriteState(int sprite=0);
+    int spriteStart(int sprite=0);
+    int spriteFrames(int sprite=0);
+    int spriteDuration(int sprite=0);
+    int spriteCount();//Like state count, but for the image states
+    int maxFrames();
+    QImage assembledImage();
+private:
+    QList<QSGSprite*> m_sprites;
+};
+
 //Common use is to have your own list property which is transparently an engine
 inline void spriteAppend(QDeclarativeListProperty<QSGSprite> *p, QSGSprite* s)
 {
diff --git a/src/declarative/items/qsgspriteimage.cpp b/src/declarative/items/qsgspriteimage.cpp
index afa80e4aa1f339a51ce8519964fa977ed737f545..5557ea564a6754389b27e773958b4509c7e60156 100644
--- a/src/declarative/items/qsgspriteimage.cpp
+++ b/src/declarative/items/qsgspriteimage.cpp
@@ -263,7 +263,7 @@ QSGGeometryNode* QSGSpriteImage::buildNode()
     g->setDrawingMode(GL_TRIANGLES);
 
     SpriteVertices *p = (SpriteVertices *) g->vertexData();
-    m_spriteEngine->startSprite(0);
+    m_spriteEngine->start(0);
     p->v1.animT = p->v2.animT = p->v3.animT = p->v4.animT = 0;
     p->v1.animIdx = p->v2.animIdx = p->v3.animIdx = p->v4.animIdx = 0;
     p->v1.frameCount = p->v2.frameCount = p->v3.frameCount = p->v4.frameCount = m_spriteEngine->spriteFrames();
diff --git a/src/declarative/particles/particles.pri b/src/declarative/particles/particles.pri
index 527bd9ae61e266b02fb23d1ec404e2463d96f1cb..8676e521726f4edef1a2c0bbd664404d317b1b5a 100644
--- a/src/declarative/particles/particles.pri
+++ b/src/declarative/particles/particles.pri
@@ -29,7 +29,8 @@ HEADERS += \
     $$PWD/qsgtargetaffector_p.h \
     $$PWD/qsgcumulativedirection_p.h \
     $$PWD/qsgv8particledata_p.h \
-    $$PWD/qsgrectangleextruder_p.h
+    $$PWD/qsgrectangleextruder_p.h \
+    $$PWD/qsgparticlegroup_p.h
 
 SOURCES += \
     $$PWD/qsgangledirection.cpp \
@@ -60,7 +61,8 @@ SOURCES += \
     $$PWD/qsgtargetaffector.cpp \
     $$PWD/qsgcumulativedirection.cpp \
     $$PWD/qsgv8particledata.cpp \
-    $$PWD/qsgrectangleextruder.cpp
+    $$PWD/qsgrectangleextruder.cpp \
+    $$PWD/qsgparticlegroup.cpp
 
 RESOURCES += \
     $$PWD/particles.qrc
diff --git a/src/declarative/particles/qsgcustomparticle.cpp b/src/declarative/particles/qsgcustomparticle.cpp
index ab4cfa0a3aefa467dfaed02011483c06978100be..a1d65a5360f5c4ea12dad7574a672ed9d84b59b9 100644
--- a/src/declarative/particles/qsgcustomparticle.cpp
+++ b/src/declarative/particles/qsgcustomparticle.cpp
@@ -465,7 +465,7 @@ QSGShaderEffectNode* QSGCustomParticle::buildCustomNodes()
         s.vertexCode = qt_particles_default_vertex_code;
     s.vertexCode = qt_particles_template_vertex_code + s.vertexCode;
     m_material.setProgramSource(s);
-    foreach (const QString &str, m_particles){
+    foreach (const QString &str, m_groups){
         int gIdx = m_system->m_groupIds[str];
         int count = m_system->m_groupData[gIdx]->size();
         //Create Particle Geometry
diff --git a/src/declarative/particles/qsgimageparticle.cpp b/src/declarative/particles/qsgimageparticle.cpp
index 581024d5787b627984f0ed4a5aee05e76e16aa52..b1aef784e87984f2bfdb592791791a5518d01f6a 100644
--- a/src/declarative/particles/qsgimageparticle.cpp
+++ b/src/declarative/particles/qsgimageparticle.cpp
@@ -1021,7 +1021,7 @@ QSGGeometryNode* QSGImageParticle::buildParticleNodes()
         m_material->setFlag(QSGMaterial::Blending);
     }
 
-    foreach (const QString &str, m_particles){
+    foreach (const QString &str, m_groups){
         int gIdx = m_system->m_groupIds[str];
         int count = m_system->m_groupData[gIdx]->size();
         QSGGeometryNode* node = new QSGGeometryNode();
@@ -1151,7 +1151,7 @@ void QSGImageParticle::prepareNextFrame()
         //Advance State
         getState<ImageMaterialData>(m_material)->animcount = m_spriteEngine->spriteCount();
         m_spriteEngine->updateSprites(timeStamp);
-        foreach (const QString &str, m_particles){
+        foreach (const QString &str, m_groups){
             int gIdx = m_system->m_groupIds[str];
             int count = m_system->m_groupData[gIdx]->size();
 
@@ -1199,7 +1199,7 @@ void QSGImageParticle::initialize(int gIdx, int pIdx)
             datum->animT = datum->t;
             datum->animIdx = 0;
             if (m_spriteEngine){
-                m_spriteEngine->startSprite(spriteIdx);
+                m_spriteEngine->start(spriteIdx);
                 datum->frameCount = m_spriteEngine->spriteFrames(spriteIdx);
                 datum->frameDuration = m_spriteEngine->spriteDuration(spriteIdx);
             }else{
diff --git a/src/declarative/particles/qsgimageparticle_p.h b/src/declarative/particles/qsgimageparticle_p.h
index 01eacbad995930d72e858d5b38ee4c8c623ec270..1f87b16f63cf1580a544eaaa7115f28be91d81b1 100644
--- a/src/declarative/particles/qsgimageparticle_p.h
+++ b/src/declarative/particles/qsgimageparticle_p.h
@@ -56,7 +56,7 @@ class ImageMaterialData;
 class QSGGeometryNode;
 
 class QSGSprite;
-class QSGSpriteEngine;
+class QSGStochasticEngine;
 
 struct SimpleVertex {
     float x;
@@ -186,7 +186,7 @@ public:
 
 
     QDeclarativeListProperty<QSGSprite> sprites();
-    QSGSpriteEngine* spriteEngine() {return m_spriteEngine;}
+    QSGStochasticEngine* spriteEngine() {return m_spriteEngine;}
 
     enum EntryEffect {
         None = 0,
diff --git a/src/declarative/particles/qsgitemparticle.cpp b/src/declarative/particles/qsgitemparticle.cpp
index c33088098095bcb04f4d78f5388fa3f309f61c64..2572d677833bc7e5a5fc9e8e78a398b47710395d 100644
--- a/src/declarative/particles/qsgitemparticle.cpp
+++ b/src/declarative/particles/qsgitemparticle.cpp
@@ -226,7 +226,7 @@ void QSGItemParticle::prepareNextFrame()
         return;
 
     //TODO: Size, better fade?
-    foreach (const QString &str, m_particles){
+    foreach (const QString &str, m_groups){
         int gIdx = m_system->m_groupIds[str];
         int count = m_system->m_groupData[gIdx]->size();
 
diff --git a/src/declarative/particles/qsgparticleaffector.cpp b/src/declarative/particles/qsgparticleaffector.cpp
index cff3c294443158e50bae29fca6615583984a4563..7cb4869f3eeb32059955d36e5535aee3fbe289cf 100644
--- a/src/declarative/particles/qsgparticleaffector.cpp
+++ b/src/declarative/particles/qsgparticleaffector.cpp
@@ -59,7 +59,7 @@ QT_BEGIN_NAMESPACE
     If the Affector is a direct child of a ParticleSystem, it will automatically be associated with it.
 */
 /*!
-    \qmlproperty list<string> QtQuick.Particles2::Affector::particles
+    \qmlproperty list<string> QtQuick.Particles2::Affector::groups
     Which logical particle groups will be affected.
 
     If empty, it will affect all particles.
@@ -100,9 +100,9 @@ QT_BEGIN_NAMESPACE
     x,y are the coordinates of the affected particle, relative to the ParticleSystem.
 
 */
-//TODO: Document particle 'type'
+
 /*!
-    \qmlsignal QtQuick.Particles2::Affector::affectParticle(particle, dt)
+    \qmlsignal QtQuick.Particles2::Affector::affectParticle(particle particle, real dt)
 
     This handler is called when particles are selected to be affected.
 
@@ -113,7 +113,7 @@ QT_BEGIN_NAMESPACE
     high-volume particle systems.
 */
 /*!
-    \qmlsignal QtQuick.Particles2::Affector::affected(x, y)
+    \qmlsignal QtQuick.Particles2::Affector::affected(real x, real y)
 
     This handler is called when a particle is selected to be affected. It will
     only be called if signal is set to true.
@@ -142,12 +142,12 @@ void QSGParticleAffector::componentComplete()
 
 bool QSGParticleAffector::activeGroup(int g) {
     if (m_updateIntSet){
-        m_groups.clear();
-        foreach (const QString &p, m_particles)
-            m_groups << m_system->m_groupIds[p];//###Can this occur before group ids are properly assigned?
+        m_groupIds.clear();
+        foreach (const QString &p, m_groups)
+            m_groupIds << m_system->m_groupIds[p];//###Can this occur before group ids are properly assigned?
         m_updateIntSet = false;
     }
-    return m_groups.isEmpty() || m_groups.contains(g);
+    return m_groupIds.isEmpty() || m_groupIds.contains(g);
 }
 
 void QSGParticleAffector::affectSystem(qreal dt)
@@ -195,7 +195,7 @@ bool QSGParticleAffector::affectParticle(QSGParticleData *, qreal )
 void QSGParticleAffector::reset(QSGParticleData* pd)
 {//TODO: This, among other ones, should be restructured so they don't all need to remember to call the superclass
     if (m_onceOff)
-        if (m_groups.isEmpty() || m_groups.contains(pd->group))
+        if (m_groups.isEmpty() || m_groupIds.contains(pd->group))
             m_onceOffed.remove(qMakePair(pd->group, pd->index));
 }
 
diff --git a/src/declarative/particles/qsgparticleaffector_p.h b/src/declarative/particles/qsgparticleaffector_p.h
index 5700969aad8d140198c444958acbd25afd24813f..0dadeff6a746d82ed1ad9f3191c2c32a3f8eaa8e 100644
--- a/src/declarative/particles/qsgparticleaffector_p.h
+++ b/src/declarative/particles/qsgparticleaffector_p.h
@@ -56,7 +56,7 @@ class QSGParticleAffector : public QSGItem
 {
     Q_OBJECT
     Q_PROPERTY(QSGParticleSystem* system READ system WRITE setSystem NOTIFY systemChanged)
-    Q_PROPERTY(QStringList particles READ particles WRITE setParticles NOTIFY particlesChanged)
+    Q_PROPERTY(QStringList groups READ groups WRITE setGroups NOTIFY groupsChanged)
     Q_PROPERTY(QStringList whenCollidingWith READ whenCollidingWith WRITE setWhenCollidingWith NOTIFY whenCollidingWithChanged)
     Q_PROPERTY(bool enabled READ enabled WRITE setEnabled NOTIFY enabledChanged)
     Q_PROPERTY(bool once READ onceOff WRITE setOnceOff NOTIFY onceChanged)
@@ -71,9 +71,9 @@ public:
         return m_system;
     }
 
-    QStringList particles() const
+    QStringList groups() const
     {
-        return m_particles;
+        return m_groups;
     }
 
     bool enabled() const
@@ -100,7 +100,7 @@ signals:
 
     void systemChanged(QSGParticleSystem* arg);
 
-    void particlesChanged(QStringList arg);
+    void groupsChanged(QStringList arg);
 
     void enabledChanged(bool arg);
 
@@ -122,12 +122,12 @@ void setSystem(QSGParticleSystem* arg)
     }
 }
 
-void setParticles(QStringList arg)
+void setGroups(QStringList arg)
 {
-    if (m_particles != arg) {
-        m_particles = arg;
+    if (m_groups != arg) {
+        m_groups = arg;
         m_updateIntSet = true;
-        emit particlesChanged(arg);
+        emit groupsChanged(arg);
     }
 }
 
@@ -169,14 +169,14 @@ protected:
     virtual bool affectParticle(QSGParticleData *d, qreal dt);
     bool m_needsReset;//### What is this really saving?
     QSGParticleSystem* m_system;
-    QStringList m_particles;
+    QStringList m_groups;
     bool activeGroup(int g);
     bool m_enabled;
     virtual void componentComplete();
     QPointF m_offset;
     bool isAffectedConnected();
 private:
-    QSet<int> m_groups;
+    QSet<int> m_groupIds;
     QSet<QPair<int, int> > m_onceOffed;
     bool m_updateIntSet;
 
diff --git a/src/declarative/particles/qsgparticleemitter.cpp b/src/declarative/particles/qsgparticleemitter.cpp
index 13ab3e61855d80225dbbec0d6b052afec9aac6ce..fdba3def415c5cdba7ed44af7286d24f274f4a51 100644
--- a/src/declarative/particles/qsgparticleemitter.cpp
+++ b/src/declarative/particles/qsgparticleemitter.cpp
@@ -68,9 +68,9 @@ QT_BEGIN_NAMESPACE
     This can be omitted if the Emitter is a direct child of the ParticleSystem
 */
 /*!
-    \qmlproperty string QtQuick.Particles2::Emitter::particle
+    \qmlproperty string QtQuick.Particles2::Emitter::group
 
-    This is the type of logical particle which it will emit.
+    This is the logical particle group which it will emit into.
 
     Default value is "" (empty string).
 */
@@ -396,7 +396,7 @@ void QSGParticleEmitter::emitWindow(int timeStamp)
         pt = time;
     while ((pt < time && m_emitCap) || !m_burstQueue.isEmpty()) {
         //int pos = m_last_particle % m_particle_count;
-        QSGParticleData* datum = m_system->newDatum(m_system->m_groupIds[m_particle], !m_overwrite);
+        QSGParticleData* datum = m_system->newDatum(m_system->m_groupIds[m_group], !m_overwrite);
         if (datum){//actually emit(otherwise we've been asked to skip this one)
             datum->e = this;//###useful?
             qreal t = 1 - (pt - opt) / dt;
diff --git a/src/declarative/particles/qsgparticleemitter_p.h b/src/declarative/particles/qsgparticleemitter_p.h
index 8ed7ee7053f0c2c5532b2ac44103631a5647d7f8..8bd205b207b7b59666ed85aaf813668e8790798e 100644
--- a/src/declarative/particles/qsgparticleemitter_p.h
+++ b/src/declarative/particles/qsgparticleemitter_p.h
@@ -61,7 +61,7 @@ class QSGParticleEmitter : public QSGItem
 {
     Q_OBJECT
     Q_PROPERTY(QSGParticleSystem* system READ system WRITE setSystem NOTIFY systemChanged)
-    Q_PROPERTY(QString particle READ particle WRITE setParticle NOTIFY particleChanged)
+    Q_PROPERTY(QString group READ group WRITE setGroup NOTIFY groupChanged)
     Q_PROPERTY(QSGParticleExtruder* shape READ extruder WRITE setExtruder NOTIFY extruderChanged)
     Q_PROPERTY(bool enabled READ enabled WRITE setEnabled NOTIFY enabledChanged)
     Q_PROPERTY(int startTime READ startTime WRITE setStartTime NOTIFY startTimeChanged)
@@ -109,9 +109,9 @@ public:
         return m_system;
     }
 
-    QString particle() const
+    QString group() const
     {
-        return m_particle;
+        return m_group;
     }
 
     int particleDurationVariation() const
@@ -130,7 +130,7 @@ signals:
 
     void systemChanged(QSGParticleSystem* arg);
 
-    void particleChanged(QString arg);
+    void groupChanged(QString arg);
 
     void particleDurationVariationChanged(int arg);
 
@@ -185,11 +185,11 @@ public slots:
         }
     }
 
-    void setParticle(QString arg)
+    void setGroup(QString arg)
     {
-        if (m_particle != arg) {
-            m_particle = arg;
-            emit particleChanged(arg);
+        if (m_group != arg) {
+            m_group = arg;
+            emit groupChanged(arg);
         }
     }
 
@@ -308,7 +308,7 @@ protected:
        int m_particleDurationVariation;
        bool m_enabled;
        QSGParticleSystem* m_system;
-       QString m_particle;
+       QString m_group;
        QSGParticleExtruder* m_extruder;
        QSGParticleExtruder* m_defaultExtruder;
        QSGParticleExtruder* effectiveExtruder();
diff --git a/src/declarative/particles/qsgparticlegroup.cpp b/src/declarative/particles/qsgparticlegroup.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..28eb4d2c85287132e84b96c8949fc8f2ee4bf64a
--- /dev/null
+++ b/src/declarative/particles/qsgparticlegroup.cpp
@@ -0,0 +1,142 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Declarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsgparticlegroup_p.h"
+
+/*!
+    \qmlclass ParticleGroup QSGParticleGroup
+    \inqmlmodule QtQuick.Particles 2
+    \brief ParticleGroup elements allow you to set attributes on a logical particle group.
+
+    This element allows you to set timed transitions on particle groups.
+
+    You can also use this element to group particle system elements related to the logical
+    particle group. Emitters, Affectors and Painters set as direct children of a ParticleGroup
+    will automatically apply to that logical particle group. TrailEmitters will automatically follow
+    the group.
+
+    If a ParticleGroup element is not defined for a group, the group will function normally as if
+    none of the transition properties were set.
+*/
+/*!
+    \qmlproperty ParticleSystem QtQuick.Particles2::ParticleGroup::system
+    This is the system which will contain the group.
+
+    If the ParticleGroup is a direct child of a ParticleSystem, it will automatically be associated with it.
+*/
+/*!
+    \qmlproperty string QtQuick.Particles2::ParticleGroup::name
+    This is the name of the particle group, and how it is generally referred to by other elements.
+
+    If elements refer to a name which does not have an explicit ParticleGroup created, it will
+    work normally (with no transitions specified for the group). If you do not need to assign
+    duration based transitions to a group, you do not need to create a ParticleGroup with that name (although you may).
+*/
+/*!
+    \qmlproperty int QtQuick.Particles2::ParticleGroup::duration
+    The time in milliseconds before the group will attempt to transition.
+
+*/
+/*!
+    \qmlproperty ParticleSystem QtQuick.Particles2::ParticleGroup::durationVariation
+    The maximum number of milliseconds that the duration of the transition cycle varies per particle in the group.
+
+    Default value is zero.
+*/
+/*!
+    \qmlproperty ParticleSystem QtQuick.Particles2::ParticleGroup::to
+    The weighted list of transitions valid for this group.
+
+    If the chosen transition stays in this group, another duration (+/- up to durationVariation)
+    milliseconds will occur before another transition is attempted.
+*/
+
+QSGParticleGroup::QSGParticleGroup(QObject* parent)
+    : QSGStochasticState(parent)
+{
+
+}
+
+void delayedRedirect(QDeclarativeListProperty<QObject> *prop, QObject *value)
+{
+    QSGParticleGroup* pg = qobject_cast<QSGParticleGroup*>(prop->object);
+    if (pg)
+        pg->delayRedirect(value);
+}
+
+QDeclarativeListProperty<QObject> QSGParticleGroup::particleChildren()
+{
+    QSGParticleSystem* system = qobject_cast<QSGParticleSystem*>(parent());
+    if (system)
+        return QDeclarativeListProperty<QObject>(this, 0, &QSGParticleSystem::statePropertyRedirect);
+    else
+        return QDeclarativeListProperty<QObject>(this, 0, &delayedRedirect);
+}
+
+void QSGParticleGroup::setSystem(QSGParticleSystem* arg)
+{
+    if (m_system != arg) {
+        m_system = arg;
+        m_system->registerParticleGroup(this);
+        performDelayedRedirects();
+        emit systemChanged(arg);
+    }
+}
+
+void QSGParticleGroup::delayRedirect(QObject *obj)
+{
+    m_delayedRedirects << obj;
+}
+
+void QSGParticleGroup::performDelayedRedirects()
+{
+    if (!m_system)
+        return;
+    foreach (QObject* obj, m_delayedRedirects)
+        m_system->stateRedirect(this, m_system, obj);
+
+    m_delayedRedirects.clear();
+}
+
+void QSGParticleGroup::componentComplete(){
+    if (!m_system && qobject_cast<QSGParticleSystem*>(parent()))
+        setSystem(qobject_cast<QSGParticleSystem*>(parent()));
+}
diff --git a/src/declarative/particles/qsgparticlegroup_p.h b/src/declarative/particles/qsgparticlegroup_p.h
new file mode 100644
index 0000000000000000000000000000000000000000..346b4ab77e80988cc97d316f82e9a327fab67754
--- /dev/null
+++ b/src/declarative/particles/qsgparticlegroup_p.h
@@ -0,0 +1,107 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Declarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef QSGPARTICLEGROUP
+#define QSGPARTICLEGROUP
+#include "qsgspriteengine_p.h"
+#include "qsgparticlesystem_p.h"
+#include "qdeclarativeparserstatus.h"
+
+class QSGParticleGroup : public QSGStochasticState, public QDeclarativeParserStatus
+{
+    Q_OBJECT
+    //### Would setting limits per group be useful? Or clutter the API?
+    //Q_PROPERTY(int maximumAlive READ maximumAlive WRITE setMaximumAlive NOTIFY maximumAliveChanged)
+
+    Q_PROPERTY(QSGParticleSystem* system READ system WRITE setSystem NOTIFY systemChanged)
+
+    //Intercept children requests and assign to the group & system
+    Q_PROPERTY(QDeclarativeListProperty<QObject> particleChildren READ particleChildren DESIGNABLE false)//### Hidden property for in-state system definitions - ought not to be used in actual "Sprite" states
+    Q_CLASSINFO("DefaultProperty", "particleChildren")
+
+public:
+    explicit QSGParticleGroup(QObject* parent = 0);
+
+    QDeclarativeListProperty<QObject> particleChildren();
+
+    int maximumAlive() const
+    {
+        return m_maximumAlive;
+    }
+
+    QSGParticleSystem* system() const
+    {
+        return m_system;
+    }
+
+public slots:
+
+    void setMaximumAlive(int arg)
+    {
+        if (m_maximumAlive != arg) {
+            m_maximumAlive = arg;
+            emit maximumAliveChanged(arg);
+        }
+    }
+
+    void setSystem(QSGParticleSystem* arg);
+
+    void delayRedirect(QObject* obj);
+
+signals:
+
+    void maximumAliveChanged(int arg);
+
+    void systemChanged(QSGParticleSystem* arg);
+
+protected:
+    virtual void componentComplete();
+    virtual void classBegin(){;}
+
+private:
+
+    void performDelayedRedirects();
+
+    int m_maximumAlive;
+    QSGParticleSystem* m_system;
+    QList<QObject*> m_delayedRedirects;
+};
+
+#endif
diff --git a/src/declarative/particles/qsgparticlepainter.cpp b/src/declarative/particles/qsgparticlepainter.cpp
index f4639c20a2c49d8b24ebfbaae852b724a7005969..670c1f21182cf7f7233bf8683af5297bb1807bdd 100644
--- a/src/declarative/particles/qsgparticlepainter.cpp
+++ b/src/declarative/particles/qsgparticlepainter.cpp
@@ -58,10 +58,10 @@ QT_BEGIN_NAMESPACE
     If the ParticlePainter is a direct child of a ParticleSystem, it will automatically be associated with it.
 */
 /*!
-    \qmlproperty list<string> QtQuick.Particles2::ParticlePainter::particles
+    \qmlproperty list<string> QtQuick.Particles2::ParticlePainter::groups
     Which logical particle groups will be painted.
 
-    If empty, it will paint the default particle ("").
+    If empty, it will paint the default particle group ("").
 */
 QSGParticlePainter::QSGParticlePainter(QSGItem *parent) :
     QSGItem(parent),
@@ -144,7 +144,7 @@ void QSGParticlePainter::calcSystemOffset(bool resetPending)
     m_systemOffset = -1 * this->mapFromItem(m_system, QPointF(0.0, 0.0));
     if (lastOffset != m_systemOffset && !resetPending){
         //Reload all particles//TODO: Necessary?
-        foreach (const QString &g, m_particles){
+        foreach (const QString &g, m_groups){
             int gId = m_system->m_groupIds[g];
             foreach (QSGParticleData* d, m_system->m_groupData[gId]->data)
                 reload(d);
diff --git a/src/declarative/particles/qsgparticlepainter_p.h b/src/declarative/particles/qsgparticlepainter_p.h
index 08ae3aede814d4ff5a542c14a797210023ff421b..d469947453c7ed4aa30f3a4fd3721280ed20bcdf 100644
--- a/src/declarative/particles/qsgparticlepainter_p.h
+++ b/src/declarative/particles/qsgparticlepainter_p.h
@@ -58,7 +58,7 @@ class QSGParticlePainter : public QSGItem
 {
     Q_OBJECT
     Q_PROPERTY(QSGParticleSystem* system READ system WRITE setSystem NOTIFY systemChanged)
-    Q_PROPERTY(QStringList particles READ particles WRITE setParticles NOTIFY particlesChanged)
+    Q_PROPERTY(QStringList groups READ groups WRITE setGroups NOTIFY groupsChanged)
 
 public:
     explicit QSGParticlePainter(QSGItem *parent = 0);
@@ -74,25 +74,25 @@ public:
     }
 
 
-    QStringList particles() const
+    QStringList groups() const
     {
-        return m_particles;
+        return m_groups;
     }
 
 signals:
     void countChanged();
     void systemChanged(QSGParticleSystem* arg);
 
-    void particlesChanged(QStringList arg);
+    void groupsChanged(QStringList arg);
 
 public slots:
 void setSystem(QSGParticleSystem* arg);
 
-void setParticles(QStringList arg)
+void setGroups(QStringList arg)
 {
-    if (m_particles != arg) {
-        m_particles = arg;
-        emit particlesChanged(arg);
+    if (m_groups != arg) {
+        m_groups = arg;
+        emit groupsChanged(arg);
     }
 }
 
@@ -121,7 +121,7 @@ protected:
     friend class QSGParticleSystem;
     int m_count;
     bool m_pleaseReset;
-    QStringList m_particles;
+    QStringList m_groups;
     QPointF m_systemOffset;
 
 private:
diff --git a/src/declarative/particles/qsgparticlesmodule.cpp b/src/declarative/particles/qsgparticlesmodule.cpp
index 9fa0ebad97d38d6ea5a14b38c017616086eef7a4..f880842acb1d4d96d5c385991a80dd309e5b7787 100644
--- a/src/declarative/particles/qsgparticlesmodule.cpp
+++ b/src/declarative/particles/qsgparticlesmodule.cpp
@@ -67,6 +67,7 @@
 #include "qsgcumulativedirection_p.h"
 #include "qsgcustomaffector_p.h"
 #include "qsgrectangleextruder_p.h"
+#include "qsgparticlegroup_p.h"
 
 QT_BEGIN_NAMESPACE
 
@@ -75,6 +76,7 @@ void QSGParticlesModule::defineModule()
     const char* uri = "QtQuick.Particles";
 
     qmlRegisterType<QSGParticleSystem>(uri, 2, 0, "ParticleSystem");
+    qmlRegisterType<QSGParticleGroup>(uri, 2, 0, "ParticleGroup");
 
     qmlRegisterType<QSGImageParticle>(uri, 2, 0, "ImageParticle");
     qmlRegisterType<QSGCustomParticle>(uri, 2, 0, "CustomParticle");
diff --git a/src/declarative/particles/qsgparticlesystem.cpp b/src/declarative/particles/qsgparticlesystem.cpp
index 085e6afe649d7392d84b5f648e05e08f4ff80cb8..bc2707314235e0ebaf418f89eca4516f6393c9ad 100644
--- a/src/declarative/particles/qsgparticlesystem.cpp
+++ b/src/declarative/particles/qsgparticlesystem.cpp
@@ -47,6 +47,7 @@
 #include "qsgspriteengine_p.h"
 #include "qsgsprite_p.h"
 #include "qsgv8particledata_p.h"
+#include "qsgparticlegroup_p.h"
 
 #include "qsgtrailemitter_p.h"//###For auto-follow on states, perhaps should be in emitter?
 #include <private/qdeclarativeengine_p.h>
@@ -549,8 +550,8 @@ void QSGParticleData::debugDump()
 {
     qDebug() << "Particle" << systemIndex << group << "/" << index << stillAlive()
              << "Pos: " << x << "," << y
-             //<< "Vel: " << vx << "," << sy
-             //<< "Acc: " << ax << "," << ay
+             << "Vel: " << vx << "," << vy
+             << "Acc: " << ax << "," << ay
              << "Size: " << size << "," << endSize
              << "Time: " << t << "," <<lifeSpan << ";" << (system->m_timeInt / 1000.0) ;
 }
@@ -559,7 +560,6 @@ bool QSGParticleData::stillAlive()
 {
     if (!system)
         return false;
-    //fprintf(stderr, "%.9lf %.9lf\n",((qreal)system->m_timeInt/1000.0), (t+lifeSpan));
     return (t + lifeSpan - EPSILON) > ((qreal)system->m_timeInt/1000.0);
 }
 
@@ -601,7 +601,7 @@ void QSGParticleData::extendLife(float time)
 
 QSGParticleSystem::QSGParticleSystem(QSGItem *parent) :
     QSGItem(parent), m_particle_count(0), m_running(true), m_paused(false)
-  , m_nextIndex(0), m_componentComplete(false), m_spriteEngine(0)
+  , m_nextIndex(0), m_componentComplete(false), m_stateEngine(0)
 {
     connect(&m_painterMapper, SIGNAL(mapped(QObject*)),
             this, SLOT(loadPainter(QObject*)));
@@ -630,16 +630,11 @@ void QSGParticleSystem::initGroups()
     m_nextGroupId = 1;
 }
 
-    QDeclarativeListProperty<QSGSprite> QSGParticleSystem::particleStates()
-{
-    return QDeclarativeListProperty<QSGSprite>(this, &m_states, spriteAppend, spriteCount, spriteAt, spriteClear);
-}
-
 void QSGParticleSystem::registerParticlePainter(QSGParticlePainter* p)
 {
     //TODO: a way to Unregister emitters, painters and affectors
     m_painters << QPointer<QSGParticlePainter>(p);//###Set or uniqueness checking?
-    connect(p, SIGNAL(particlesChanged(QStringList)),
+    connect(p, SIGNAL(groupsChanged(QStringList)),
             &m_painterMapper, SLOT(map()));
     loadPainter(p);
 }
@@ -649,7 +644,7 @@ void QSGParticleSystem::registerParticleEmitter(QSGParticleEmitter* e)
     m_emitters << QPointer<QSGParticleEmitter>(e);//###How to get them out?
     connect(e, SIGNAL(particleCountChanged()),
             this, SLOT(emittersChanged()));
-    connect(e, SIGNAL(particleChanged(QString)),
+    connect(e, SIGNAL(groupChanged(QString)),
             this, SLOT(emittersChanged()));
     emittersChanged();
     e->reset();//Start, so that starttime factors appropriately
@@ -660,6 +655,12 @@ void QSGParticleSystem::registerParticleAffector(QSGParticleAffector* a)
     m_affectors << QPointer<QSGParticleAffector>(a);
 }
 
+void QSGParticleSystem::registerParticleGroup(QSGParticleGroup* g)
+{
+    m_groups << QPointer<QSGParticleGroup>(g);
+    createEngine();
+}
+
 void QSGParticleSystem::setRunning(bool arg)
 {
     if (m_running != arg) {
@@ -685,40 +686,45 @@ void QSGParticleSystem::setPaused(bool arg){
     }
 }
 
-void QSGParticleSystem::stateRedirect(QDeclarativeListProperty<QObject> *prop, QObject *value)
+void QSGParticleSystem::statePropertyRedirect(QDeclarativeListProperty<QObject> *prop, QObject *value)
 {
     //Hooks up automatic state-associated stuff
     QSGParticleSystem* sys = qobject_cast<QSGParticleSystem*>(prop->object->parent());
-    QSGSprite* sprite = qobject_cast<QSGSprite*>(prop->object);
-    if (!sprite || !sys)
+    QSGParticleGroup* group = qobject_cast<QSGParticleGroup*>(prop->object);
+    if (!group || !sys || !value)
         return;
+    stateRedirect(group, sys, value);
+}
+
+void QSGParticleSystem::stateRedirect(QSGParticleGroup* group, QSGParticleSystem* sys, QObject *value)
+{
     QStringList list;
-    list << sprite->name();
+    list << group->name();
     QSGParticleAffector* a = qobject_cast<QSGParticleAffector*>(value);
     if (a){
         a->setParentItem(sys);
-        a->setParticles(list);
+        a->setGroups(list);
         a->setSystem(sys);
         return;
     }
     QSGTrailEmitter* fe = qobject_cast<QSGTrailEmitter*>(value);
     if (fe){
         fe->setParentItem(sys);
-        fe->setFollow(sprite->name());
+        fe->setFollow(group->name());
         fe->setSystem(sys);
         return;
     }
     QSGParticleEmitter* e = qobject_cast<QSGParticleEmitter*>(value);
     if (e){
         e->setParentItem(sys);
-        e->setParticle(sprite->name());
+        e->setGroup(group->name());
         e->setSystem(sys);
         return;
     }
     QSGParticlePainter* p = qobject_cast<QSGParticlePainter*>(value);
     if (p){
         p->setParentItem(sys);
-        p->setParticles(list);
+        p->setGroups(list);
         p->setSystem(sys);
         return;
     }
@@ -785,14 +791,14 @@ void QSGParticleSystem::loadPainter(QObject *p)
     foreach (QSGParticleGroupData* sg, m_groupData)
         sg->painters.remove(painter);
     int particleCount = 0;
-    if (painter->particles().isEmpty()){//Uses default particle
+    if (painter->groups().isEmpty()){//Uses default particle
         QStringList def;
         def << "";
-        painter->setParticles(def);
+        painter->setGroups(def);
         particleCount += m_groupData[0]->size();
         m_groupData[0]->painters << painter;
     }else{
-        foreach (const QString &group, painter->particles()){
+        foreach (const QString &group, painter->groups()){
             if (group != QLatin1String("") && !m_groupIds[group]){//new group
                 int id = m_nextGroupId++;
                 QSGParticleGroupData* gd = new QSGParticleGroupData(id, this);
@@ -824,16 +830,16 @@ void QSGParticleSystem::emittersChanged()
     }
 
     foreach (QSGParticleEmitter* e, m_emitters){//Populate groups and set sizes.
-        if (!m_groupIds.contains(e->particle())
-                || (!e->particle().isEmpty() && !m_groupIds[e->particle()])){//or it was accidentally inserted by a failed lookup earlier
+        if (!m_groupIds.contains(e->group())
+                || (!e->group().isEmpty() && !m_groupIds[e->group()])){//or it was accidentally inserted by a failed lookup earlier
             int id = m_nextGroupId++;
             QSGParticleGroupData* gd = new QSGParticleGroupData(id, this);
-            m_groupIds.insert(e->particle(), id);
+            m_groupIds.insert(e->group(), id);
             m_groupData.insert(id, gd);
             previousSizes << 0;
             newSizes << 0;
         }
-        newSizes[m_groupIds[e->particle()]] += e->particleCount();
+        newSizes[m_groupIds[e->group()]] += e->particleCount();
         //###: Cull emptied groups?
     }
 
@@ -850,7 +856,7 @@ void QSGParticleSystem::emittersChanged()
     foreach (QSGParticlePainter *p, m_painters)
         loadPainter(p);
 
-    if (!m_states.isEmpty())
+    if (!m_groups.isEmpty())
         createEngine();
 
     if (m_debugMode)
@@ -861,58 +867,63 @@ void QSGParticleSystem::createEngine()
 {
     if (!m_componentComplete)
         return;
+    if (m_stateEngine && m_debugMode)
+        qDebug() << "Resetting Existing Sprite Engine...";
     //### Solve the losses if size/states go down
-    foreach (QSGSprite* sprite, m_states){
+    foreach (QSGParticleGroup* group, m_groups){
         bool exists = false;
         foreach (const QString &name, m_groupIds.keys())
-            if (sprite->name() == name)
+            if (group->name() == name)
                 exists = true;
         if (!exists){
             int id = m_nextGroupId++;
             QSGParticleGroupData* gd = new QSGParticleGroupData(id, this);
-            m_groupIds.insert(sprite->name(), id);
+            m_groupIds.insert(group->name(), id);
             m_groupData.insert(id, gd);
         }
     }
 
-    if (m_states.count()){
-        //Reorder Sprite List so as to have the same order as groups
-        QList<QSGSprite*> newList;
+    if (m_groups.count()){
+        //Reorder groups List so as to have the same order as groupData
+        QList<QSGParticleGroup*> newList;
         for (int i=0; i<m_nextGroupId; i++){
             bool exists = false;
             QString name = m_groupData[i]->name();
-            foreach (QSGSprite* existing, m_states){
+            foreach (QSGParticleGroup* existing, m_groups){
                 if (existing->name() == name){
                     newList << existing;
                     exists = true;
                 }
             }
             if (!exists){
-                newList << new QSGSprite(this);
+                newList << new QSGParticleGroup(this);
                 newList.back()->setName(name);
             }
         }
-        m_states = newList;
+        m_groups = newList;
+        QList<QSGStochasticState*> states;
+        foreach (QSGParticleGroup* g, m_groups)
+            states << (QSGStochasticState*)g;
 
-        if (!m_spriteEngine)
-            m_spriteEngine = new QSGSpriteEngine(this);
-        m_spriteEngine->setCount(m_particle_count);
-        m_spriteEngine->m_states = m_states;
+        if (!m_stateEngine)
+            m_stateEngine = new QSGStochasticEngine(this);
+        m_stateEngine->setCount(m_particle_count);
+        m_stateEngine->m_states = states;
 
-        connect(m_spriteEngine, SIGNAL(stateChanged(int)),
+        connect(m_stateEngine, SIGNAL(stateChanged(int)),
                 this, SLOT(particleStateChange(int)));
 
     }else{
-        if (m_spriteEngine)
-            delete m_spriteEngine;
-        m_spriteEngine = 0;
+        if (m_stateEngine)
+            delete m_stateEngine;
+        m_stateEngine = 0;
     }
 
 }
 
 void QSGParticleSystem::particleStateChange(int idx)
 {
-    moveGroups(m_bySysIdx[idx], m_spriteEngine->spriteState(idx));
+    moveGroups(m_bySysIdx[idx], m_stateEngine->curState(idx));
 }
 
 void QSGParticleSystem::moveGroups(QSGParticleData *d, int newGIdx)
@@ -934,8 +945,8 @@ int QSGParticleSystem::nextSystemIndex()
     }
     if (m_nextIndex >= m_bySysIdx.size()){
         m_bySysIdx.resize(m_bySysIdx.size() < 10 ? 10 : m_bySysIdx.size()*1.1);//###+1,10%,+10? Choose something non-arbitrarily
-        if (m_spriteEngine)
-            m_spriteEngine->setCount(m_bySysIdx.size());
+        if (m_stateEngine)
+            m_stateEngine->setCount(m_bySysIdx.size());
 
     }
     return m_nextIndex++;
@@ -954,8 +965,8 @@ QSGParticleData* QSGParticleSystem::newDatum(int groupId, bool respectLimits, in
             ret->systemIndex = nextSystemIndex();
     }else{
         if (ret->systemIndex != -1){
-            if (m_spriteEngine)
-                m_spriteEngine->stopSprite(ret->systemIndex);
+            if (m_stateEngine)
+                m_stateEngine->stop(ret->systemIndex);
             m_reusableIndexes << ret->systemIndex;
             m_bySysIdx[ret->systemIndex] = 0;
         }
@@ -963,8 +974,8 @@ QSGParticleData* QSGParticleSystem::newDatum(int groupId, bool respectLimits, in
     }
     m_bySysIdx[ret->systemIndex] = ret;
 
-    if (m_spriteEngine)
-        m_spriteEngine->startSprite(ret->systemIndex, ret->group);
+    if (m_stateEngine)
+        m_stateEngine->start(ret->systemIndex, ret->group);
 
     m_empty = false;
     return ret;
@@ -1011,8 +1022,8 @@ void QSGParticleSystem::updateCurrentTime( int currentTime )
     foreach (QSGParticleGroupData* gd, m_groupData)//Recycle all groups and see if they're out of live particles
         m_empty = gd->recycle() && m_empty;
 
-    if (m_spriteEngine)
-        m_spriteEngine->updateSprites(m_timeInt);
+    if (m_stateEngine)
+        m_stateEngine->updateSprites(m_timeInt);
 
     foreach (QSGParticleEmitter* emitter, m_emitters)
         if (emitter)
diff --git a/src/declarative/particles/qsgparticlesystem_p.h b/src/declarative/particles/qsgparticlesystem_p.h
index 25a0c87f53ae6d1b47c5ececae24e43cdc302dba..f531e7fcf95a6087a7113654251fdd30ac88a48b 100644
--- a/src/declarative/particles/qsgparticlesystem_p.h
+++ b/src/declarative/particles/qsgparticlesystem_p.h
@@ -65,9 +65,10 @@ class QSGParticleEmitter;
 class QSGParticlePainter;
 class QSGParticleData;
 class QSGParticleSystemAnimation;
-class QSGSpriteEngine;
+class QSGStochasticEngine;
 class QSGSprite;
 class QSGV8ParticleData;
+class QSGParticleGroup;
 
 struct QSGParticleDataHeapNode{
     int time;//in ms
@@ -223,12 +224,10 @@ class QSGParticleSystem : public QSGItem
     Q_PROPERTY(bool running READ isRunning WRITE setRunning NOTIFY runningChanged)
     Q_PROPERTY(bool paused READ isPaused WRITE setPaused NOTIFY pausedChanged)
     Q_PROPERTY(bool empty READ isEmpty NOTIFY emptyChanged)
-    Q_PROPERTY(QDeclarativeListProperty<QSGSprite> particleStates READ particleStates)
 
 public:
     explicit QSGParticleSystem(QSGItem *parent = 0);
     ~QSGParticleSystem();
-    QDeclarativeListProperty<QSGSprite> particleStates();
 
     //TODO: Hook up running and temporal manipulators to the animation
     bool isRunning() const
@@ -286,7 +285,7 @@ public://###but only really for related class usage. Perhaps we should all be fr
     QVector<QSGParticleData*> m_bySysIdx; //Another reference to the data (data owned by group), but by sysIdx
     QHash<QString, int> m_groupIds;
     QHash<int, QSGParticleGroupData*> m_groupData;
-    QSGSpriteEngine* m_spriteEngine;
+    QSGStochasticEngine* m_stateEngine;
 
     int m_timeInt;
     bool m_initialized;
@@ -294,9 +293,11 @@ public://###but only really for related class usage. Perhaps we should all be fr
     void registerParticlePainter(QSGParticlePainter* p);
     void registerParticleEmitter(QSGParticleEmitter* e);
     void registerParticleAffector(QSGParticleAffector* a);
+    void registerParticleGroup(QSGParticleGroup* g);
 
     int m_particle_count;
-    static void stateRedirect(QDeclarativeListProperty<QObject> *prop, QObject *value);//From QSGSprite
+    static void statePropertyRedirect(QDeclarativeListProperty<QObject> *prop, QObject *value);
+    static void stateRedirect(QSGParticleGroup* group, QSGParticleSystem* sys, QObject *value);
     bool isPaused() const
     {
         return m_paused;
@@ -315,11 +316,11 @@ private:
     QList<QPointer<QSGParticleAffector> > m_affectors;
     QList<QPointer<QSGParticlePainter> > m_painters;
     QList<QPointer<QSGParticlePainter> > m_syncList;
+    QList<QSGParticleGroup*> m_groups;
     int m_nextGroupId;
     int m_nextIndex;
     QSet<int> m_reusableIndexes;
     bool m_componentComplete;
-    QList<QSGSprite*> m_states;
 
     QSignalMapper m_painterMapper;
     QSignalMapper m_emitterMapper;
diff --git a/src/declarative/particles/qsgspritegoal.cpp b/src/declarative/particles/qsgspritegoal.cpp
index 183716781378c153dc4cfecc1d5732bb0bc7cda9..ec2be02326d3a366cb271826fe139fb549df2e34 100644
--- a/src/declarative/particles/qsgspritegoal.cpp
+++ b/src/declarative/particles/qsgspritegoal.cpp
@@ -64,16 +64,12 @@ QT_BEGIN_NAMESPACE
     \qmlproperty bool QtQuick.Particles2::SpriteGoal::systemStates
 */
 
-    Q_PROPERTY(QString goalState READ goalState WRITE setGoalState NOTIFY goalStateChanged)
-    Q_PROPERTY(bool jump READ jump WRITE setJump NOTIFY jumpChanged)
-    Q_PROPERTY(bool systemStates READ systemStates WRITE setSystemStates NOTIFY systemStatesChanged)
-
 QSGSpriteGoalAffector::QSGSpriteGoalAffector(QSGItem *parent) :
     QSGParticleAffector(parent), m_goalIdx(-1), m_jump(false), m_systemStates(false), m_lastEngine(0), m_notUsingEngine(false)
 {
 }
 
-void QSGSpriteGoalAffector::updateStateIndex(QSGSpriteEngine* e)
+void QSGSpriteGoalAffector::updateStateIndex(QSGStochasticEngine* e)
 {
     if (m_systemStates){
         m_goalIdx = m_system->m_groupIds[m_goalState];
@@ -104,14 +100,14 @@ void QSGSpriteGoalAffector::setGoalState(QString arg)
 bool QSGSpriteGoalAffector::affectParticle(QSGParticleData *d, qreal dt)
 {
     Q_UNUSED(dt);
-    QSGSpriteEngine *engine = 0;
+    QSGStochasticEngine *engine = 0;
     if (!m_systemStates){
         //TODO: Affect all engines
         foreach (QSGParticlePainter *p, m_system->m_groupData[d->group]->painters)
             if (qobject_cast<QSGImageParticle*>(p))
                 engine = qobject_cast<QSGImageParticle*>(p)->spriteEngine();
     }else{
-        engine = m_system->m_spriteEngine;
+        engine = m_system->m_stateEngine;
         if (!engine)
             m_notUsingEngine = true;
     }
@@ -126,7 +122,7 @@ bool QSGSpriteGoalAffector::affectParticle(QSGParticleData *d, qreal dt)
     if (m_notUsingEngine){//systemStates && no stochastic states defined. So cut out the engine
         //TODO: It's possible to move to a group that is intermediate and not used by painters or emitters - but right now that will redirect to the default group
         m_system->moveGroups(d, m_goalIdx);
-    }else if (engine->spriteState(index) != m_goalIdx){
+    }else if (engine->curState(index) != m_goalIdx){
         engine->setGoal(m_goalIdx, index, m_jump);
         emit affected(QPointF(d->curX(), d->curY()));//###Expensive if unconnected? Move to Affector?
         return true; //Doesn't affect particle data, but necessary for onceOff
diff --git a/src/declarative/particles/qsgspritegoal_p.h b/src/declarative/particles/qsgspritegoal_p.h
index 7c799b13b102729b87f4f6bbf05f622ec4edfe25..043970b90b6ad165c6128ed0015309596aa904b8 100644
--- a/src/declarative/particles/qsgspritegoal_p.h
+++ b/src/declarative/particles/qsgspritegoal_p.h
@@ -49,7 +49,7 @@ QT_BEGIN_NAMESPACE
 
 QT_MODULE(Declarative)
 
-class QSGSpriteEngine;
+class QSGStochasticEngine;
 
 class QSGSpriteGoalAffector : public QSGParticleAffector
 {
@@ -106,10 +106,10 @@ void setSystemStates(bool arg)
 }
 
 private:
-    void updateStateIndex(QSGSpriteEngine* e);
+    void updateStateIndex(QSGStochasticEngine* e);
     QString m_goalState;
     int m_goalIdx;
-    QSGSpriteEngine* m_lastEngine;
+    QSGStochasticEngine* m_lastEngine;
     bool m_jump;
     bool m_systemStates;
 
diff --git a/src/declarative/particles/qsgtrailemitter.cpp b/src/declarative/particles/qsgtrailemitter.cpp
index 427b587cafc16a8415cbdd86c9d6f0ca52c65afd..5a19ac508b65ebd16b3b320c9151ef26564e425f 100644
--- a/src/declarative/particles/qsgtrailemitter.cpp
+++ b/src/declarative/particles/qsgtrailemitter.cpp
@@ -166,7 +166,7 @@ void QSGTrailEmitter::emitWindow(int timeStamp)
     qreal sizeAtEnd = m_particleEndSize >= 0 ? m_particleEndSize : m_particleSize;
 
     int gId = m_system->m_groupIds[m_follow];
-    int gId2 = m_system->m_groupIds[m_particle];
+    int gId2 = m_system->m_groupIds[m_group];
     foreach (QSGParticleData *d, m_system->m_groupData[gId]->data){
         if (!d || !d->stillAlive()){
             m_lastEmission[d->index] = time; //Should only start emitting when it returns to life
diff --git a/src/declarative/particles/qsgtrailemitter_p.h b/src/declarative/particles/qsgtrailemitter_p.h
index 5ab6f2427096b2261a1fbc287a052c03a65a8fc7..009ccd508c06075f0818d9a64211edf9829fe141 100644
--- a/src/declarative/particles/qsgtrailemitter_p.h
+++ b/src/declarative/particles/qsgtrailemitter_p.h
@@ -94,7 +94,7 @@ public:
     }
 
 signals:
-    void emitFollowParticle(QDeclarativeV8Handle particle, QDeclarativeV8Handle followed);
+    void emitFollowParticle(QDeclarativeV8Handle group, QDeclarativeV8Handle followed);
 
     void particlesPerParticlePerSecondChanged(int arg);