diff --git a/README.md b/README.md
index c4d676f4be..56491a4be4 100644
--- a/README.md
+++ b/README.md
@@ -19,8 +19,9 @@ Detailed changelogs are published on the [official osu! site](https://osu.ppy.sh
 ## Requirements
 
 - A desktop platform with the [.NET Core SDK 2.2](https://www.microsoft.com/net/learn/get-started) or higher installed.
+- When running on linux, please have a system-wide ffmpeg installation available to support video decoding.
+- When running on Windows 7 or 8.1, **[additional prerequisites](https://docs.microsoft.com/en-us/dotnet/core/windows-prerequisites?tabs=netcore2x)** may be required to correctly run .NET Core applications if your operating system is not up-to-date with the latest service packs.
 - When working with the codebase, we recommend using an IDE with intellisense and syntax highlighting, such as [Visual Studio 2017+](https://visualstudio.microsoft.com/vs/), [Jetbrains Rider](https://www.jetbrains.com/rider/) or [Visual Studio Code](https://code.visualstudio.com/).
-- Note that there are **[additional requirements for Windows 7 and Windows 8.1](https://docs.microsoft.com/en-us/dotnet/core/windows-prerequisites?tabs=netcore2x)** which you may need to manually install if your operating system is not up-to-date.
 
 ## Running osu!
 
diff --git a/osu.Android.props b/osu.Android.props
index 721d341c08..85741fcf84 100644
--- a/osu.Android.props
+++ b/osu.Android.props
@@ -63,6 +63,6 @@
   </ItemGroup>
   <ItemGroup>
     <PackageReference Include="ppy.osu.Game.Resources" Version="2019.809.0" />
-    <PackageReference Include="ppy.osu.Framework.Android" Version="2019.809.0" />
+    <PackageReference Include="ppy.osu.Framework.Android" Version="2019.813.0" />
   </ItemGroup>
 </Project>
diff --git a/osu.Game.Tests/Visual/Menus/TestSceneIntroTriangles.cs b/osu.Game.Tests/Visual/Menus/TestSceneIntroTriangles.cs
new file mode 100644
index 0000000000..df79584167
--- /dev/null
+++ b/osu.Game.Tests/Visual/Menus/TestSceneIntroTriangles.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 NUnit.Framework;
+using osu.Framework.Screens;
+using osu.Game.Screens.Menu;
+
+namespace osu.Game.Tests.Visual.Menus
+{
+    [TestFixture]
+    public class TestSceneIntroTriangles : IntroTestScene
+    {
+        protected override IScreen CreateScreen() => new IntroTriangles();
+    }
+}
diff --git a/osu.Game.Tests/Visual/Online/TestSceneUserProfilePreviousUsernames.cs b/osu.Game.Tests/Visual/Online/TestSceneUserProfilePreviousUsernames.cs
new file mode 100644
index 0000000000..d09a50b12c
--- /dev/null
+++ b/osu.Game.Tests/Visual/Online/TestSceneUserProfilePreviousUsernames.cs
@@ -0,0 +1,69 @@
+// 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.Collections.Generic;
+using NUnit.Framework;
+using osu.Framework.Allocation;
+using osu.Framework.Bindables;
+using osu.Framework.Graphics;
+using osu.Game.Online.API;
+using osu.Game.Online.API.Requests;
+using osu.Game.Overlays.Profile.Header.Components;
+using osu.Game.Users;
+
+namespace osu.Game.Tests.Visual.Online
+{
+    [TestFixture]
+    public class TestSceneUserProfilePreviousUsernames : OsuTestScene
+    {
+        public override IReadOnlyList<Type> RequiredTypes => new[]
+        {
+            typeof(PreviousUsernames)
+        };
+
+        [Resolved]
+        private IAPIProvider api { get; set; }
+
+        private readonly Bindable<User> user = new Bindable<User>();
+
+        public TestSceneUserProfilePreviousUsernames()
+        {
+            Child = new PreviousUsernames
+            {
+                Anchor = Anchor.Centre,
+                Origin = Anchor.Centre,
+                User = { BindTarget = user },
+            };
+
+            User[] users =
+            {
+                new User { PreviousUsernames = new[] { "username1" } },
+                new User { PreviousUsernames = new[] { "longusername", "longerusername" } },
+                new User { PreviousUsernames = new[] { "test", "angelsim", "verylongusername" } },
+                new User { PreviousUsernames = new[] { "ihavenoidea", "howcani", "makethistext", "anylonger" } },
+                new User { PreviousUsernames = new string[0] },
+                null
+            };
+
+            AddStep("single username", () => user.Value = users[0]);
+            AddStep("two usernames", () => user.Value = users[1]);
+            AddStep("three usernames", () => user.Value = users[2]);
+            AddStep("four usernames", () => user.Value = users[3]);
+            AddStep("no username", () => user.Value = users[4]);
+            AddStep("null user", () => user.Value = users[5]);
+        }
+
+        protected override void LoadComplete()
+        {
+            base.LoadComplete();
+
+            AddStep("online user (Angelsim)", () =>
+            {
+                var request = new GetUserRequest(1777162);
+                request.Success += user => this.user.Value = user;
+                api.Queue(request);
+            });
+        }
+    }
+}
diff --git a/osu.Game/Configuration/IntroSequence.cs b/osu.Game/Configuration/IntroSequence.cs
new file mode 100644
index 0000000000..1eb953be36
--- /dev/null
+++ b/osu.Game/Configuration/IntroSequence.cs
@@ -0,0 +1,11 @@
+// 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.Configuration
+{
+    public enum IntroSequence
+    {
+        Circles,
+        Triangles
+    }
+}
diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs
index 1da7c7ec1d..19f46c1d6a 100644
--- a/osu.Game/Configuration/OsuConfigManager.cs
+++ b/osu.Game/Configuration/OsuConfigManager.cs
@@ -105,6 +105,8 @@ namespace osu.Game.Configuration
             Set(OsuSetting.ScalingPositionY, 0.5f, 0f, 1f);
 
             Set(OsuSetting.UIScale, 1f, 0.8f, 1.6f, 0.01f);
+
+            Set(OsuSetting.IntroSequence, IntroSequence.Triangles);
         }
 
         public OsuConfigManager(Storage storage)
@@ -167,6 +169,7 @@ namespace osu.Game.Configuration
         ScalingPositionY,
         ScalingSizeX,
         ScalingSizeY,
-        UIScale
+        UIScale,
+        IntroSequence
     }
 }
diff --git a/osu.Game/Overlays/Profile/Header/Components/PreviousUsernames.cs b/osu.Game/Overlays/Profile/Header/Components/PreviousUsernames.cs
new file mode 100644
index 0000000000..f18f319e27
--- /dev/null
+++ b/osu.Game/Overlays/Profile/Header/Components/PreviousUsernames.cs
@@ -0,0 +1,168 @@
+// 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.Bindables;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Shapes;
+using osu.Framework.Graphics.Sprites;
+using osu.Framework.Input.Events;
+using osu.Game.Graphics;
+using osu.Game.Users;
+using osuTK;
+
+namespace osu.Game.Overlays.Profile.Header.Components
+{
+    public class PreviousUsernames : CompositeDrawable
+    {
+        private const int duration = 200;
+        private const int margin = 10;
+        private const int width = 310;
+        private const int move_offset = 15;
+
+        public readonly Bindable<User> User = new Bindable<User>();
+
+        private readonly TextFlowContainer text;
+        private readonly Box background;
+        private readonly SpriteText header;
+
+        public PreviousUsernames()
+        {
+            HoverIconContainer hoverIcon;
+
+            AutoSizeAxes = Axes.Y;
+            Width = width;
+            Masking = true;
+            CornerRadius = 5;
+
+            AddRangeInternal(new Drawable[]
+            {
+                background = new Box
+                {
+                    RelativeSizeAxes = Axes.Both,
+                },
+                new GridContainer
+                {
+                    AutoSizeAxes = Axes.Y,
+                    RelativeSizeAxes = Axes.X,
+                    RowDimensions = new[]
+                    {
+                        new Dimension(GridSizeMode.AutoSize),
+                        new Dimension(GridSizeMode.AutoSize)
+                    },
+                    ColumnDimensions = new[]
+                    {
+                        new Dimension(GridSizeMode.AutoSize),
+                        new Dimension(GridSizeMode.Distributed)
+                    },
+                    Content = new[]
+                    {
+                        new Drawable[]
+                        {
+                            hoverIcon = new HoverIconContainer(),
+                            header = new SpriteText
+                            {
+                                Anchor = Anchor.BottomLeft,
+                                Origin = Anchor.BottomLeft,
+                                Text = @"formerly known as",
+                                Font = OsuFont.GetFont(size: 10, italics: true)
+                            }
+                        },
+                        new Drawable[]
+                        {
+                            new Container
+                            {
+                                RelativeSizeAxes = Axes.Both,
+                            },
+                            text = new TextFlowContainer(s => s.Font = OsuFont.GetFont(size: 12, weight: FontWeight.Bold, italics: true))
+                            {
+                                RelativeSizeAxes = Axes.X,
+                                AutoSizeAxes = Axes.Y,
+                                Direction = FillDirection.Full,
+                                Margin = new MarginPadding { Bottom = margin, Top = margin / 2f }
+                            }
+                        }
+                    }
+                }
+            });
+
+            hoverIcon.ActivateHover += showContent;
+            hideContent();
+        }
+
+        [BackgroundDependencyLoader]
+        private void load(OsuColour colours)
+        {
+            background.Colour = colours.GreySeafoamDarker;
+        }
+
+        protected override void LoadComplete()
+        {
+            base.LoadComplete();
+            User.BindValueChanged(onUserChanged, true);
+        }
+
+        private void onUserChanged(ValueChangedEvent<User> user)
+        {
+            text.Text = string.Empty;
+
+            var usernames = user.NewValue?.PreviousUsernames;
+
+            if (usernames?.Any() ?? false)
+            {
+                text.Text = string.Join(", ", usernames);
+                Show();
+                return;
+            }
+
+            Hide();
+        }
+
+        protected override void OnHoverLost(HoverLostEvent e)
+        {
+            base.OnHoverLost(e);
+            hideContent();
+        }
+
+        private void showContent()
+        {
+            text.FadeIn(duration, Easing.OutQuint);
+            header.FadeIn(duration, Easing.OutQuint);
+            background.FadeIn(duration, Easing.OutQuint);
+            this.MoveToY(-move_offset, duration, Easing.OutQuint);
+        }
+
+        private void hideContent()
+        {
+            text.FadeOut(duration, Easing.OutQuint);
+            header.FadeOut(duration, Easing.OutQuint);
+            background.FadeOut(duration, Easing.OutQuint);
+            this.MoveToY(0, duration, Easing.OutQuint);
+        }
+
+        private class HoverIconContainer : Container
+        {
+            public Action ActivateHover;
+
+            public HoverIconContainer()
+            {
+                AutoSizeAxes = Axes.Both;
+                Child = new SpriteIcon
+                {
+                    Margin = new MarginPadding { Top = 6, Left = margin, Right = margin * 2 },
+                    Size = new Vector2(15),
+                    Icon = FontAwesome.Solid.IdCard,
+                };
+            }
+
+            protected override bool OnHover(HoverEvent e)
+            {
+                ActivateHover?.Invoke();
+                return base.OnHover(e);
+            }
+        }
+    }
+}
diff --git a/osu.Game/Overlays/Settings/Sections/Audio/MainMenuSettings.cs b/osu.Game/Overlays/Settings/Sections/Audio/MainMenuSettings.cs
index 4e43caff23..5ccdc952ba 100644
--- a/osu.Game/Overlays/Settings/Sections/Audio/MainMenuSettings.cs
+++ b/osu.Game/Overlays/Settings/Sections/Audio/MainMenuSettings.cs
@@ -1,7 +1,10 @@
 // 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.Game.Configuration;
 
 namespace osu.Game.Overlays.Settings.Sections.Audio
@@ -13,7 +16,7 @@ namespace osu.Game.Overlays.Settings.Sections.Audio
         [BackgroundDependencyLoader]
         private void load(OsuConfigManager config)
         {
-            Children = new[]
+            Children = new Drawable[]
             {
                 new SettingsCheckbox
                 {
@@ -25,6 +28,12 @@ namespace osu.Game.Overlays.Settings.Sections.Audio
                     LabelText = "osu! music theme",
                     Bindable = config.GetBindable<bool>(OsuSetting.MenuMusic)
                 },
+                new SettingsDropdown<IntroSequence>
+                {
+                    LabelText = "Intro sequence",
+                    Bindable = config.GetBindable<IntroSequence>(OsuSetting.IntroSequence),
+                    Items = Enum.GetValues(typeof(IntroSequence)).Cast<IntroSequence>()
+                },
             };
         }
     }
diff --git a/osu.Game/Screens/BackgroundScreen.cs b/osu.Game/Screens/BackgroundScreen.cs
index bbe162cf7c..5dfaceccf5 100644
--- a/osu.Game/Screens/BackgroundScreen.cs
+++ b/osu.Game/Screens/BackgroundScreen.cs
@@ -11,8 +11,11 @@ namespace osu.Game.Screens
 {
     public abstract class BackgroundScreen : Screen, IEquatable<BackgroundScreen>
     {
-        protected BackgroundScreen()
+        private readonly bool animateOnEnter;
+
+        protected BackgroundScreen(bool animateOnEnter = true)
         {
+            this.animateOnEnter = animateOnEnter;
             Anchor = Anchor.Centre;
             Origin = Anchor.Centre;
         }
@@ -39,11 +42,14 @@ namespace osu.Game.Screens
 
         public override void OnEntering(IScreen last)
         {
-            this.FadeOut();
-            this.MoveToX(x_movement_amount);
+            if (animateOnEnter)
+            {
+                this.FadeOut();
+                this.MoveToX(x_movement_amount);
 
-            this.FadeIn(transition_length, Easing.InOutQuart);
-            this.MoveToX(0, transition_length, Easing.InOutQuart);
+                this.FadeIn(transition_length, Easing.InOutQuart);
+                this.MoveToX(0, transition_length, Easing.InOutQuart);
+            }
 
             base.OnEntering(last);
         }
diff --git a/osu.Game/Screens/Backgrounds/BackgroundScreenDefault.cs b/osu.Game/Screens/Backgrounds/BackgroundScreenDefault.cs
index 55338ea01a..2d7fe6a6a3 100644
--- a/osu.Game/Screens/Backgrounds/BackgroundScreenDefault.cs
+++ b/osu.Game/Screens/Backgrounds/BackgroundScreenDefault.cs
@@ -25,6 +25,11 @@ namespace osu.Game.Screens.Backgrounds
         private Bindable<User> user;
         private Bindable<Skin> skin;
 
+        public BackgroundScreenDefault(bool animateOnEnter = true)
+            : base(animateOnEnter)
+        {
+        }
+
         [BackgroundDependencyLoader]
         private void load(IAPIProvider api, SkinManager skinManager)
         {
diff --git a/osu.Game/Screens/Loader.cs b/osu.Game/Screens/Loader.cs
index de00ba2e9f..850349272e 100644
--- a/osu.Game/Screens/Loader.cs
+++ b/osu.Game/Screens/Loader.cs
@@ -9,6 +9,8 @@ using osu.Framework.Graphics.Shaders;
 using osu.Game.Screens.Menu;
 using osuTK;
 using osu.Framework.Screens;
+using osu.Game.Configuration;
+using IntroSequence = osu.Game.Configuration.IntroSequence;
 
 namespace osu.Game.Screens
 {
@@ -45,6 +47,8 @@ namespace osu.Game.Screens
         private OsuScreen loadableScreen;
         private ShaderPrecompiler precompiler;
 
+        private IntroSequence introSequence;
+
         protected virtual OsuScreen CreateLoadableScreen()
         {
             if (showDisclaimer)
@@ -53,7 +57,17 @@ namespace osu.Game.Screens
             return getIntroSequence();
         }
 
-        private IntroScreen getIntroSequence() => new IntroCircles();
+        private IntroScreen getIntroSequence()
+        {
+            switch (introSequence)
+            {
+                case IntroSequence.Circles:
+                    return new IntroCircles();
+
+                default:
+                    return new IntroTriangles();
+            }
+        }
 
         protected virtual ShaderPrecompiler CreateShaderPrecompiler() => new ShaderPrecompiler();
 
@@ -79,9 +93,10 @@ namespace osu.Game.Screens
         }
 
         [BackgroundDependencyLoader]
-        private void load(OsuGameBase game)
+        private void load(OsuGameBase game, OsuConfigManager config)
         {
             showDisclaimer = game.IsDeployedBuild;
+            introSequence = config.Get<IntroSequence>(OsuSetting.IntroSequence);
         }
 
         /// <summary>
diff --git a/osu.Game/Screens/Menu/IntroTriangles.cs b/osu.Game/Screens/Menu/IntroTriangles.cs
new file mode 100644
index 0000000000..ba0d624959
--- /dev/null
+++ b/osu.Game/Screens/Menu/IntroTriangles.cs
@@ -0,0 +1,413 @@
+// 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.Collections.Generic;
+using System.IO;
+using osu.Framework.Allocation;
+using osu.Framework.Audio;
+using osu.Framework.Audio.Sample;
+using osu.Framework.Audio.Track;
+using osu.Framework.Bindables;
+using osu.Framework.Screens;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Shapes;
+using osu.Framework.Graphics.Textures;
+using osu.Framework.Graphics.Video;
+using osu.Framework.MathUtils;
+using osu.Framework.Timing;
+using osu.Game.Beatmaps;
+using osu.Game.Configuration;
+using osu.Game.Graphics;
+using osu.Game.Graphics.Containers;
+using osu.Game.Graphics.Sprites;
+using osu.Game.IO.Archives;
+using osu.Game.Rulesets;
+using osu.Game.Screens.Backgrounds;
+using osuTK;
+using osuTK.Graphics;
+
+namespace osu.Game.Screens.Menu
+{
+    public class IntroTriangles : IntroScreen
+    {
+        private const string menu_music_beatmap_hash = "a1556d0801b3a6b175dda32ef546f0ec812b400499f575c44fccbe9c67f9b1e5";
+
+        private SampleChannel welcome;
+
+        protected override BackgroundScreen CreateBackground() => background = new BackgroundScreenDefault(false)
+        {
+            Alpha = 0,
+        };
+
+        [Resolved]
+        private AudioManager audio { get; set; }
+
+        private Bindable<bool> menuMusic;
+        private Track track;
+        private WorkingBeatmap introBeatmap;
+
+        private BackgroundScreenDefault background;
+
+        [BackgroundDependencyLoader]
+        private void load(OsuConfigManager config, BeatmapManager beatmaps, Framework.Game game)
+        {
+            menuMusic = config.GetBindable<bool>(OsuSetting.MenuMusic);
+
+            BeatmapSetInfo setInfo = null;
+
+            if (!menuMusic.Value)
+            {
+                var sets = beatmaps.GetAllUsableBeatmapSets();
+                if (sets.Count > 0)
+                    setInfo = beatmaps.QueryBeatmapSet(s => s.ID == sets[RNG.Next(0, sets.Count - 1)].ID);
+            }
+
+            if (setInfo == null)
+            {
+                setInfo = beatmaps.QueryBeatmapSet(b => b.Hash == menu_music_beatmap_hash);
+
+                if (setInfo == null)
+                {
+                    // we need to import the default menu background beatmap
+                    setInfo = beatmaps.Import(new ZipArchiveReader(game.Resources.GetStream(@"Tracks/triangles.osz"), "triangles.osz")).Result;
+
+                    setInfo.Protected = true;
+                    beatmaps.Update(setInfo);
+                }
+            }
+
+            introBeatmap = beatmaps.GetWorkingBeatmap(setInfo.Beatmaps[0]);
+
+            track = introBeatmap.Track;
+            track.Reset();
+
+            if (config.Get<bool>(OsuSetting.MenuVoice) && !menuMusic.Value)
+                // triangles has welcome sound included in the track. only play this if the user doesn't want menu music.
+                welcome = audio.Samples.Get(@"welcome");
+        }
+
+        protected override void LogoArriving(OsuLogo logo, bool resuming)
+        {
+            base.LogoArriving(logo, resuming);
+
+            logo.Triangles = true;
+
+            if (!resuming)
+            {
+                Beatmap.Value = introBeatmap;
+                introBeatmap = null;
+
+                PrepareMenuLoad();
+
+                LoadComponentAsync(new TrianglesIntroSequence(logo, background)
+                {
+                    RelativeSizeAxes = Axes.Both,
+                    Clock = new FramedClock(menuMusic.Value ? track : null),
+                    LoadMenu = LoadMenu
+                }, t =>
+                {
+                    AddInternal(t);
+                    welcome?.Play();
+
+                    // Only start the current track if it is the menu music. A beatmap's track is started when entering the Main Menu.
+                    if (menuMusic.Value)
+                        track.Start();
+                });
+            }
+        }
+
+        public override void OnResuming(IScreen last)
+        {
+            base.OnResuming(last);
+            background.FadeOut(100);
+        }
+
+        public override void OnSuspending(IScreen next)
+        {
+            track = null;
+            base.OnSuspending(next);
+        }
+
+        private class TrianglesIntroSequence : CompositeDrawable
+        {
+            private readonly OsuLogo logo;
+            private readonly BackgroundScreenDefault background;
+            private OsuSpriteText welcomeText;
+
+            private RulesetFlow rulesets;
+            private Container rulesetsScale;
+            private Drawable logoContainerSecondary;
+            private Drawable logoContainer;
+
+            private GlitchingTriangles triangles;
+
+            public Action LoadMenu;
+
+            public TrianglesIntroSequence(OsuLogo logo, BackgroundScreenDefault background)
+            {
+                this.logo = logo;
+                this.background = background;
+            }
+
+            private OsuGameBase game;
+
+            [BackgroundDependencyLoader]
+            private void load(TextureStore textures, OsuGameBase game)
+            {
+                this.game = game;
+
+                InternalChildren = new[]
+                {
+                    triangles = new GlitchingTriangles
+                    {
+                        Alpha = 0,
+                        Anchor = Anchor.Centre,
+                        Origin = Anchor.Centre,
+                        Size = new Vector2(0.4f, 0.16f)
+                    },
+                    welcomeText = new OsuSpriteText
+                    {
+                        Anchor = Anchor.Centre,
+                        Origin = Anchor.Centre,
+                        Padding = new MarginPadding { Bottom = 10 },
+                        Font = OsuFont.GetFont(weight: FontWeight.Light, size: 42),
+                        Alpha = 1,
+                        Spacing = new Vector2(5),
+                    },
+                    rulesetsScale = new Container
+                    {
+                        RelativeSizeAxes = Axes.Both,
+                        Anchor = Anchor.Centre,
+                        Origin = Anchor.Centre,
+                        Children = new Drawable[]
+                        {
+                            rulesets = new RulesetFlow()
+                        }
+                    },
+                    logoContainerSecondary = new Container
+                    {
+                        RelativeSizeAxes = Axes.Both,
+                        Anchor = Anchor.Centre,
+                        Origin = Anchor.Centre,
+                        Child = logoContainer = new LazerLogo(textures.GetStream("Menu/logo-triangles.mp4"))
+                        {
+                            Anchor = Anchor.Centre,
+                            Origin = Anchor.Centre,
+                        }
+                    },
+                };
+            }
+
+            private const double text_1 = 200;
+            private const double text_2 = 400;
+            private const double text_3 = 700;
+            private const double text_4 = 900;
+            private const double text_glitch = 1060;
+
+            private const double rulesets_1 = 1450;
+            private const double rulesets_2 = 1650;
+            private const double rulesets_3 = 1850;
+
+            private const double logo_scale_duration = 920;
+            private const double logo_1 = 2080;
+            private const double logo_2 = logo_1 + logo_scale_duration;
+
+            protected override void LoadComplete()
+            {
+                base.LoadComplete();
+
+                const float scale_start = 1.2f;
+                const float scale_adjust = 0.8f;
+
+                rulesets.Hide();
+                logoContainer.Hide();
+                background.Hide();
+
+                using (BeginAbsoluteSequence(0, true))
+                {
+                    using (BeginDelayedSequence(text_1, true))
+                        welcomeText.FadeIn().OnComplete(t => t.Text = "wel");
+
+                    using (BeginDelayedSequence(text_2, true))
+                        welcomeText.FadeIn().OnComplete(t => t.Text = "welcome");
+
+                    using (BeginDelayedSequence(text_3, true))
+                        welcomeText.FadeIn().OnComplete(t => t.Text = "welcome to");
+
+                    using (BeginDelayedSequence(text_4, true))
+                    {
+                        welcomeText.FadeIn().OnComplete(t => t.Text = "welcome to osu!");
+                        welcomeText.TransformTo(nameof(welcomeText.Spacing), new Vector2(50, 0), 5000);
+                    }
+
+                    using (BeginDelayedSequence(text_glitch, true))
+                        triangles.FadeIn();
+
+                    using (BeginDelayedSequence(rulesets_1, true))
+                    {
+                        rulesetsScale.ScaleTo(0.8f, 1000);
+                        rulesets.FadeIn().ScaleTo(1).TransformSpacingTo(new Vector2(200, 0));
+                        welcomeText.FadeOut();
+                        triangles.FadeOut();
+                    }
+
+                    using (BeginDelayedSequence(rulesets_2, true))
+                    {
+                        rulesets.ScaleTo(2).TransformSpacingTo(new Vector2(30, 0));
+                    }
+
+                    using (BeginDelayedSequence(rulesets_3, true))
+                    {
+                        rulesets.ScaleTo(4).TransformSpacingTo(new Vector2(10, 0));
+                        rulesetsScale.ScaleTo(1.3f, 1000);
+                    }
+
+                    using (BeginDelayedSequence(logo_1, true))
+                    {
+                        rulesets.FadeOut();
+
+                        // matching flyte curve y = 0.25x^2 + (max(0, x - 0.7) / 0.3) ^ 5
+                        logoContainer.FadeIn().ScaleTo(scale_start).Then().Delay(logo_scale_duration * 0.7f).ScaleTo(scale_start - scale_adjust, logo_scale_duration * 0.3f, Easing.InQuint);
+                        logoContainerSecondary.ScaleTo(scale_start).Then().ScaleTo(scale_start - scale_adjust * 0.25f, logo_scale_duration, Easing.InQuad);
+                    }
+
+                    using (BeginDelayedSequence(logo_2, true))
+                    {
+                        logoContainer.FadeOut().OnComplete(_ =>
+                        {
+                            logo.FadeIn();
+                            background.FadeIn();
+
+                            game.Add(new GameWideFlash());
+
+                            LoadMenu();
+                        });
+                    }
+                }
+            }
+
+            private class GameWideFlash : Box
+            {
+                private const double flash_length = 1000;
+
+                public GameWideFlash()
+                {
+                    Colour = Color4.White;
+                    RelativeSizeAxes = Axes.Both;
+                    Blending = BlendingMode.Additive;
+                }
+
+                protected override void LoadComplete()
+                {
+                    base.LoadComplete();
+                    this.FadeOutFromOne(flash_length, Easing.Out);
+                }
+            }
+
+            private class LazerLogo : CompositeDrawable
+            {
+                public LazerLogo(Stream videoStream)
+                {
+                    Size = new Vector2(960);
+
+                    InternalChild = new VideoSprite(videoStream)
+                    {
+                        RelativeSizeAxes = Axes.Both,
+                        Clock = new FramedOffsetClock(Clock) { Offset = -logo_1 }
+                    };
+                }
+            }
+
+            private class RulesetFlow : FillFlowContainer
+            {
+                [BackgroundDependencyLoader]
+                private void load(RulesetStore rulesets)
+                {
+                    var modes = new List<Drawable>();
+
+                    foreach (var ruleset in rulesets.AvailableRulesets)
+                    {
+                        var icon = new ConstrainedIconContainer
+                        {
+                            Icon = ruleset.CreateInstance().CreateIcon(),
+                            Size = new Vector2(30),
+                        };
+
+                        modes.Add(icon);
+                    }
+
+                    AutoSizeAxes = Axes.Both;
+                    Children = modes;
+
+                    Anchor = Anchor.Centre;
+                    Origin = Anchor.Centre;
+                }
+            }
+
+            private class GlitchingTriangles : CompositeDrawable
+            {
+                public GlitchingTriangles()
+                {
+                    RelativeSizeAxes = Axes.Both;
+                }
+
+                private double? lastGenTime;
+
+                private const double time_between_triangles = 22;
+
+                protected override void Update()
+                {
+                    base.Update();
+
+                    if (lastGenTime == null || Time.Current - lastGenTime > time_between_triangles)
+                    {
+                        lastGenTime = (lastGenTime ?? Time.Current) + time_between_triangles;
+
+                        Drawable triangle = new OutlineTriangle(RNG.NextBool(), (RNG.NextSingle() + 0.2f) * 80)
+                        {
+                            RelativePositionAxes = Axes.Both,
+                            Position = new Vector2(RNG.NextSingle(), RNG.NextSingle()),
+                        };
+
+                        AddInternal(triangle);
+
+                        triangle.FadeOutFromOne(120);
+                    }
+                }
+
+                /// <summary>
+                /// Represents a sprite that is drawn in a triangle shape, instead of a rectangle shape.
+                /// </summary>
+                public class OutlineTriangle : BufferedContainer
+                {
+                    public OutlineTriangle(bool outlineOnly, float size)
+                    {
+                        Size = new Vector2(size);
+
+                        InternalChildren = new Drawable[]
+                        {
+                            new Triangle { RelativeSizeAxes = Axes.Both },
+                        };
+
+                        if (outlineOnly)
+                        {
+                            AddInternal(new Triangle
+                            {
+                                Anchor = Anchor.Centre,
+                                Origin = Anchor.Centre,
+                                Colour = Color4.Black,
+                                Size = new Vector2(size - 5),
+                                Blending = BlendingMode.None,
+                            });
+                        }
+
+                        Blending = BlendingMode.Additive;
+                        CacheDrawnFrameBuffer = true;
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/osu.Game/Users/User.cs b/osu.Game/Users/User.cs
index a5f3578711..9986f70557 100644
--- a/osu.Game/Users/User.cs
+++ b/osu.Game/Users/User.cs
@@ -20,6 +20,9 @@ namespace osu.Game.Users
         [JsonProperty(@"username")]
         public string Username;
 
+        [JsonProperty(@"previous_usernames")]
+        public string[] PreviousUsernames;
+
         [JsonProperty(@"country")]
         public Country Country;
 
diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj
index b5266fd75d..e149c4338d 100644
--- a/osu.Game/osu.Game.csproj
+++ b/osu.Game/osu.Game.csproj
@@ -15,7 +15,7 @@
     <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.6" />
     <PackageReference Include="Newtonsoft.Json" Version="12.0.2" />
     <PackageReference Include="ppy.osu.Game.Resources" Version="2019.809.0" />
-    <PackageReference Include="ppy.osu.Framework" Version="2019.809.0" />
+    <PackageReference Include="ppy.osu.Framework" Version="2019.813.0" />
     <PackageReference Include="SharpCompress" Version="0.23.0" />
     <PackageReference Include="NUnit" Version="3.12.0" />
     <PackageReference Include="SharpRaven" Version="2.4.0" />
diff --git a/osu.iOS.props b/osu.iOS.props
index 103d89cadc..26a8fa7647 100644
--- a/osu.iOS.props
+++ b/osu.iOS.props
@@ -105,12 +105,12 @@
     <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.1" />
     <PackageReference Include="Newtonsoft.Json" Version="12.0.1" />
     <PackageReference Include="ppy.osu.Game.Resources" Version="2019.809.0" />
-    <PackageReference Include="ppy.osu.Framework" Version="2019.809.0" />
-    <PackageReference Include="ppy.osu.Framework.iOS" Version="2019.809.0" />
+    <PackageReference Include="ppy.osu.Framework" Version="2019.813.0" />
+    <PackageReference Include="ppy.osu.Framework.iOS" Version="2019.813.0" />
     <PackageReference Include="SharpCompress" Version="0.22.0" />
     <PackageReference Include="NUnit" Version="3.11.0" />
     <PackageReference Include="SharpRaven" Version="2.4.0" />
     <PackageReference Include="System.ComponentModel.Annotations" Version="4.5.0" />
-    <PackageReference Include="ppy.osu.Framework.NativeLibs" Version="2019.307.0" ExcludeAssets="all" />
+    <PackageReference Include="ppy.osu.Framework.NativeLibs" Version="2019.813.0" ExcludeAssets="all" />
   </ItemGroup>
 </Project>