diff --git a/client/src/tutorial.js b/client/src/tutorial.js
new file mode 100644
index 00000000..35a84402
--- /dev/null
+++ b/client/src/tutorial.js
@@ -0,0 +1,134 @@
+const toast = require('izitoast');
+
+const OK_BUTTON = '';
+const NO_MORE_BUTTON = '';
+
+function noMore(instance, thisToast) {
+ window.localStorage.set('tutorial', false);
+ return instance.hide({ transitionOut: 'fadeOut' }, thisToast);
+}
+
+const WELCOME_MESSAGE = `
+Welcome to cryps.gg
+Enter a username and password and press register to sign up,
+or just press DEMO to quick start.
+`;
+
+const HOMEPAGE_MESSAGE = `
+This homepage shows your cryps, joinable online games, PVE options and your items.\n
+If you have no cryps yet, press SPAWN and give your cryp a name to create one.
+Once you have made a cryp, click on them to visit their stat page and teach them some SKILLS.
+The stat page also has descriptions of each skill and their effects.
+cryps have 3 basic stats: stamina, physical damage and magic damage.
+Toggle whether a cryp is selected for your team by clicking the coloured stripes next to the cryp or press 1,2,3.
+Once you have a team ready press the New PVE Game button to start playing.
+`;
+
+const SKILL_PHASE_MESSAGE = `
+A cryps battle has three main phases. This first phase is called the SKILL PHASE.
+Your cryps are positioned on the left, your opponent's are on the right.
+In the centre are your cryps' SKILLS, grayed out SKILLS are currently ON COOLDOWN.
+A skill's cooldown reduces on every turn that cryp does not use a skill with a cooldown.
+For the moment, drag ATTACK onto the opponent team to have your cryps attack them with physical damage.
+`;
+
+const TARGET_PHASE_MESSAGE = `
+This phase is the TARGET PHASE.
+In cryps you do not directly attack your opponent's cryps, you attack the opponent as a team
+and you and your opponent choose which cryp is the TARGET of each ability.
+Drag the incoming ATTACKS from the right hand side onto your own cryps.
+It's wise to spread the damage around!
+`;
+
+const RESOLUTION_PHASE_MESSAGE = `
+Finally we come to the RESOLUTION PHASE.
+This phase happens automatically, every skill is RESOLVED in order of its SPEED.
+This is important because attacks only RESOVLE while their caster is still able to use the skill,
+a fast skill that does a small amount of damage may KO an opponent cryp, causing any SKILLS
+they have used to no longer RESOLVE!
+Another example of this is the skill STUN. STUN causes an opponent cryp to be unable to use any
+abilities for TWO TURNS (including the turn it resolves on).
+Try it now!
+`;
+
+const FINISH_PHASE_MESSAGE = `
+gg! The game has now concluded, if you were the winner you have been awarded with a STAT REROLL ITEM.
+You can use this to reroll a stat on a cryp which may be lacking.
+A good metric is that if a stat is more than 1/2 of its STAMINA that's a good roll.
+Now that you have learned the basics, press BACKSPACE to return to the main menu
+and experiment with some combinations of SKILLS or replace the ones your cryps know in the STAT PAGE.
+glhf!
+`;
+
+const STEPS = [
+ 'init',
+ 'welcome',
+ 'homepage',
+ 'skillPhase',
+ 'targetPhase',
+ 'resolutionPhase',
+ 'finishPhase',
+ 'none',
+];
+
+function showTutorial(message, step, nextStep) {
+ const existing = document.querySelector(`#${step}`); // Selector of your toast
+
+ if (existing) return false;
+
+ toast.info({
+ id: step,
+ theme: 'dark',
+ color: 'black',
+ timeout: false,
+ drag: false,
+ title: 'TUTORIAL',
+ position: 'bottomCenter',
+ maxWidth: window.innerWidth / 2,
+ close: false,
+ buttons: [
+ [NO_MORE_BUTTON, noMore],
+ [OK_BUTTON, (instance, thisToast) => {
+ const thisStep = STEPS.indexOf(step);
+ const storageStep = STEPS.indexOf(window.localStorage.getItem('tutorial'));
+ if (thisStep >= storageStep) {
+ window.localStorage.setItem('tutorial', nextStep);
+ }
+
+ return instance.hide({ transitionOut: 'fadeOut' }, thisToast);
+ }],
+ ],
+ message,
+ });
+
+ return true;
+}
+
+function tutorial() {
+ function show(step) {
+ const currentStage = window.localStorage.getItem('tutorial');
+ if (currentStage === false) {
+ return false;
+ }
+
+ const thisStep = STEPS.indexOf(step);
+ const storageStep = STEPS.indexOf(window.localStorage.getItem('tutorial'));
+
+ if (thisStep < storageStep) return false;
+
+ if (step === 'welcome') return showTutorial(WELCOME_MESSAGE, 'welcome', 'homepage');
+ if (step === 'homepage') return showTutorial(HOMEPAGE_MESSAGE, 'homepage', 'skillPhase');
+ if (step === 'skillPhase') return showTutorial(SKILL_PHASE_MESSAGE, 'skillPhase', 'targetPhase');
+ if (step === 'targetPhase') return showTutorial(TARGET_PHASE_MESSAGE, 'targetPhase', 'resolutionPhase');
+ if (step === 'resolutionPhase') return showTutorial(RESOLUTION_PHASE_MESSAGE, 'resolutionPhase', 'finishPhase');
+ if (step === 'finishPhase') return showTutorial(FINISH_PHASE_MESSAGE, 'finishPhase', 'none');
+
+ return true;
+ }
+
+ if (window.localStorage.getItem('tutorial') !== 'none') window.localStorage.setItem('tutorial', 'init');
+
+ return show;
+}
+
+module.exports = tutorial;