diff --git a/src/components/gui/gui.jsx b/src/components/gui/gui.jsx
index 5c7a378dc43c44ff9f3d5d37e3b9ae79ffbbe87f..b7f2a0acd1d73abf9f683dd8c7d3505394c8a441 100644
--- a/src/components/gui/gui.jsx
+++ b/src/components/gui/gui.jsx
@@ -14,7 +14,7 @@ import TargetPane from '../../containers/target-pane.jsx';
 import SoundTab from '../../containers/sound-tab.jsx';
 import StageHeader from '../../containers/stage-header.jsx';
 import Stage from '../../containers/stage.jsx';
-
+import Loader from '../loader/loader.jsx';
 import Box from '../box/box.jsx';
 import FeedbackForm from '../feedback-form/feedback-form.jsx';
 import MenuBar from '../menu-bar/menu-bar.jsx';
@@ -44,6 +44,7 @@ const GUIComponent = props => {
         feedbackFormVisible,
         importInfoVisible,
         intl,
+        loading,
         onExtensionButtonClick,
         onActivateTab,
         previewInfoVisible,
@@ -78,6 +79,9 @@ const GUIComponent = props => {
             {previewInfoVisible ? (
                 <PreviewModal />
             ) : null}
+            {loading ? (
+                <Loader />
+            ) : null}
             {importInfoVisible ? (
                 <ImportModal />
             ) : null}
@@ -176,6 +180,7 @@ GUIComponent.propTypes = {
     feedbackFormVisible: PropTypes.bool,
     importInfoVisible: PropTypes.bool,
     intl: intlShape.isRequired,
+    loading: PropTypes.bool,
     onActivateTab: PropTypes.func,
     onExtensionButtonClick: PropTypes.func,
     onTabSelect: PropTypes.func,
diff --git a/src/components/loader/bottom-block.svg b/src/components/loader/bottom-block.svg
new file mode 100644
index 0000000000000000000000000000000000000000..b397068c448eeabc04d4245e1f5cdd210b72df5c
Binary files /dev/null and b/src/components/loader/bottom-block.svg differ
diff --git a/src/components/loader/loader.css b/src/components/loader/loader.css
new file mode 100644
index 0000000000000000000000000000000000000000..68f8e5f4f08f852f5f1ac3c968d30e5c33729a8e
--- /dev/null
+++ b/src/components/loader/loader.css
@@ -0,0 +1,89 @@
+@import "../../css/colors.css";
+
+.background {
+    position: fixed;
+    top: 0;
+    left: 0;
+    width: 100%;
+    height: 100%;
+    z-index: 999; /* Below preview modal */
+    display: flex;
+    justify-content: center;
+    align-items: center;
+    background-color: $motion-primary;
+    font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
+    text-align: center;
+    color: white;
+}
+
+.block-animation {
+    width: 125px;
+    height: 150px;
+    margin: 50px auto 0px;
+}
+
+.block-animation img {
+    display: block;
+    position: relative;
+    height: 30%;
+    margin-top: -4px;
+}
+
+.topBlock {
+    animation: top-slide-in 1.5s ease infinite;
+}
+
+.middleBlock {
+    animation: middle-slide-in 1.5s ease infinite;
+}
+
+.bottomBlock {
+    animation: bottom-slide-in 1.5s ease infinite;
+}
+
+
+@keyframes top-slide-in {
+  0% {
+    transform: translateY(50px);
+    opacity: 0;
+  }
+
+  33% {
+    transform: translateY(0px);
+    opacity: 1;
+  }
+}
+
+@keyframes middle-slide-in {
+  0% {
+    transform: translateY(50px);
+    opacity: 0;
+  }
+
+  33% {
+    transform: translateY(50px);
+    opacity: 0;
+  }
+
+  66% {
+    transform: translateY(0px);
+    opacity: 1;
+  }
+}
+
+@keyframes bottom-slide-in {
+  0% {
+    transform: translateY(50px);
+    opacity: 0;
+  }
+
+  66% {
+    transform: translateY(50px);
+    opacity: 0;
+  }
+
+  100% {
+    transform: translateY(0px);
+    opacity: 1;
+  }
+}
diff --git a/src/components/loader/loader.jsx b/src/components/loader/loader.jsx
new file mode 100644
index 0000000000000000000000000000000000000000..8a93e5c36abd385665c9fcdd1374f30096d9c0a8
--- /dev/null
+++ b/src/components/loader/loader.jsx
@@ -0,0 +1,144 @@
+import React from 'react';
+import {FormattedMessage} from 'react-intl';
+import styles from './loader.css';
+
+import topBlock from './top-block.svg';
+import middleBlock from './middle-block.svg';
+import bottomBlock from './bottom-block.svg';
+
+const LoaderComponent = () => {
+    const messages = [
+        {
+            message: (
+                <FormattedMessage
+                    defaultMessage="Creating blocks …"
+                    description="One of the loading messages"
+                    id="gui.loader.message1"
+                />
+            ),
+            weight: 50
+        },
+        {
+            message: (
+                <FormattedMessage
+                    defaultMessage="Loading sprites …"
+                    description="One of the loading messages"
+                    id="gui.loader.message2"
+                />
+            ),
+            weight: 50
+        },
+        {
+            message: (
+                <FormattedMessage
+                    defaultMessage="Loading sounds …"
+                    description="One of the loading messages"
+                    id="gui.loader.message3"
+                />
+            ),
+            weight: 50
+        },
+        {
+            message: (
+                <FormattedMessage
+                    defaultMessage="Loading extensions …"
+                    description="One of the loading messages"
+                    id="gui.loader.message4"
+                />
+            ),
+            weight: 50
+        },
+        {
+            message: (
+                <FormattedMessage
+                    defaultMessage="Creating blocks …"
+                    description="One of the loading messages"
+                    id="gui.loader.message1"
+                />
+            ),
+            weight: 20
+        },
+        {
+            message: (
+                <FormattedMessage
+                    defaultMessage="Herding cats …"
+                    description="One of the loading messages"
+                    id="gui.loader.message5"
+                />
+            ),
+            weight: 1
+        },
+        {
+            message: (
+                <FormattedMessage
+                    defaultMessage="Transmitting nanos …"
+                    description="One of the loading messages"
+                    id="gui.loader.message6"
+                />
+            ),
+            weight: 1
+        },
+        {
+            message: (
+                <FormattedMessage
+                    defaultMessage="Inflating gobos …"
+                    description="One of the loading messages"
+                    id="gui.loader.message7"
+                />
+            ),
+            weight: 1
+        },
+        {
+            message: (
+                <FormattedMessage
+                    defaultMessage="Preparing emojiis …"
+                    description="One of the loading messages"
+                    id="gui.loader.message8"
+                />
+            ),
+            weight: 1
+        }
+    ];
+
+    let message;
+    const sum = messages.reduce((acc, m) => acc + m.weight, 0);
+    let rand = sum * Math.random();
+    for (let i = 0; i < messages.length; i++) {
+        rand -= messages[i].weight;
+        if (rand <= 0) {
+            message = messages[i].message;
+            break;
+        }
+    }
+
+    return (
+        <div className={styles.background}>
+            <div className={styles.container}>
+                <div className={styles.blockAnimation}>
+                    <img
+                        className={styles.topBlock}
+                        src={topBlock}
+                    />
+                    <img
+                        className={styles.middleBlock}
+                        src={middleBlock}
+                    />
+                    <img
+                        className={styles.bottomBlock}
+                        src={bottomBlock}
+                    />
+                </div>
+                <h1 className={styles.title}>
+                    <FormattedMessage
+                        defaultMessage="Loading Project"
+                        description="Main loading message"
+                        id="gui.loader.headline"
+                    />
+                </h1>
+                <p>{message}</p>
+            </div>
+        </div>
+    );
+};
+
+export default LoaderComponent;
diff --git a/src/components/loader/middle-block.svg b/src/components/loader/middle-block.svg
new file mode 100644
index 0000000000000000000000000000000000000000..6e52d4213a9e8bfff2c8a0289fa324cfcbd60c27
Binary files /dev/null and b/src/components/loader/middle-block.svg differ
diff --git a/src/components/loader/top-block.svg b/src/components/loader/top-block.svg
new file mode 100644
index 0000000000000000000000000000000000000000..2b52a0d5d09684056643fb04aec7c80967569c95
Binary files /dev/null and b/src/components/loader/top-block.svg differ
diff --git a/src/containers/gui.jsx b/src/containers/gui.jsx
index de4fe27a284623c4058f07e62da59d32fd378643..320eedcdb6661a076d39bea33892c9e4a0deefa4 100644
--- a/src/containers/gui.jsx
+++ b/src/containers/gui.jsx
@@ -17,16 +17,29 @@ import vmListenerHOC from '../lib/vm-listener-hoc.jsx';
 import GUIComponent from '../components/gui/gui.jsx';
 
 class GUI extends React.Component {
+    constructor (props) {
+        super(props);
+        this.state = {
+            loading: true
+        };
+    }
     componentDidMount () {
         this.audioEngine = new AudioEngine();
         this.props.vm.attachAudioEngine(this.audioEngine);
-        this.props.vm.loadProject(this.props.projectData);
-        this.props.vm.setCompatibilityMode(true);
-        this.props.vm.start();
+        this.props.vm.loadProject(this.props.projectData).then(() => {
+            this.setState({loading: false}, () => {
+                this.props.vm.setCompatibilityMode(true);
+                this.props.vm.start();
+            });
+        });
     }
     componentWillReceiveProps (nextProps) {
         if (this.props.projectData !== nextProps.projectData) {
-            this.props.vm.loadProject(nextProps.projectData);
+            this.setState({loading: true}, () => {
+                this.props.vm.loadProject(nextProps.projectData).then(() => {
+                    this.setState({loading: false});
+                });
+            });
         }
     }
     componentWillUnmount () {
@@ -35,12 +48,14 @@ class GUI extends React.Component {
     render () {
         const {
             children,
+            fetchingProject,
             projectData, // eslint-disable-line no-unused-vars
             vm,
             ...componentProps
         } = this.props;
         return (
             <GUIComponent
+                loading={fetchingProject || this.state.loading}
                 vm={vm}
                 {...componentProps}
             >
@@ -53,6 +68,7 @@ class GUI extends React.Component {
 GUI.propTypes = {
     ...GUIComponent.propTypes,
     feedbackFormVisible: PropTypes.bool,
+    fetchingProject: PropTypes.bool,
     importInfoVisible: PropTypes.bool,
     previewInfoVisible: PropTypes.bool,
     projectData: PropTypes.string,
diff --git a/src/lib/project-loader-hoc.jsx b/src/lib/project-loader-hoc.jsx
index 854928c7eaa2720dff7150c586faf9e179d90116..2c20b9fed50736b490643d02ca40c06f11a051a0 100644
--- a/src/lib/project-loader-hoc.jsx
+++ b/src/lib/project-loader-hoc.jsx
@@ -17,21 +17,25 @@ const ProjectLoaderHOC = function (WrappedComponent) {
             this.updateProject = this.updateProject.bind(this);
             this.state = {
                 projectId: null,
-                projectData: null
+                projectData: null,
+                fetchingProject: false
             };
         }
         componentDidMount () {
             window.addEventListener('hashchange', this.updateProject);
             this.updateProject();
         }
-        componentDidUpdate (prevProps, prevState) {
-            if (this.state.projectId !== prevState.projectId) {
-                storage
-                    .load(storage.AssetType.Project, this.state.projectId, storage.DataFormat.JSON)
-                    .then(projectAsset => projectAsset && this.setState({
-                        projectData: projectAsset.data.toString()
-                    }))
-                    .catch(err => log.error(err));
+        componentWillUpdate (nextProps, nextState) {
+            if (this.state.projectId !== nextState.projectId) {
+                this.setState({fetchingProject: true}, () => {
+                    storage
+                        .load(storage.AssetType.Project, this.state.projectId, storage.DataFormat.JSON)
+                        .then(projectAsset => projectAsset && this.setState({
+                            projectData: projectAsset.data.toString(),
+                            fetchingProject: false
+                        }))
+                        .catch(err => log.error(err));
+                });
             }
         }
         componentWillUnmount () {
@@ -60,6 +64,7 @@ const ProjectLoaderHOC = function (WrappedComponent) {
             if (!this.state.projectData) return null;
             return (
                 <WrappedComponent
+                    fetchingProject={this.state.fetchingProject}
                     projectData={this.state.projectData}
                     {...this.props}
                 />