diff --git a/osu.Desktop.VisualTests/Tests/TestCaseHitObjects.cs b/osu.Desktop.VisualTests/Tests/TestCaseHitObjects.cs
index cf66829ceb..54d8bbd2e7 100644
--- a/osu.Desktop.VisualTests/Tests/TestCaseHitObjects.cs
+++ b/osu.Desktop.VisualTests/Tests/TestCaseHitObjects.cs
@@ -8,6 +8,7 @@ using osu.Framework.Timing;
 using osu.Game.Beatmaps.Objects;
 using osu.Game.Beatmaps.Objects.Osu;
 using osu.Game.Beatmaps.Objects.Osu.Drawable;
+using OpenTK;
 
 namespace osu.Desktop.VisualTests.Tests
 {
@@ -33,12 +34,14 @@ namespace osu.Desktop.VisualTests.Tests
 
             ourClock.ProcessFrame();
 
-            for (int i = 0; i < 20; i++)
+            const int count = 10;
+
+            for (int i = 0; i < count; i++)
             {
                 var h = new Circle
                 {
                     StartTime = ourClock.CurrentTime + 1000 + i * 80,
-                    Position = new OpenTK.Vector2(i * 14),
+                    Position = new Vector2((i - count / 2) * 14),
                 };
 
                 Add(new DrawableCircle(h)
diff --git a/osu.Desktop.VisualTests/Tests/TestCasePlayer.cs b/osu.Desktop.VisualTests/Tests/TestCasePlayer.cs
index 1d292442e6..bb7b86db43 100644
--- a/osu.Desktop.VisualTests/Tests/TestCasePlayer.cs
+++ b/osu.Desktop.VisualTests/Tests/TestCasePlayer.cs
@@ -6,6 +6,7 @@ using osu.Framework.GameModes.Testing;
 using osu.Framework.MathUtils;
 using osu.Framework.Timing;
 using osu.Game.Beatmaps;
+using osu.Game.Beatmaps.Formats;
 using osu.Game.Beatmaps.Objects;
 using osu.Game.Beatmaps.Objects.Osu;
 using osu.Game.GameModes.Play;
@@ -38,18 +39,25 @@ namespace osu.Desktop.VisualTests.Tests
                 objects.Add(new Circle()
                 {
                     StartTime = time,
-                    Position = new Vector2(RNG.Next(100, 400), RNG.Next(100, 200))
+                    Position = new Vector2(RNG.Next(0, 512), RNG.Next(0, 384)),
+                    NewCombo = i % 4 == 0
                 });
 
                 time += 500;
             }
 
+            var decoder = new ConstructableBeatmapDecoder();
+
+            Beatmap b = new Beatmap
+            {
+                HitObjects = objects
+            };
+
+            decoder.Process(b);
+
             Add(new Player
             {
-                Beatmap = new WorkingBeatmap(new Beatmap
-                {
-                    HitObjects = objects
-                })
+                Beatmap = new WorkingBeatmap(b)
             });
         }
 
diff --git a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs
index eb8e9260df..bbd441d25a 100644
--- a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs
+++ b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs
@@ -8,6 +8,7 @@ using osu.Framework.Desktop.Platform;
 using osu.Framework.Platform;
 using osu.Game.Database;
 using osu.Game.IPC;
+using osu.Game.GameModes.Play;
 
 namespace osu.Game.Tests.Beatmaps.IO
 {
@@ -45,7 +46,7 @@ namespace osu.Game.Tests.Beatmaps.IO
 
             var importer = new BeatmapImporter(client);
             if (!importer.Import(osz_path).Wait(1000))
-                    Assert.Fail(@"IPC took too long to send");
+                Assert.Fail(@"IPC took too long to send");
 
             ensureLoaded(osu, 10000);
         }
@@ -78,7 +79,7 @@ namespace osu.Game.Tests.Beatmaps.IO
                 @"BeatmapSet did not import to the database");
 
             //ensure we were stored to beatmap database backing...
-            
+
             Assert.IsTrue(resultSets.Count() == 1);
 
             IEnumerable<BeatmapInfo> resultBeatmaps = null;
@@ -103,7 +104,7 @@ namespace osu.Game.Tests.Beatmaps.IO
 
             Assert.IsTrue(set.Beatmaps.Count > 0);
 
-            var beatmap = osu.Beatmaps.GetBeatmap(set.Beatmaps[0]);
+            var beatmap = osu.Beatmaps.GetBeatmap(set.Beatmaps.First(b => b.Mode == PlayMode.Osu));
 
             Assert.IsTrue(beatmap.HitObjects.Count > 0);
         }
diff --git a/osu.Game/Beatmaps/Formats/BeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/BeatmapDecoder.cs
index f376d831cf..e3d89ac6fc 100644
--- a/osu.Game/Beatmaps/Formats/BeatmapDecoder.cs
+++ b/osu.Game/Beatmaps/Formats/BeatmapDecoder.cs
@@ -1,13 +1,15 @@
 using System;
 using System.Collections.Generic;
 using System.IO;
+using osu.Game.Beatmaps.Objects;
+using OpenTK.Graphics;
 
 namespace osu.Game.Beatmaps.Formats
 {
     public abstract class BeatmapDecoder
     {
         private static Dictionary<string, Type> decoders { get; } = new Dictionary<string, Type>();
-    
+
         public static BeatmapDecoder GetDecoder(TextReader stream)
         {
             var line = stream.ReadLine().Trim();
@@ -20,7 +22,39 @@ namespace osu.Game.Beatmaps.Formats
         {
             decoders[magic] = typeof(T);
         }
-    
-        public abstract Beatmap Decode(TextReader stream);
+
+        public virtual Beatmap Decode(TextReader stream)
+        {
+            Beatmap b = ParseFile(stream);
+            Process(b);
+            return b;
+        }
+
+        public virtual Beatmap Process(Beatmap beatmap)
+        {
+            ApplyColours(beatmap);
+
+            return beatmap;
+        }
+
+        protected abstract Beatmap ParseFile(TextReader stream);
+
+        public virtual void ApplyColours(Beatmap b)
+        {
+            List<Color4> colours = b.ComboColors ?? new List<Color4>() {
+                new Color4(17, 136, 170, 255),
+                new Color4(102,136,0, 255),
+                new Color4(204,102,0, 255),
+                new Color4(121,9,13, 255),
+            };
+
+            int i = -1;
+
+            foreach (HitObject h in b.HitObjects)
+            {
+                if (h.NewCombo) i = (i + 1) % colours.Count;
+                h.Colour = colours[i];
+            }
+        }
     }
 }
diff --git a/osu.Game/Beatmaps/Formats/ConstructableBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/ConstructableBeatmapDecoder.cs
new file mode 100644
index 0000000000..e1dae7b193
--- /dev/null
+++ b/osu.Game/Beatmaps/Formats/ConstructableBeatmapDecoder.cs
@@ -0,0 +1,20 @@
+//Copyright (c) 2007-2016 ppy Pty Ltd <contact@ppy.sh>.
+//Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace osu.Game.Beatmaps.Formats
+{
+    public class ConstructableBeatmapDecoder : BeatmapDecoder
+    {
+        protected override Beatmap ParseFile(TextReader stream)
+        {
+            throw new NotImplementedException();
+        }
+    }
+}
diff --git a/osu.Game/Beatmaps/Formats/OsuLegacyDecoder.cs b/osu.Game/Beatmaps/Formats/OsuLegacyDecoder.cs
index 7d3b918961..e988498093 100644
--- a/osu.Game/Beatmaps/Formats/OsuLegacyDecoder.cs
+++ b/osu.Game/Beatmaps/Formats/OsuLegacyDecoder.cs
@@ -207,7 +207,7 @@ namespace osu.Game.Beatmaps.Formats
             });
         }
 
-        public override Beatmap Decode(TextReader stream)
+        protected override Beatmap ParseFile(TextReader stream)
         {
             var beatmap = new Beatmap
             {
@@ -270,7 +270,9 @@ namespace osu.Game.Beatmaps.Formats
                         handleColours(beatmap, key, val);
                         break;
                     case Section.HitObjects:
-                        beatmap.HitObjects.Add(HitObject.Parse(beatmap.BeatmapInfo.Mode, val));
+                        var h = HitObject.Parse(beatmap.BeatmapInfo.Mode, val);
+                        if (h != null)
+                            beatmap.HitObjects.Add(h);
                         break;
                 }
             }
diff --git a/osu.Game/Beatmaps/Objects/DrawableHitObject.cs b/osu.Game/Beatmaps/Objects/DrawableHitObject.cs
index d6b459b0d1..126f66be19 100644
--- a/osu.Game/Beatmaps/Objects/DrawableHitObject.cs
+++ b/osu.Game/Beatmaps/Objects/DrawableHitObject.cs
@@ -14,14 +14,18 @@ namespace osu.Game.Beatmaps.Objects
 {
     public abstract class DrawableHitObject : Container, IStateful<ArmedState>
     {
+        //todo: move to a more central implementation. this logic should not be at a drawable level.
         public Action<DrawableHitObject> OnHit;
         public Action<DrawableHitObject> OnMiss;
 
+        public Func<DrawableHitObject, bool> AllowHit;
+
         public HitObject HitObject;
 
         public DrawableHitObject(HitObject hitObject)
         {
             HitObject = hitObject;
+            Depth = -(float)hitObject.StartTime;
         }
 
         private ArmedState state;
@@ -31,12 +35,29 @@ namespace osu.Game.Beatmaps.Objects
 
             set
             {
+                if (state == value) return;
                 state = value;
 
                 UpdateState(state);
             }
         }
 
+        protected double? HitTime;
+
+        protected virtual bool Hit()
+        {
+            if (State != ArmedState.Disarmed)
+                return false;
+
+            if (AllowHit?.Invoke(this) == false)
+                return false;
+
+            HitTime = Time;
+
+            State = ArmedState.Armed;
+            return true;
+        }
+
         protected override void LoadComplete()
         {
             base.LoadComplete();
diff --git a/osu.Game/Beatmaps/Objects/HitObject.cs b/osu.Game/Beatmaps/Objects/HitObject.cs
index 44cab54fa3..ef6659224f 100644
--- a/osu.Game/Beatmaps/Objects/HitObject.cs
+++ b/osu.Game/Beatmaps/Objects/HitObject.cs
@@ -4,6 +4,7 @@
 using osu.Game.Beatmaps.Objects.Osu;
 using osu.Game.Beatmaps.Samples;
 using osu.Game.GameModes.Play;
+using OpenTK.Graphics;
 
 namespace osu.Game.Beatmaps.Objects
 {
@@ -15,6 +16,10 @@ namespace osu.Game.Beatmaps.Objects
         public double StartTime;
         public virtual double EndTime => StartTime;
 
+        public bool NewCombo { get; set; }
+
+        public Color4 Colour = new Color4(17, 136, 170, 255);
+
         public double Duration => EndTime - StartTime;
 
         public HitSampleInfo Sample;
diff --git a/osu.Game/Beatmaps/Objects/Osu/Circle.cs b/osu.Game/Beatmaps/Objects/Osu/Circle.cs
index e276132ebc..9b7f92f29e 100644
--- a/osu.Game/Beatmaps/Objects/Osu/Circle.cs
+++ b/osu.Game/Beatmaps/Objects/Osu/Circle.cs
@@ -7,6 +7,5 @@ namespace osu.Game.Beatmaps.Objects.Osu
 {
     public class Circle : OsuBaseHit
     {
-        public Color4 Colour = new Color4(17, 136, 170, 255);
     }
 }
diff --git a/osu.Game/Beatmaps/Objects/Osu/Drawable/DrawableCircle.cs b/osu.Game/Beatmaps/Objects/Osu/Drawable/DrawableCircle.cs
index 2b52657f1e..26d7375852 100644
--- a/osu.Game/Beatmaps/Objects/Osu/Drawable/DrawableCircle.cs
+++ b/osu.Game/Beatmaps/Objects/Osu/Drawable/DrawableCircle.cs
@@ -8,22 +8,21 @@ using osu.Framework.Graphics.Containers;
 using osu.Framework.Graphics.Sprites;
 using osu.Framework.Graphics.Textures;
 using osu.Framework.Graphics.Transformations;
-using OpenTK;
-using OpenTK.Graphics;
 using osu.Framework.Input;
 using osu.Framework.MathUtils;
+using OpenTK;
 
 namespace osu.Game.Beatmaps.Objects.Osu.Drawable
 {
     public class DrawableCircle : DrawableHitObject
     {
         private Sprite approachCircle;
-        private CirclePart circle;
-        private RingPart ring;
-        private FlashPart flash;
-        private ExplodePart explode;
-        private NumberPart number;
-        private GlowPart glow;
+        private CircleLayer circle;
+        private RingLayer ring;
+        private FlashLayer flash;
+        private ExplodeLayer explode;
+        private NumberLayer number;
+        private GlowLayer glow;
         private OsuBaseHit h;
 
         public DrawableCircle(Circle h) : base(h)
@@ -31,28 +30,27 @@ namespace osu.Game.Beatmaps.Objects.Osu.Drawable
             this.h = h;
 
             Origin = Anchor.Centre;
-            Alpha = 0;
-            Position = h.Position;
-            Scale = new Vector2(0.4f);
+            RelativePositionAxes = Axes.Both;
+            Position = new Vector2(h.Position.X / 512, h.Position.Y / 384);
 
             Children = new Framework.Graphics.Drawable[]
             {
-                glow = new GlowPart()
-                {
-                    Colour = h.Colour,
-                },
-                circle = new CirclePart()
-                {
-                    Colour = h.Colour,
-                    Hit = delegate { State = ArmedState.Armed; }
-                },
-                number = new NumberPart(),
-                ring = new RingPart(),
-                flash = new FlashPart(),
-                explode = new ExplodePart()
+                glow = new GlowLayer
                 {
                     Colour = h.Colour
                 },
+                circle = new CircleLayer
+                {
+                    Colour = h.Colour,
+                    Hit = Hit,
+                },
+                number = new NumberLayer(),
+                ring = new RingLayer(),
+                flash = new FlashLayer(),
+                explode = new ExplodeLayer
+                {
+                    Colour = h.Colour,
+                },
                 approachCircle = new Sprite
                 {
                     Anchor = Anchor.Centre,
@@ -61,7 +59,8 @@ namespace osu.Game.Beatmaps.Objects.Osu.Drawable
                 }
             };
 
-            Size = new Vector2(100);
+            //may not be so correct
+            Size = circle.DrawSize;
         }
 
         protected override void Load(BaseGame game)
@@ -71,63 +70,82 @@ namespace osu.Game.Beatmaps.Objects.Osu.Drawable
             approachCircle.Texture = game.Textures.Get(@"Play/osu/approachcircle@2x");
         }
 
+        protected override void LoadComplete()
+        {
+            base.LoadComplete();
+
+            //force application of the state that was set before we loaded.
+            UpdateState(State);
+        }
+
         protected override void UpdateState(ArmedState state)
         {
             if (!IsLoaded) return;
 
-            Flush(); //move to DrawableHitObject
+            Flush(true); //move to DrawableHitObject
 
-            Transforms.Add(new TransformAlpha { StartTime = h.StartTime - 1000, EndTime = h.StartTime - 800, StartValue = 0, EndValue = 1 });
+            double t = HitTime ?? h.StartTime;
 
-            approachCircle.Transforms.Add(new TransformScale { StartTime = h.StartTime - 1000, EndTime = h.StartTime, StartValue = new Vector2(2f), EndValue = new Vector2(0.6f) });
-            approachCircle.Transforms.Add(new TransformAlpha { StartTime = h.StartTime, EndTime = h.StartTime, StartValue = 1, EndValue = 0 });
+            //sane defaults
+            ring.Alpha = circle.Alpha = number.Alpha = approachCircle.Alpha = glow.Alpha = 1;
+            explode.Alpha = 0;
+            Scale = Vector2.One;
 
-            glow.Transforms.Add(new TransformAlpha { StartTime = h.StartTime, EndTime = h.StartTime + 400, StartValue = glow.Alpha, EndValue = 0 });
+            //always-present transforms
+            Transforms.Add(new TransformAlpha { StartTime = t - 1000, EndTime = t - 800, StartValue = 0, EndValue = 1 });
+            approachCircle.Transforms.Add(new TransformScale { StartTime = t - 1000, EndTime = t, StartValue = new Vector2(2f), EndValue = new Vector2(0.6f) });
+
+            //set transform delay to t==hitTime
+            Delay(t - Time, true);
+
+            approachCircle.FadeOut();
+            glow.FadeOut(400);
 
             switch (state)
             {
                 case ArmedState.Disarmed:
-                    Transforms.Add(new TransformAlpha { StartTime = h.StartTime + h.Duration + 200, EndTime = h.StartTime + h.Duration + 400, StartValue = 1, EndValue = 0 });
+                    Delay(h.Duration + 200);
+                    FadeOut(200);
                     break;
                 case ArmedState.Armed:
-                    const float flashIn = 30;
-                    const float fadeOut = 800;
+                    const double flash_in = 30;
 
-                    //Transforms.Add(new TransformScale() { StartTime = h.StartTime, EndTime = h.StartTime + 400, StartValue = Scale, EndValue = Scale * 1.1f });
+                    flash.FadeTo(0.8f, flash_in);
+                    flash.Delay(flash_in);
+                    flash.FadeOut(100);
 
-                    ring.Transforms.Add(new TransformAlpha { StartTime = h.StartTime + flashIn, EndTime = h.StartTime + flashIn, StartValue = 0, EndValue = 0 });
-                    circle.Transforms.Add(new TransformAlpha { StartTime = h.StartTime + flashIn, EndTime = h.StartTime + flashIn, StartValue = 0, EndValue = 0 });
-                    number.Transforms.Add(new TransformAlpha { StartTime = h.StartTime + flashIn, EndTime = h.StartTime + flashIn, StartValue = 0, EndValue = 0 });
+                    explode.FadeIn(flash_in);
 
-                    flash.Transforms.Add(new TransformAlpha { StartTime = h.StartTime, EndTime = h.StartTime + flashIn, StartValue = 0, EndValue = 0.8f });
-                    flash.Transforms.Add(new TransformAlpha { StartTime = h.StartTime + flashIn, EndTime = h.StartTime + flashIn + 100, StartValue = 0.8f, EndValue = 0 });
+                    Delay(flash_in, true);
 
-                    explode.Transforms.Add(new TransformAlpha { StartTime = h.StartTime, EndTime = h.StartTime + flashIn, StartValue = 0, EndValue = 1 });
+                    //after the flash, we can hide some elements that were behind it
+                    ring.FadeOut();
+                    circle.FadeOut();
+                    number.FadeOut();
 
-                    Transforms.Add(new TransformAlpha { StartTime = h.StartTime + flashIn, EndTime = h.StartTime + flashIn + fadeOut, StartValue = 1, EndValue = 0 });
-
-                    Transforms.Add(new TransformScale { StartTime = h.StartTime + h.Duration, EndTime = h.StartTime + h.Duration + 400, StartValue = Scale, EndValue = Scale * 1.5f, Easing = EasingTypes.OutQuad });
+                    FadeOut(800);
+                    ScaleTo(Scale * 1.5f, 400, EasingTypes.OutQuad);
                     break;
             }
         }
 
-        class NumberPart : Container
+        private class NumberLayer : Container
         {
             private Sprite number;
 
-            public NumberPart()
+            public NumberLayer()
             {
                 Anchor = Anchor.Centre;
                 Origin = Anchor.Centre;
 
                 Children = new[]
                 {
-                        number = new Sprite
-                        {
-                            Anchor = Anchor.Centre,
-                            Origin = Anchor.Centre,
-                            Alpha = 1,
-                        }
+                    number = new Sprite
+                    {
+                        Anchor = Anchor.Centre,
+                        Origin = Anchor.Centre,
+                        Alpha = 1
+                    }
                 };
             }
 
@@ -138,39 +156,39 @@ namespace osu.Game.Beatmaps.Objects.Osu.Drawable
             }
         }
 
-        class GlowPart : Container
+        private class GlowLayer : Container
         {
-            private Sprite layer3;
+            private Sprite layer;
 
-            public GlowPart()
+            public GlowLayer()
             {
                 Anchor = Anchor.Centre;
                 Origin = Anchor.Centre;
 
                 Children = new[]
                 {
-                        layer3 = new Sprite
-                        {
-                            Anchor = Anchor.Centre,
-                            Origin = Anchor.Centre,
-                            BlendingMode = BlendingMode.Additive,
-                            Alpha = 0.5f,
-                        }
+                    layer = new Sprite
+                    {
+                        Anchor = Anchor.Centre,
+                        Origin = Anchor.Centre,
+                        BlendingMode = BlendingMode.Additive,
+                        Alpha = 0.5f
+                    }
                 };
             }
 
             protected override void Load(BaseGame game)
             {
                 base.Load(game);
-                layer3.Texture = game.Textures.Get(@"Play/osu/ring-glow@2x");
+                layer.Texture = game.Textures.Get(@"Play/osu/ring-glow@2x");
             }
         }
 
-        class RingPart : Container
+        private class RingLayer : Container
         {
             private Sprite ring;
 
-            public RingPart()
+            public RingLayer()
             {
                 Anchor = Anchor.Centre;
                 Origin = Anchor.Centre;
@@ -180,8 +198,8 @@ namespace osu.Game.Beatmaps.Objects.Osu.Drawable
                     ring = new Sprite
                     {
                         Anchor = Anchor.Centre,
-                        Origin = Anchor.Centre,
-                    },
+                        Origin = Anchor.Centre
+                    }
                 };
             }
 
@@ -192,11 +210,12 @@ namespace osu.Game.Beatmaps.Objects.Osu.Drawable
             }
         }
 
-        class FlashPart : Container
+        private class FlashLayer : Container
         {
-            public FlashPart()
+            public FlashLayer()
             {
                 Size = new Vector2(144);
+
                 Masking = true;
                 CornerRadius = Size.X / 2;
 
@@ -210,15 +229,15 @@ namespace osu.Game.Beatmaps.Objects.Osu.Drawable
                 {
                     new Box
                     {
-                        RelativeSizeAxes = Axes.Both,
+                        RelativeSizeAxes = Axes.Both
                     }
                 };
             }
         }
 
-        class ExplodePart : Container
+        private class ExplodeLayer : Container
         {
-            public ExplodePart()
+            public ExplodeLayer()
             {
                 Size = new Vector2(144);
 
@@ -232,53 +251,21 @@ namespace osu.Game.Beatmaps.Objects.Osu.Drawable
                 {
                     new Triangles
                     {
-                        RelativeSizeAxes = Axes.Both,
-                    },
-                };
-            }
-
-            class Triangles : Container
-            {
-                private Texture tex;
-
-                protected override void Load(BaseGame game)
-                {
-                    base.Load(game);
-
-                    tex = game.Textures.Get(@"Play/osu/triangle@2x");
-
-                    for (int i = 0; i < 10; i++)
-                    {
-                        Add(new Sprite
-                        {
-                            Texture = tex,
-                            Origin = Anchor.Centre,
-                            Position = new Vector2(RNG.NextSingle() * DrawSize.X, RNG.NextSingle() * DrawSize.Y),
-                            Scale = new Vector2(RNG.NextSingle() * 0.4f + 0.2f),
-                            Alpha = RNG.NextSingle() * 0.3f,
-                        });
+                        RelativeSizeAxes = Axes.Both
                     }
-                }
-
-                protected override void Update()
-                {
-                    base.Update();
-
-                    foreach (Framework.Graphics.Drawable d in Children)
-                        d.Position -= new Vector2(0, (float)(d.Scale.X * (Clock.ElapsedFrameTime / 20)));
-                }
+                };
             }
         }
 
-        class CirclePart : Container
+        private class CircleLayer : Container
         {
 
             private Sprite disc;
             private Triangles triangles;
 
-            public Action Hit;
+            public Func<bool> Hit;
 
-            public CirclePart()
+            public CircleLayer()
             {
                 Size = new Vector2(144);
                 Masking = true;
@@ -292,13 +279,13 @@ namespace osu.Game.Beatmaps.Objects.Osu.Drawable
                     disc = new Sprite
                     {
                         Anchor = Anchor.Centre,
-                        Origin = Anchor.Centre,
+                        Origin = Anchor.Centre
                     },
                     triangles = new Triangles
                     {
                         BlendingMode = BlendingMode.Additive,
-                        RelativeSizeAxes = Axes.Both,
-                    },
+                        RelativeSizeAxes = Axes.Both
+                    }
                 };
             }
 
@@ -313,37 +300,38 @@ namespace osu.Game.Beatmaps.Objects.Osu.Drawable
                 Hit?.Invoke();
                 return true;
             }
+        }
 
-            class Triangles : Container
+        private class Triangles : Container
+        {
+            private Texture tex;
+
+            protected override void Load(BaseGame game)
             {
-                private Texture tex;
+                base.Load(game);
 
-                protected override void Load(BaseGame game)
+                tex = game.Textures.Get(@"Play/osu/triangle@2x");
+
+                for (int i = 0; i < 10; i++)
                 {
-                    base.Load(game);
-
-                    tex = game.Textures.Get(@"Play/osu/triangle@2x");
-
-                    for (int i = 0; i < 10; i++)
+                    Add(new Sprite
                     {
-                        Add(new Sprite
-                        {
-                            Texture = tex,
-                            Origin = Anchor.Centre,
-                            Position = new Vector2(RNG.NextSingle() * DrawSize.X, RNG.NextSingle() * DrawSize.Y),
-                            Scale = new Vector2(RNG.NextSingle() * 0.4f + 0.2f),
-                            Alpha = RNG.NextSingle() * 0.3f,
-                        });
-                    }
+                        Texture = tex,
+                        Origin = Anchor.Centre,
+                        RelativePositionAxes = Axes.Both,
+                        Position = new Vector2(RNG.NextSingle(), RNG.NextSingle()),
+                        Scale = new Vector2(RNG.NextSingle() * 0.4f + 0.2f),
+                        Alpha = RNG.NextSingle() * 0.3f
+                    });
                 }
+            }
 
-                protected override void Update()
-                {
-                    base.Update();
+            protected override void Update()
+            {
+                base.Update();
 
-                    foreach (Framework.Graphics.Drawable d in Children)
-                        d.Position -= new Vector2(0, (float)(d.Scale.X * (Clock.ElapsedFrameTime / 20)));
-                }
+                foreach (Framework.Graphics.Drawable d in Children)
+                    d.Position -= new Vector2(0, (float)(d.Scale.X * (Clock.ElapsedFrameTime / 2880)));
             }
         }
     }
diff --git a/osu.Game/Beatmaps/Objects/Osu/OsuBaseHit.cs b/osu.Game/Beatmaps/Objects/Osu/OsuBaseHit.cs
index 2f94c70a0e..e4bb272404 100644
--- a/osu.Game/Beatmaps/Objects/Osu/OsuBaseHit.cs
+++ b/osu.Game/Beatmaps/Objects/Osu/OsuBaseHit.cs
@@ -10,7 +10,6 @@ namespace osu.Game.Beatmaps.Objects.Osu
     public abstract class OsuBaseHit : HitObject
     {
         public Vector2 Position { get; set; }
-        public bool NewCombo { get; set; }
 
         [Flags]
         private enum HitObjectType
diff --git a/osu.Game/GameModes/Play/HitRenderer.cs b/osu.Game/GameModes/Play/HitRenderer.cs
index d77aaab50f..e693da527d 100644
--- a/osu.Game/GameModes/Play/HitRenderer.cs
+++ b/osu.Game/GameModes/Play/HitRenderer.cs
@@ -7,6 +7,7 @@ using osu.Framework.Graphics.Containers;
 using osu.Game.Beatmaps.Objects;
 using osu.Framework;
 using System;
+using System.Linq;
 
 namespace osu.Game.GameModes.Play
 {
@@ -14,6 +15,10 @@ namespace osu.Game.GameModes.Play
     {
         public Action<HitObject> OnHit;
         public Action<HitObject> OnMiss;
+
+        protected Playfield Playfield;
+
+        public IEnumerable<DrawableHitObject> DrawableObjects => Playfield.Children.Cast<DrawableHitObject>();
     }
 
     public abstract class HitRenderer<T> : HitRenderer
@@ -31,8 +36,6 @@ namespace osu.Game.GameModes.Play
             }
         }
 
-        private Playfield playfield;
-
         protected abstract Playfield CreatePlayfield();
 
         protected abstract HitObjectConverter<T> Converter { get; }
@@ -47,7 +50,7 @@ namespace osu.Game.GameModes.Play
 
             Children = new Drawable[]
             {
-                playfield = CreatePlayfield()
+                Playfield = CreatePlayfield()
             };
 
             loadObjects();
@@ -65,7 +68,7 @@ namespace osu.Game.GameModes.Play
                 drawableObject.OnHit = onHit;
                 drawableObject.OnMiss = onMiss;
 
-                playfield.Add(drawableObject);
+                Playfield.Add(drawableObject);
             }
         }
 
diff --git a/osu.Game/GameModes/Play/Osu/OsuPlayfield.cs b/osu.Game/GameModes/Play/Osu/OsuPlayfield.cs
index 111dd608c1..a6c4112321 100644
--- a/osu.Game/GameModes/Play/Osu/OsuPlayfield.cs
+++ b/osu.Game/GameModes/Play/Osu/OsuPlayfield.cs
@@ -12,26 +12,48 @@ namespace osu.Game.GameModes.Play.Osu
 {
     public class OsuPlayfield : Playfield
     {
-        public OsuPlayfield()
+        protected override Container Content => hitObjectContainer;
+
+        private Container hitObjectContainer;
+
+        public override Vector2 Size
         {
-            Size = new Vector2(512, 384);
-            Scale = new Vector2(1.6f);
-            Anchor = Anchor.Centre;
-            Origin = Anchor.Centre;
+            get
+            {
+                var parentSize = Parent.DrawSize;
+                var aspectSize = parentSize.X * 0.75f < parentSize.Y ? new Vector2(parentSize.X, parentSize.X * 0.75f) : new Vector2(parentSize.Y * 4f / 3f, parentSize.Y);
+
+                return new Vector2(aspectSize.X / parentSize.X, aspectSize.Y / parentSize.Y) * base.Size;
+            }
         }
 
-        protected override void Load(BaseGame game)
+        public OsuPlayfield()
         {
-            base.Load(game);
+            Anchor = Anchor.Centre;
+            Origin = Anchor.Centre;
+            RelativeSizeAxes = Axes.Both;
+            Size = new Vector2(0.75f);
 
-            Add(new Box()
+            AddInternal(new Box
             {
+                RelativeSizeAxes = Axes.Both,
                 Anchor = Anchor.Centre,
                 Origin = Anchor.Centre,
-                RelativeSizeAxes = Axes.Both,
                 Colour = Color4.Black,
-                Alpha = 0.5f
+                Alpha = 0.5f,
             });
+
+            AddInternal(hitObjectContainer = new HitObjectContainer
+            {
+                RelativeSizeAxes = Axes.Both,
+                Anchor = Anchor.Centre,
+                Origin = Anchor.Centre,
+            });
+        }
+
+        class HitObjectContainer : Container
+        {
+            protected override Vector2 ChildScale => new Vector2(0.625f);
         }
     }
 }
\ No newline at end of file
diff --git a/osu.Game/GameModes/Play/Player.cs b/osu.Game/GameModes/Play/Player.cs
index 860e077588..d48e252fa5 100644
--- a/osu.Game/GameModes/Play/Player.cs
+++ b/osu.Game/GameModes/Play/Player.cs
@@ -2,7 +2,6 @@
 //Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
 
 using osu.Framework.Graphics;
-using osu.Game.Beatmaps;
 using osu.Game.Beatmaps.Objects;
 using osu.Game.GameModes.Backgrounds;
 using osu.Game.GameModes.Play.Catch;
@@ -12,13 +11,15 @@ using osu.Game.GameModes.Play.Taiko;
 using osu.Framework;
 using osu.Game.Database;
 using osu.Framework.Timing;
-using osu.Framework.GameModes;
 using osu.Framework.Audio.Track;
+using osu.Framework.Extensions.IEnumerableExtensions;
 
 namespace osu.Game.GameModes.Play
 {
     public class Player : OsuGameMode
     {
+        const bool autoplay = false;
+
         protected override BackgroundMode CreateBackground() => new BackgroundModeCustom(@"Backgrounds/bg4");
 
         public BeatmapInfo BeatmapInfo;
@@ -124,6 +125,9 @@ namespace osu.Game.GameModes.Play
             hitRenderer.OnHit += delegate (HitObject h) { scoreOverlay.OnHit(h); };
             hitRenderer.OnMiss += delegate (HitObject h) { scoreOverlay.OnMiss(h); };
 
+            if (autoplay)
+                hitRenderer.Schedule(() => hitRenderer.DrawableObjects.ForEach(h => h.State = ArmedState.Armed));
+
             Children = new Drawable[]
             {
                 hitRenderer,
diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj
index b2a8cee6a5..92679b7d58 100644
--- a/osu.Game/osu.Game.csproj
+++ b/osu.Game/osu.Game.csproj
@@ -64,6 +64,7 @@
   </ItemGroup>
   <ItemGroup>
     <Compile Include="Beatmaps\Beatmap.cs" />
+    <Compile Include="Beatmaps\Formats\ConstructableBeatmapDecoder.cs" />
     <Compile Include="Beatmaps\WorkingBeatmap.cs" />
     <Compile Include="Beatmaps\Drawable\BeatmapSetHeader.cs" />
     <Compile Include="Beatmaps\Drawable\DifficultyIcon.cs" />