diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneFailingLayer.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneFailingLayer.cs
index fb4c9d713a..c335f7c99e 100644
--- a/osu.Game.Tests/Visual/Gameplay/TestSceneFailingLayer.cs
+++ b/osu.Game.Tests/Visual/Gameplay/TestSceneFailingLayer.cs
@@ -1,11 +1,13 @@
 // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
 // See the LICENCE file in the repository root for full licence text.
 
+using System.Linq;
 using NUnit.Framework;
 using osu.Framework.Allocation;
 using osu.Framework.Bindables;
 using osu.Framework.Graphics;
 using osu.Framework.Graphics.Containers;
+using osu.Framework.Testing;
 using osu.Game.Configuration;
 using osu.Game.Rulesets.Scoring;
 using osu.Game.Screens.Play.HUD;
@@ -50,9 +52,9 @@ namespace osu.Game.Tests.Visual.Gameplay
             });
 
             AddStep("set health to 0.10", () => layer.Current.Value = 0.1);
-            AddUntilStep("layer fade is visible", () => layer.Child.Alpha > 0.1f);
+            AddUntilStep("layer fade is visible", () => layer.ChildrenOfType<Container>().First().Alpha > 0.1f);
             AddStep("set health to 1", () => layer.Current.Value = 1f);
-            AddUntilStep("layer fade is invisible", () => !layer.Child.IsPresent);
+            AddUntilStep("layer fade is invisible", () => !layer.ChildrenOfType<Container>().First().IsPresent);
         }
 
         [Test]
diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditor.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditor.cs
index 0c2c6ed454..c53ac42d12 100644
--- a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditor.cs
+++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditor.cs
@@ -2,6 +2,7 @@
 // See the LICENCE file in the repository root for full licence text.
 
 using NUnit.Framework;
+using osu.Framework.Graphics;
 using osu.Framework.Testing;
 using osu.Game.Rulesets;
 using osu.Game.Rulesets.Osu;
@@ -21,6 +22,7 @@ namespace osu.Game.Tests.Visual.Gameplay
             AddStep("add editor overlay", () =>
             {
                 skinEditor?.Expire();
+                Player.ScaleTo(SkinEditorOverlay.VISIBLE_TARGET_SCALE);
                 LoadComponentAsync(skinEditor = new SkinEditor(Player), Add);
             });
         }
diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditorComponentsList.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditorComponentsList.cs
new file mode 100644
index 0000000000..14bd62b98a
--- /dev/null
+++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditorComponentsList.cs
@@ -0,0 +1,26 @@
+// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using NUnit.Framework;
+using osu.Framework.Graphics;
+using osu.Game.Rulesets;
+using osu.Game.Rulesets.Osu;
+using osu.Game.Skinning.Editor;
+
+namespace osu.Game.Tests.Visual.Gameplay
+{
+    public class TestSceneSkinEditorComponentsList : SkinnableTestScene
+    {
+        [Test]
+        public void TestToggleEditor()
+        {
+            AddStep("show available components", () => SetContents(() => new SkinComponentToolbox(300)
+            {
+                Anchor = Anchor.TopCentre,
+                Origin = Anchor.TopCentre,
+            }));
+        }
+
+        protected override Ruleset CreateRulesetForSkinProvider() => new OsuRuleset();
+    }
+}
diff --git a/osu.Game/Input/IdleTracker.cs b/osu.Game/Input/IdleTracker.cs
index 2d6a21d1cf..f3d531cf6c 100644
--- a/osu.Game/Input/IdleTracker.cs
+++ b/osu.Game/Input/IdleTracker.cs
@@ -6,13 +6,14 @@ using osu.Framework.Graphics;
 using osu.Framework.Input;
 using osu.Framework.Input.Bindings;
 using osu.Framework.Input.Events;
+using osu.Game.Input.Bindings;
 
 namespace osu.Game.Input
 {
     /// <summary>
     /// Track whether the end-user is in an idle state, based on their last interaction with the game.
     /// </summary>
-    public class IdleTracker : Component, IKeyBindingHandler<PlatformAction>, IHandleGlobalKeyboardInput
+    public class IdleTracker : Component, IKeyBindingHandler<PlatformAction>, IKeyBindingHandler<GlobalAction>, IHandleGlobalKeyboardInput
     {
         private readonly double timeToIdle;
 
@@ -58,6 +59,10 @@ namespace osu.Game.Input
 
         public void OnReleased(PlatformAction action) => updateLastInteractionTime();
 
+        public bool OnPressed(GlobalAction action) => updateLastInteractionTime();
+
+        public void OnReleased(GlobalAction action) => updateLastInteractionTime();
+
         protected override bool Handle(UIEvent e)
         {
             switch (e)
diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs
index b1173784b5..f860cd8dd2 100644
--- a/osu.Game/OsuGame.cs
+++ b/osu.Game/OsuGame.cs
@@ -82,6 +82,21 @@ namespace osu.Game
 
         private SkinEditorOverlay skinEditor;
 
+        private Container overlayContent;
+
+        private Container rightFloatingOverlayContent;
+
+        private Container leftFloatingOverlayContent;
+
+        private Container topMostOverlayContent;
+
+        private ScalingContainer screenContainer;
+
+        private Container screenOffsetContainer;
+
+        [Resolved]
+        private FrameworkConfigManager frameworkConfig { get; set; }
+
         [Cached]
         private readonly DifficultyRecommender difficultyRecommender = new DifficultyRecommender();
 
@@ -597,28 +612,35 @@ namespace osu.Game
                     ActionRequested = action => volume.Adjust(action),
                     ScrollActionRequested = (action, amount, isPrecise) => volume.Adjust(action, amount, isPrecise),
                 },
-                screenContainer = new ScalingContainer(ScalingMode.ExcludeOverlays)
+                screenOffsetContainer = new Container
                 {
                     RelativeSizeAxes = Axes.Both,
-                    Anchor = Anchor.Centre,
-                    Origin = Anchor.Centre,
                     Children = new Drawable[]
                     {
-                        receptor = new BackButton.Receptor(),
-                        ScreenStack = new OsuScreenStack { RelativeSizeAxes = Axes.Both },
-                        BackButton = new BackButton(receptor)
+                        screenContainer = new ScalingContainer(ScalingMode.ExcludeOverlays)
                         {
-                            Anchor = Anchor.BottomLeft,
-                            Origin = Anchor.BottomLeft,
-                            Action = () =>
+                            RelativeSizeAxes = Axes.Both,
+                            Anchor = Anchor.Centre,
+                            Origin = Anchor.Centre,
+                            Children = new Drawable[]
                             {
-                                var currentScreen = ScreenStack.CurrentScreen as IOsuScreen;
+                                receptor = new BackButton.Receptor(),
+                                ScreenStack = new OsuScreenStack { RelativeSizeAxes = Axes.Both },
+                                BackButton = new BackButton(receptor)
+                                {
+                                    Anchor = Anchor.BottomLeft,
+                                    Origin = Anchor.BottomLeft,
+                                    Action = () =>
+                                    {
+                                        var currentScreen = ScreenStack.CurrentScreen as IOsuScreen;
 
-                                if (currentScreen?.AllowBackButton == true && !currentScreen.OnBackButton())
-                                    ScreenStack.Exit();
+                                        if (currentScreen?.AllowBackButton == true && !currentScreen.OnBackButton())
+                                            ScreenStack.Exit();
+                                    }
+                                },
+                                logoContainer = new Container { RelativeSizeAxes = Axes.Both },
                             }
                         },
-                        logoContainer = new Container { RelativeSizeAxes = Axes.Both },
                     }
                 },
                 overlayContent = new Container { RelativeSizeAxes = Axes.Both },
@@ -768,7 +790,7 @@ namespace osu.Game
                 if (notifications.State.Value == Visibility.Visible)
                     offset -= Toolbar.HEIGHT / 2;
 
-                screenContainer.MoveToX(offset, SettingsPanel.TRANSITION_LENGTH, Easing.OutQuint);
+                screenOffsetContainer.MoveToX(offset, SettingsPanel.TRANSITION_LENGTH, Easing.OutQuint);
             }
 
             Settings.State.ValueChanged += _ => updateScreenOffset();
@@ -935,19 +957,6 @@ namespace osu.Game
         {
         }
 
-        private Container overlayContent;
-
-        private Container rightFloatingOverlayContent;
-
-        private Container leftFloatingOverlayContent;
-
-        private Container topMostOverlayContent;
-
-        [Resolved]
-        private FrameworkConfigManager frameworkConfig { get; set; }
-
-        private ScalingContainer screenContainer;
-
         protected override bool OnExiting()
         {
             if (ScreenStack.CurrentScreen is Loader)
@@ -966,7 +975,7 @@ namespace osu.Game
         {
             base.UpdateAfterChildren();
 
-            screenContainer.Padding = new MarginPadding { Top = ToolbarOffset };
+            screenOffsetContainer.Padding = new MarginPadding { Top = ToolbarOffset };
             overlayContent.Padding = new MarginPadding { Top = ToolbarOffset };
 
             MenuCursorContainer.CanShowCursor = (ScreenStack.CurrentScreen as IOsuScreen)?.CursorVisible ?? false;
diff --git a/osu.Game/Rulesets/Edit/ScrollingToolboxGroup.cs b/osu.Game/Rulesets/Edit/ScrollingToolboxGroup.cs
new file mode 100644
index 0000000000..a54f574bff
--- /dev/null
+++ b/osu.Game/Rulesets/Edit/ScrollingToolboxGroup.cs
@@ -0,0 +1,32 @@
+// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Game.Graphics.Containers;
+
+namespace osu.Game.Rulesets.Edit
+{
+    public class ScrollingToolboxGroup : ToolboxGroup
+    {
+        protected readonly OsuScrollContainer Scroll;
+
+        protected override Container<Drawable> Content { get; }
+
+        public ScrollingToolboxGroup(string title, float scrollAreaHeight)
+            : base(title)
+        {
+            base.Content.Add(Scroll = new OsuScrollContainer
+            {
+                RelativeSizeAxes = Axes.X,
+                Height = scrollAreaHeight,
+                Child = Content = new FillFlowContainer
+                {
+                    RelativeSizeAxes = Axes.X,
+                    AutoSizeAxes = Axes.Y,
+                    Direction = FillDirection.Vertical,
+                },
+            });
+        }
+    }
+}
diff --git a/osu.Game/Screens/Play/HUD/DefaultAccuracyCounter.cs b/osu.Game/Screens/Play/HUD/DefaultAccuracyCounter.cs
index e41acae9e8..e4c865803d 100644
--- a/osu.Game/Screens/Play/HUD/DefaultAccuracyCounter.cs
+++ b/osu.Game/Screens/Play/HUD/DefaultAccuracyCounter.cs
@@ -4,6 +4,7 @@
 using osu.Framework.Allocation;
 using osu.Framework.Graphics;
 using osu.Game.Graphics;
+using osu.Game.Skinning;
 using osuTK;
 
 namespace osu.Game.Screens.Play.HUD
diff --git a/osu.Game/Screens/Play/HUD/DefaultComboCounter.cs b/osu.Game/Screens/Play/HUD/DefaultComboCounter.cs
index 64226bbb34..375ff293aa 100644
--- a/osu.Game/Screens/Play/HUD/DefaultComboCounter.cs
+++ b/osu.Game/Screens/Play/HUD/DefaultComboCounter.cs
@@ -8,6 +8,7 @@ using osu.Game.Graphics;
 using osu.Game.Graphics.Sprites;
 using osu.Game.Graphics.UserInterface;
 using osu.Game.Rulesets.Scoring;
+using osu.Game.Skinning;
 using osuTK;
 
 namespace osu.Game.Screens.Play.HUD
diff --git a/osu.Game/Screens/Play/HUD/DefaultHealthDisplay.cs b/osu.Game/Screens/Play/HUD/DefaultHealthDisplay.cs
index 27f81467cb..241777244b 100644
--- a/osu.Game/Screens/Play/HUD/DefaultHealthDisplay.cs
+++ b/osu.Game/Screens/Play/HUD/DefaultHealthDisplay.cs
@@ -13,6 +13,7 @@ using osuTK;
 using osuTK.Graphics;
 using osu.Framework.Graphics.Shapes;
 using osu.Framework.Utils;
+using osu.Game.Skinning;
 
 namespace osu.Game.Screens.Play.HUD
 {
@@ -77,7 +78,7 @@ namespace osu.Game.Screens.Play.HUD
             RelativeSizeAxes = Axes.X;
             Margin = new MarginPadding { Top = 20 };
 
-            Children = new Drawable[]
+            InternalChildren = new Drawable[]
             {
                 new Box
                 {
diff --git a/osu.Game/Screens/Play/HUD/DefaultScoreCounter.cs b/osu.Game/Screens/Play/HUD/DefaultScoreCounter.cs
index 37b14df536..8e37797446 100644
--- a/osu.Game/Screens/Play/HUD/DefaultScoreCounter.cs
+++ b/osu.Game/Screens/Play/HUD/DefaultScoreCounter.cs
@@ -4,6 +4,7 @@
 using osu.Framework.Allocation;
 using osu.Framework.Graphics;
 using osu.Game.Graphics;
+using osu.Game.Skinning;
 
 namespace osu.Game.Screens.Play.HUD
 {
diff --git a/osu.Game/Screens/Play/HUD/FailingLayer.cs b/osu.Game/Screens/Play/HUD/FailingLayer.cs
index e071337f34..424ee55766 100644
--- a/osu.Game/Screens/Play/HUD/FailingLayer.cs
+++ b/osu.Game/Screens/Play/HUD/FailingLayer.cs
@@ -43,7 +43,7 @@ namespace osu.Game.Screens.Play.HUD
         public FailingLayer()
         {
             RelativeSizeAxes = Axes.Both;
-            Children = new Drawable[]
+            InternalChildren = new Drawable[]
             {
                 boxes = new Container
                 {
diff --git a/osu.Game/Screens/Play/HUD/HealthDisplay.cs b/osu.Game/Screens/Play/HUD/HealthDisplay.cs
index 6c2571cc28..b970ecd1c7 100644
--- a/osu.Game/Screens/Play/HUD/HealthDisplay.cs
+++ b/osu.Game/Screens/Play/HUD/HealthDisplay.cs
@@ -14,7 +14,7 @@ namespace osu.Game.Screens.Play.HUD
     /// A container for components displaying the current player health.
     /// Gets bound automatically to the <see cref="Rulesets.Scoring.HealthProcessor"/> when inserted to <see cref="DrawableRuleset.Overlays"/> hierarchy.
     /// </summary>
-    public abstract class HealthDisplay : Container
+    public abstract class HealthDisplay : CompositeDrawable
     {
         [Resolved]
         protected HealthProcessor HealthProcessor { get; private set; }
diff --git a/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs b/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs
index c5a21ade03..2e84c9c97d 100644
--- a/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs
+++ b/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs
@@ -13,6 +13,7 @@ using osu.Framework.Graphics.Sprites;
 using osu.Game.Graphics;
 using osu.Game.Rulesets.Judgements;
 using osu.Game.Rulesets.Scoring;
+using osu.Game.Skinning;
 using osuTK;
 using osuTK.Graphics;
 
diff --git a/osu.Game/Screens/Play/HUDOverlay.cs b/osu.Game/Screens/Play/HUDOverlay.cs
index 22812b779f..6ddaa338cc 100644
--- a/osu.Game/Screens/Play/HUDOverlay.cs
+++ b/osu.Game/Screens/Play/HUDOverlay.cs
@@ -16,12 +16,13 @@ using osu.Game.Overlays.Notifications;
 using osu.Game.Rulesets.Mods;
 using osu.Game.Rulesets.UI;
 using osu.Game.Screens.Play.HUD;
+using osu.Game.Skinning;
 using osuTK;
 
 namespace osu.Game.Screens.Play
 {
     [Cached]
-    public class HUDOverlay : Container, IKeyBindingHandler<GlobalAction>
+    public class HUDOverlay : Container, IKeyBindingHandler<GlobalAction>, IDefaultSkinnableTarget
     {
         public const float FADE_DURATION = 300;
 
diff --git a/osu.Game/Screens/Play/SongProgress.cs b/osu.Game/Screens/Play/SongProgress.cs
index db81633aea..d85f3538e4 100644
--- a/osu.Game/Screens/Play/SongProgress.cs
+++ b/osu.Game/Screens/Play/SongProgress.cs
@@ -14,7 +14,7 @@ using osu.Framework.Timing;
 using osu.Game.Configuration;
 using osu.Game.Rulesets.Objects;
 using osu.Game.Rulesets.UI;
-using osu.Game.Screens.Play.HUD;
+using osu.Game.Skinning;
 
 namespace osu.Game.Screens.Play
 {
@@ -76,10 +76,6 @@ namespace osu.Game.Screens.Play
             {
                 new SongProgressDisplay
                 {
-                    Masking = true,
-                    RelativeSizeAxes = Axes.Both,
-                    Anchor = Anchor.BottomCentre,
-                    Origin = Anchor.BottomCentre,
                     Children = new Drawable[]
                     {
                         info = new SongProgressInfo
@@ -187,8 +183,16 @@ namespace osu.Game.Screens.Play
 
         public class SongProgressDisplay : Container, ISkinnableComponent
         {
-            // TODO: move actual implementation into this.
-            // exists for skin customisation purposes.
+            public SongProgressDisplay()
+            {
+                // TODO: move actual implementation into this.
+                // exists for skin customisation purposes.
+
+                Masking = true;
+                RelativeSizeAxes = Axes.Both;
+                Anchor = Anchor.BottomCentre;
+                Origin = Anchor.BottomCentre;
+            }
         }
     }
 }
diff --git a/osu.Game/Skinning/Editor/SkinBlueprint.cs b/osu.Game/Skinning/Editor/SkinBlueprint.cs
index 11409c46ab..b8dfdbad0a 100644
--- a/osu.Game/Skinning/Editor/SkinBlueprint.cs
+++ b/osu.Game/Skinning/Editor/SkinBlueprint.cs
@@ -9,7 +9,6 @@ using osu.Framework.Graphics.Shapes;
 using osu.Game.Graphics;
 using osu.Game.Graphics.UserInterface;
 using osu.Game.Rulesets.Edit;
-using osu.Game.Screens.Play.HUD;
 using osuTK;
 
 namespace osu.Game.Skinning.Editor
diff --git a/osu.Game/Skinning/Editor/SkinBlueprintContainer.cs b/osu.Game/Skinning/Editor/SkinBlueprintContainer.cs
index d9bfefe5f2..35e93d9aff 100644
--- a/osu.Game/Skinning/Editor/SkinBlueprintContainer.cs
+++ b/osu.Game/Skinning/Editor/SkinBlueprintContainer.cs
@@ -6,7 +6,6 @@ using osu.Framework.Graphics;
 using osu.Framework.Testing;
 using osu.Game.Rulesets.Edit;
 using osu.Game.Screens.Edit.Compose.Components;
-using osu.Game.Screens.Play.HUD;
 
 namespace osu.Game.Skinning.Editor
 {
diff --git a/osu.Game/Skinning/Editor/SkinComponentToolbox.cs b/osu.Game/Skinning/Editor/SkinComponentToolbox.cs
new file mode 100644
index 0000000000..a000204062
--- /dev/null
+++ b/osu.Game/Skinning/Editor/SkinComponentToolbox.cs
@@ -0,0 +1,162 @@
+// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System;
+using System.Diagnostics;
+using System.Linq;
+using osu.Framework.Allocation;
+using osu.Framework.Extensions.Color4Extensions;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Effects;
+using osu.Framework.Input.Events;
+using osu.Game.Graphics;
+using osu.Game.Graphics.Sprites;
+using osu.Game.Graphics.UserInterface;
+using osu.Game.Rulesets.Edit;
+using osu.Game.Rulesets.Scoring;
+using osuTK;
+using osuTK.Graphics;
+
+namespace osu.Game.Skinning.Editor
+{
+    public class SkinComponentToolbox : ScrollingToolboxGroup
+    {
+        public Action<Type> RequestPlacement;
+
+        private const float component_display_scale = 0.8f;
+
+        [Cached]
+        private ScoreProcessor scoreProcessor = new ScoreProcessor
+        {
+            Combo = { Value = 727 },
+            TotalScore = { Value = 1337377 }
+        };
+
+        [Cached(typeof(HealthProcessor))]
+        private HealthProcessor healthProcessor = new DrainingHealthProcessor(0);
+
+        public SkinComponentToolbox(float height)
+            : base("Components", height)
+        {
+            RelativeSizeAxes = Axes.None;
+            Width = 200;
+        }
+
+        [BackgroundDependencyLoader]
+        private void load()
+        {
+            FillFlowContainer fill;
+
+            Child = fill = new FillFlowContainer
+            {
+                RelativeSizeAxes = Axes.X,
+                AutoSizeAxes = Axes.Y,
+                Direction = FillDirection.Vertical,
+                Spacing = new Vector2(20)
+            };
+
+            var skinnableTypes = typeof(OsuGame).Assembly.GetTypes().Where(t => typeof(ISkinnableComponent).IsAssignableFrom(t)).ToArray();
+
+            foreach (var type in skinnableTypes)
+            {
+                var component = attemptAddComponent(type);
+
+                if (component != null)
+                {
+                    component.RequestPlacement = t => RequestPlacement?.Invoke(t);
+                    fill.Add(component);
+                }
+            }
+        }
+
+        private static ToolboxComponentButton attemptAddComponent(Type type)
+        {
+            try
+            {
+                var instance = (Drawable)Activator.CreateInstance(type);
+
+                Debug.Assert(instance != null);
+
+                return new ToolboxComponentButton(instance);
+            }
+            catch
+            {
+                return null;
+            }
+        }
+
+        private class ToolboxComponentButton : OsuButton
+        {
+            private readonly Drawable component;
+
+            public Action<Type> RequestPlacement;
+
+            private Container innerContainer;
+
+            public ToolboxComponentButton(Drawable component)
+            {
+                this.component = component;
+
+                Enabled.Value = true;
+
+                RelativeSizeAxes = Axes.X;
+                Height = 70;
+            }
+
+            [BackgroundDependencyLoader]
+            private void load(OsuColour colours)
+            {
+                BackgroundColour = colours.Gray3;
+                Content.EdgeEffect = new EdgeEffectParameters
+                {
+                    Type = EdgeEffectType.Shadow,
+                    Radius = 2,
+                    Offset = new Vector2(0, 1),
+                    Colour = Color4.Black.Opacity(0.5f)
+                };
+
+                AddRange(new Drawable[]
+                {
+                    new OsuSpriteText
+                    {
+                        Text = component.GetType().Name,
+                        Anchor = Anchor.TopCentre,
+                        Origin = Anchor.TopCentre,
+                    },
+                    innerContainer = new Container
+                    {
+                        Y = 10,
+                        Anchor = Anchor.Centre,
+                        Origin = Anchor.Centre,
+                        RelativeSizeAxes = Axes.Both,
+                        Scale = new Vector2(component_display_scale),
+                        Masking = true,
+                        Child = component
+                    }
+                });
+
+                // adjust provided component to fit / display in a known state.
+                component.Anchor = Anchor.Centre;
+                component.Origin = Anchor.Centre;
+            }
+
+            protected override void LoadComplete()
+            {
+                base.LoadComplete();
+
+                if (component.RelativeSizeAxes != Axes.None)
+                {
+                    innerContainer.AutoSizeAxes = Axes.None;
+                    innerContainer.Height = 100;
+                }
+            }
+
+            protected override bool OnClick(ClickEvent e)
+            {
+                RequestPlacement?.Invoke(component.GetType());
+                return true;
+            }
+        }
+    }
+}
diff --git a/osu.Game/Skinning/Editor/SkinEditor.cs b/osu.Game/Skinning/Editor/SkinEditor.cs
index 562dd23224..18a8b220df 100644
--- a/osu.Game/Skinning/Editor/SkinEditor.cs
+++ b/osu.Game/Skinning/Editor/SkinEditor.cs
@@ -1,10 +1,13 @@
 // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
 // See the LICENCE file in the repository root for full licence text.
 
+using System;
+using System.Linq;
 using osu.Framework.Allocation;
 using osu.Framework.Graphics;
 using osu.Framework.Graphics.Containers;
 using osu.Framework.Input.Events;
+using osu.Framework.Testing;
 using osu.Game.Graphics;
 using osu.Game.Graphics.Containers;
 using osu.Game.Graphics.Cursor;
@@ -45,6 +48,12 @@ namespace osu.Game.Skinning.Editor
                         RelativeSizeAxes = Axes.X
                     },
                     new SkinBlueprintContainer(target),
+                    new SkinComponentToolbox(600)
+                    {
+                        Anchor = Anchor.CentreLeft,
+                        Origin = Anchor.CentreLeft,
+                        RequestPlacement = placeComponent
+                    }
                 }
             };
 
@@ -56,6 +65,15 @@ namespace osu.Game.Skinning.Editor
             });
         }
 
+        private void placeComponent(Type type)
+        {
+            var instance = (Drawable)Activator.CreateInstance(type);
+
+            var targetContainer = target.ChildrenOfType<IDefaultSkinnableTarget>().FirstOrDefault();
+
+            targetContainer?.Add(instance);
+        }
+
         protected override void LoadComplete()
         {
             base.LoadComplete();
diff --git a/osu.Game/Skinning/Editor/SkinEditorOverlay.cs b/osu.Game/Skinning/Editor/SkinEditorOverlay.cs
index 06c6dffb60..cc989bb459 100644
--- a/osu.Game/Skinning/Editor/SkinEditorOverlay.cs
+++ b/osu.Game/Skinning/Editor/SkinEditorOverlay.cs
@@ -21,7 +21,7 @@ namespace osu.Game.Skinning.Editor
         private readonly ScalingContainer target;
         private SkinEditor skinEditor;
 
-        private const float visible_target_scale = 0.8f;
+        public const float VISIBLE_TARGET_SCALE = 0.8f;
 
         [Resolved]
         private OsuColour colours { get; set; }
@@ -64,12 +64,14 @@ namespace osu.Game.Skinning.Editor
         {
             if (visibility.NewValue == Visibility.Visible)
             {
-                target.ScaleTo(visible_target_scale, SkinEditor.TRANSITION_DURATION, Easing.OutQuint);
-
                 target.Masking = true;
                 target.BorderThickness = 5;
                 target.BorderColour = colours.Yellow;
                 target.AllowScaling = false;
+                target.RelativePositionAxes = Axes.Both;
+
+                target.ScaleTo(VISIBLE_TARGET_SCALE, SkinEditor.TRANSITION_DURATION, Easing.OutQuint);
+                target.MoveToX(0.1f, SkinEditor.TRANSITION_DURATION, Easing.OutQuint);
             }
             else
             {
@@ -77,6 +79,7 @@ namespace osu.Game.Skinning.Editor
                 target.AllowScaling = true;
 
                 target.ScaleTo(1, SkinEditor.TRANSITION_DURATION, Easing.OutQuint).OnComplete(_ => target.Masking = false);
+                target.MoveToX(0f, SkinEditor.TRANSITION_DURATION, Easing.OutQuint);
             }
         }
 
diff --git a/osu.Game/Skinning/Editor/SkinSelectionHandler.cs b/osu.Game/Skinning/Editor/SkinSelectionHandler.cs
index d09ba8af0e..ad783a9c0e 100644
--- a/osu.Game/Skinning/Editor/SkinSelectionHandler.cs
+++ b/osu.Game/Skinning/Editor/SkinSelectionHandler.cs
@@ -10,7 +10,6 @@ using osu.Game.Extensions;
 using osu.Game.Graphics.UserInterface;
 using osu.Game.Rulesets.Edit;
 using osu.Game.Screens.Edit.Compose.Components;
-using osu.Game.Screens.Play.HUD;
 using osuTK;
 
 namespace osu.Game.Skinning.Editor
diff --git a/osu.Game/Skinning/IDefaultSkinnableTarget.cs b/osu.Game/Skinning/IDefaultSkinnableTarget.cs
new file mode 100644
index 0000000000..24fb454af8
--- /dev/null
+++ b/osu.Game/Skinning/IDefaultSkinnableTarget.cs
@@ -0,0 +1,12 @@
+// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+namespace osu.Game.Skinning
+{
+    /// <summary>
+    /// The default placement location for new <see cref="ISkinnableComponent"/>s.
+    /// </summary>
+    public interface IDefaultSkinnableTarget : ISkinnableTarget
+    {
+    }
+}
diff --git a/osu.Game/Screens/Play/HUD/ISkinnableComponent.cs b/osu.Game/Skinning/ISkinnableComponent.cs
similarity index 91%
rename from osu.Game/Screens/Play/HUD/ISkinnableComponent.cs
rename to osu.Game/Skinning/ISkinnableComponent.cs
index 6d4558443f..f6b0a182b4 100644
--- a/osu.Game/Screens/Play/HUD/ISkinnableComponent.cs
+++ b/osu.Game/Skinning/ISkinnableComponent.cs
@@ -3,7 +3,7 @@
 
 using osu.Framework.Graphics;
 
-namespace osu.Game.Screens.Play.HUD
+namespace osu.Game.Skinning
 {
     /// <summary>
     /// Denotes a drawable which, as a drawable, can be adjusted via skinning specifications.
diff --git a/osu.Game/Skinning/ISkinnableTarget.cs b/osu.Game/Skinning/ISkinnableTarget.cs
new file mode 100644
index 0000000000..607e89fdec
--- /dev/null
+++ b/osu.Game/Skinning/ISkinnableTarget.cs
@@ -0,0 +1,15 @@
+// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+
+namespace osu.Game.Skinning
+{
+    /// <summary>
+    /// Denotes a container which can house <see cref="ISkinnableComponent"/>s.
+    /// </summary>
+    public interface ISkinnableTarget : IContainerCollection<Drawable>
+    {
+    }
+}