diff --git a/README.md b/README.md
index e114acb5c80cb62555d3371c56b59562ab1231ee..91613cdcdc36a486a997cb8bf97889457f64d73e 100644
--- a/README.md
+++ b/README.md
@@ -47,32 +47,69 @@ Instead of `BUILD_MODE=dist npm run build` you can also use `BUILD_MODE=dist npm
 * Follow the recipe above step by step and don't change the order. It is especially important to run npm first because installing after the linking will reset the linking.
 * Make sure the repositories are siblings on your machine's file tree.
 * If you have multiple Terminal tabs or windows open for the different Scratch repositories, make sure to use the same node version in all of them.
-* In the worst case unlink the repositories with `npm unlink` and start over.
+* In the worst case unlink the repositories by running `npm unlink` in both, and start over.
 
 ## Testing
-NOTE: If you're a windows user, please run these scripts in Windows `cmd.exe`  instead of Git Bash/MINGW64.
 
-Run linter, unit tests, build, and integration tests.
+### Documentation
+
+You may want to review the documentation for [Jest](https://facebook.github.io/jest/docs/en/api.html) and [Enzyme](http://airbnb.io/enzyme/docs/api/) as you write your tests.
+
+See [jest cli docs](https://facebook.github.io/jest/docs/en/cli.html#content) for more options.
+
+### Running tests
+
+*NOTE: If you're a windows user, please run these scripts in Windows `cmd.exe`  instead of Git Bash/MINGW64.*
+
+Before running any test, make sure you have run `npm install` from this (scratch-gui) repository's top level.
+
+#### Main testing command
+
+To run linter, unit tests, build, and integration tests, all at once:
 ```bash
 npm test
 ```
 
-Run unit tests in isolation.
+#### Running unit tests
+
+To run unit tests in isolation:
+```bash
+npm run test:unit
+```
+
+To run unit tests in watch mode (watches for code changes and continuously runs tests):
 ```bash
-npm run unit-test
+npm run test:unit -- --watch
 ```
 
-Run unit tests in watch mode (watches for code changes and continuously runs tests). See [jest cli docs](https://facebook.github.io/jest/docs/en/cli.html#content) for more options.
+#### Running integration tests
+
+Integration tests use a headless browser to manipulate the actual html and javascript that the repo
+produces. You will not see this activity (though you can hear it when sounds are played!).
+
+Note that integration tests require you to first create a build that can be loaded in a browser:
+
 ```bash
-npm run unit-test -- --watch
+npm run build
 ```
 
-Run integration tests in isolation.
+Then, you can run all integration tests:
+
 ```bash
-npm run integration-test
+npm run test:integration
 ```
 
-You may want to review the documentation for [Jest](https://facebook.github.io/jest/docs/en/api.html) and [Enzyme](http://airbnb.io/enzyme/docs/api/) as you write your tests.
+Or, you can run a single file of integration tests (in this example, the `backpack` tests):
+
+```bash
+$(npm bin)/jest --runInBand test/integration/backpack.test.js
+```
+
+If you want to watch the browser as it runs the test, rather than running headless, use:
+
+```bash
+USE_HEADLESS=no $(npm bin)/jest --runInBand test/integration/backpack.test.js
+```
 
 ## Publishing to GitHub Pages
 You can publish the GUI to github.io so that others on the Internet can view it.
diff --git a/package.json b/package.json
index e622d0e06b53fe506be4b077842cdef1b96617d3..27d80a6410f9beaea97855459de09ef70b8fcea4 100644
--- a/package.json
+++ b/package.json
@@ -98,10 +98,10 @@
     "scratch-audio": "0.1.0-prerelease.20180625202813",
     "scratch-blocks": "0.1.0-prerelease.1535662135",
     "scratch-l10n": "3.0.20180830210150",
-    "scratch-paint": "0.2.0-prerelease.20180905220723",
-    "scratch-render": "0.1.0-prerelease.20180824141819",
+    "scratch-paint": "0.2.0-prerelease.20180907144642",
+    "scratch-render": "0.1.0-prerelease.20180907144714",
     "scratch-storage": "1.0.2",
-    "scratch-svg-renderer": "0.2.0-prerelease.20180817005452",
+    "scratch-svg-renderer": "0.2.0-prerelease.20180907141232",
     "scratch-vm": "0.2.0-prerelease.20180907210751",
     "selenium-webdriver": "3.6.0",
     "startaudiocontext": "1.2.1",
diff --git a/src/components/gui/gui.jsx b/src/components/gui/gui.jsx
index 71c623135093df1f503ff64c93a57183eb33e761..aa1dfc0393388111d80c4179e21b7a0d62fa95f1 100644
--- a/src/components/gui/gui.jsx
+++ b/src/components/gui/gui.jsx
@@ -74,6 +74,7 @@ const GUIComponent = props => {
         onRequestCloseBackdropLibrary,
         onRequestCloseCostumeLibrary,
         onSeeCommunity,
+        onUpdateProjectTitle,
         previewInfoVisible,
         targetIsStage,
         soundsTabVisible,
@@ -147,6 +148,7 @@ const GUIComponent = props => {
                 <MenuBar
                     enableCommunity={enableCommunity}
                     onSeeCommunity={onSeeCommunity}
+                    onUpdateProjectTitle={onUpdateProjectTitle}
                 />
                 <Box className={styles.bodyWrapper}>
                     <Box className={styles.flexWrapper}>
@@ -294,6 +296,7 @@ GUIComponent.propTypes = {
     onRequestCloseCostumeLibrary: PropTypes.func,
     onSeeCommunity: PropTypes.func,
     onTabSelect: PropTypes.func,
+    onUpdateProjectTitle: PropTypes.func,
     previewInfoVisible: PropTypes.bool,
     soundsTabVisible: PropTypes.bool,
     stageSizeMode: PropTypes.oneOf(Object.keys(STAGE_SIZE_MODES)),
diff --git a/src/components/menu-bar/menu-bar.css b/src/components/menu-bar/menu-bar.css
index 662c0e1ae6fe2e403367d2d68194b81b83a0f409..f55fccb153abd7f9af4540cbc3999c84324541fb 100644
--- a/src/components/menu-bar/menu-bar.css
+++ b/src/components/menu-bar/menu-bar.css
@@ -109,22 +109,6 @@
     height: 34px;
 }
 
-.title-field {
-    border: 1px dashed $ui-black-transparent;
-    border-radius: .25rem;
-    width: 12rem;
-    height: 34px;
-    background-color: transparent;
-    padding: .5rem;
-}
-
-.title-field,
-.title-field::placeholder {
-    color: $ui-white;
-    font-weight: bold;
-    font-size: .8rem;
-}
-
 .share-button {
     background: $data-primary;
     height: 32px;
diff --git a/src/components/menu-bar/menu-bar.jsx b/src/components/menu-bar/menu-bar.jsx
index b1ccd29412e736cd5c39b6297e17ddaf3e190668..82b37b0b058c18380f74a5b017daada9e61a74b1 100644
--- a/src/components/menu-bar/menu-bar.jsx
+++ b/src/components/menu-bar/menu-bar.jsx
@@ -13,6 +13,7 @@ import LanguageSelector from '../../containers/language-selector.jsx';
 import ProjectLoader from '../../containers/project-loader.jsx';
 import Menu from '../../containers/menu.jsx';
 import {MenuItem, MenuSection} from '../menu/menu.jsx';
+import ProjectTitleInput from './project-title-input.jsx';
 import ProjectSaver from '../../containers/project-saver.jsx';
 import DeletionRestorer from '../../containers/deletion-restorer.jsx';
 import TurboMode from '../../containers/turbo-mode.jsx';
@@ -393,11 +394,12 @@ class MenuBar extends React.Component {
                     </div>
                     <Divider className={classNames(styles.divider)} />
                     <div className={classNames(styles.menuBarItem)}>
-                        <MenuBarItemTooltip id="title-field">
-                            <input
-                                disabled
-                                className={classNames(styles.titleField)}
-                                placeholder="Untitled-1"
+                        <MenuBarItemTooltip
+                            enable
+                            id="title-field"
+                        >
+                            <ProjectTitleInput
+                                onUpdateProjectTitle={this.props.onUpdateProjectTitle}
                             />
                         </MenuBarItemTooltip>
                     </div>
@@ -521,7 +523,8 @@ MenuBar.propTypes = {
     onRequestCloseEdit: PropTypes.func,
     onRequestCloseFile: PropTypes.func,
     onRequestCloseLanguage: PropTypes.func,
-    onSeeCommunity: PropTypes.func
+    onSeeCommunity: PropTypes.func,
+    onUpdateProjectTitle: PropTypes.func
 };
 
 const mapStateToProps = state => ({
diff --git a/src/components/menu-bar/project-title-input.css b/src/components/menu-bar/project-title-input.css
new file mode 100644
index 0000000000000000000000000000000000000000..84dcd0afcda19a459d091d4877ab52ea23bdf19c
--- /dev/null
+++ b/src/components/menu-bar/project-title-input.css
@@ -0,0 +1,44 @@
+@import "../../css/colors.css";
+@import "../../css/units.css";
+@import "../../css/z-index.css";
+
+$title-width: 12rem;
+
+.title-field {
+    border: 1px dashed $ui-black-transparent;
+    border-radius: $form-radius;
+    -webkit-border-radius: $form-radius;
+    -moz-border-radius: $form-radius;
+    background-color: $ui-white-transparent;
+    background-clip: padding-box;
+    -webkit-background-clip: padding-box;
+    width: $title-width;
+    height: auto;
+    padding: .5rem;
+}
+
+.title-field {
+    color: $ui-white;
+    font-weight: bold;
+    font-size: .8rem;
+}
+
+.title-field::placeholder {
+    color: $ui-white;
+    font-weight: normal;
+    font-size: .8rem;
+    font-style: italic;
+}
+
+.title-field:hover {
+    background-color: hsla(0, 100%, 100%, 0.5);
+}
+
+.title-field:focus {
+    outline:none;
+    border: 1px solid $ui-transparent;
+    -webkit-box-shadow: 0 0 0 calc($space * .5) $ui-white-transparent;
+    box-shadow: 0 0 0 calc($space * .5) $ui-white-transparent;
+    background-color: $ui-white;
+    color: $text-primary;
+}
diff --git a/src/components/menu-bar/project-title-input.jsx b/src/components/menu-bar/project-title-input.jsx
new file mode 100644
index 0000000000000000000000000000000000000000..8b927417fddb717b4ff2fdfdd948e0bf7222e873
--- /dev/null
+++ b/src/components/menu-bar/project-title-input.jsx
@@ -0,0 +1,66 @@
+import classNames from 'classnames';
+import {connect} from 'react-redux';
+import PropTypes from 'prop-types';
+import bindAll from 'lodash.bindall';
+import React from 'react';
+import {defineMessages, intlShape, injectIntl} from 'react-intl';
+
+import BufferedInputHOC from '../forms/buffered-input-hoc.jsx';
+import Input from '../forms/input.jsx';
+const BufferedInput = BufferedInputHOC(Input);
+
+import styles from './project-title-input.css';
+
+const messages = defineMessages({
+    projectTitlePlaceholder: {
+        id: 'gui.gui.projectTitlePlaceholder',
+        description: 'Placeholder for project title when blank',
+        defaultMessage: 'Project title here'
+    }
+});
+
+class ProjectTitleInput extends React.Component {
+    constructor (props) {
+        super(props);
+        bindAll(this, [
+            'handleUpdateProjectTitle'
+        ]);
+    }
+    // call onUpdateProjectTitle if it is defined (only defined when gui
+    // is used within scratch-www)
+    handleUpdateProjectTitle (newTitle) {
+        if (this.props.onUpdateProjectTitle) {
+            this.props.onUpdateProjectTitle(newTitle);
+        }
+    }
+    render () {
+        return (
+            <BufferedInput
+                className={classNames(styles.titleField)}
+                maxLength="100"
+                placeholder={this.props.intl.formatMessage(messages.projectTitlePlaceholder)}
+                tabIndex="0"
+                type="text"
+                value={this.props.projectTitle}
+                onSubmit={this.handleUpdateProjectTitle}
+            />
+        );
+    }
+}
+
+ProjectTitleInput.propTypes = {
+    intl: intlShape.isRequired,
+    onUpdateProjectTitle: PropTypes.func,
+    projectTitle: PropTypes.string
+};
+
+const mapStateToProps = state => ({
+    projectTitle: state.scratchGui.projectTitle
+});
+
+const mapDispatchToProps = () => ({});
+
+export default injectIntl(connect(
+    mapStateToProps,
+    mapDispatchToProps
+)(ProjectTitleInput));
diff --git a/src/components/prompt/prompt.jsx b/src/components/prompt/prompt.jsx
index 42de1599939801b8896374f032525be7c77982a9..78192f3adaac829916243929db569003bab283c0 100644
--- a/src/components/prompt/prompt.jsx
+++ b/src/components/prompt/prompt.jsx
@@ -48,6 +48,7 @@ const PromptComponent = props => (
                 <input
                     autoFocus
                     className={styles.variableNameTextInput}
+                    name={props.label}
                     placeholder={props.placeholder}
                     onChange={props.onChange}
                     onKeyPress={props.onKeyPress}
diff --git a/src/containers/gui.jsx b/src/containers/gui.jsx
index f08e6bab746dd171f040e48e916663bbf438538b..04c6142dce25e073c4f0a0676cd2ab3e5c37590d 100644
--- a/src/containers/gui.jsx
+++ b/src/containers/gui.jsx
@@ -7,6 +7,7 @@ import ReactModal from 'react-modal';
 
 import ErrorBoundaryHOC from '../lib/error-boundary-hoc.jsx';
 import {openExtensionLibrary} from '../reducers/modals';
+import {setProjectTitle} from '../reducers/project-title';
 import {
     activateTab,
     BLOCKS_TAB_INDEX,
@@ -34,6 +35,10 @@ class GUI extends React.Component {
         };
     }
     componentDidMount () {
+        if (this.props.projectTitle) {
+            this.props.onUpdateReduxProjectTitle(this.props.projectTitle);
+        }
+
         if (this.props.vm.initialized) return;
         this.audioEngine = new AudioEngine();
         this.props.vm.attachAudioEngine(this.audioEngine);
@@ -65,6 +70,9 @@ class GUI extends React.Component {
                     });
             });
         }
+        if (this.props.projectTitle !== nextProps.projectTitle) {
+            this.props.onUpdateReduxProjectTitle(nextProps.projectTitle);
+        }
     }
     render () {
         if (this.state.loadingError) {
@@ -75,8 +83,10 @@ class GUI extends React.Component {
             /* eslint-disable no-unused-vars */
             assetHost,
             hideIntro,
+            onUpdateReduxProjectTitle,
             projectData,
             projectHost,
+            projectTitle,
             /* eslint-enable no-unused-vars */
             children,
             fetchingProject,
@@ -97,14 +107,19 @@ class GUI extends React.Component {
 }
 
 GUI.propTypes = {
+    assetHost: PropTypes.string,
     children: PropTypes.node,
     fetchingProject: PropTypes.bool,
     hideIntro: PropTypes.bool,
     importInfoVisible: PropTypes.bool,
     loadingStateVisible: PropTypes.bool,
     onSeeCommunity: PropTypes.func,
+    onUpdateProjectTitle: PropTypes.func,
+    onUpdateReduxProjectTitle: PropTypes.func,
     previewInfoVisible: PropTypes.bool,
     projectData: PropTypes.oneOfType([PropTypes.object, PropTypes.string]),
+    projectHost: PropTypes.string,
+    projectTitle: PropTypes.string,
     vm: PropTypes.instanceOf(VM)
 };
 
@@ -134,7 +149,8 @@ const mapDispatchToProps = dispatch => ({
     onActivateCostumesTab: () => dispatch(activateTab(COSTUMES_TAB_INDEX)),
     onActivateSoundsTab: () => dispatch(activateTab(SOUNDS_TAB_INDEX)),
     onRequestCloseBackdropLibrary: () => dispatch(closeBackdropLibrary()),
-    onRequestCloseCostumeLibrary: () => dispatch(closeCostumeLibrary())
+    onRequestCloseCostumeLibrary: () => dispatch(closeCostumeLibrary()),
+    onUpdateReduxProjectTitle: title => dispatch(setProjectTitle(title))
 });
 
 const ConnectedGUI = connect(
diff --git a/src/containers/project-loader.jsx b/src/containers/project-loader.jsx
index 507b716523796e14fb9b73fbb93cca44df2785f8..66081b7a906871cef4a5230d9264cc434c2d3dcd 100644
--- a/src/containers/project-loader.jsx
+++ b/src/containers/project-loader.jsx
@@ -6,6 +6,7 @@ import {defineMessages, injectIntl, intlShape} from 'react-intl';
 
 import analytics from '../lib/analytics';
 import log from '../lib/log';
+import {setProjectTitle} from '../reducers/project-title';
 
 import {
     openLoadingProject,
@@ -75,6 +76,12 @@ class ProjectLoader extends React.Component {
         if (thisFileInput.files) { // Don't attempt to load if no file was selected
             this.props.openLoadingState();
             reader.readAsArrayBuffer(thisFileInput.files[0]);
+            if (thisFileInput.files[0].name) {
+                const matches = thisFileInput.files[0].name.match(/^(.*)\.sb3$/);
+                if (matches) {
+                    this.props.onSetProjectTitle(matches[1].substring(0, 100));
+                }
+            }
         }
     }
     handleClick () {
@@ -112,6 +119,7 @@ ProjectLoader.propTypes = {
     children: PropTypes.func,
     closeLoadingState: PropTypes.func,
     intl: intlShape.isRequired,
+    onSetProjectTitle: PropTypes.func,
     openLoadingState: PropTypes.func,
     vm: PropTypes.shape({
         loadProject: PropTypes.func
@@ -124,6 +132,7 @@ const mapStateToProps = state => ({
 
 const mapDispatchToProps = dispatch => ({
     closeLoadingState: () => dispatch(closeLoadingProject()),
+    onSetProjectTitle: title => dispatch(setProjectTitle(title)),
     openLoadingState: () => dispatch(openLoadingProject())
 });
 
diff --git a/src/containers/project-saver.jsx b/src/containers/project-saver.jsx
index 13be33da808b1fa2cf9e10c6d2db2644112ef2e0..c32d4b1bf0a045b6c20f9d9593cf5548aebf922b 100644
--- a/src/containers/project-saver.jsx
+++ b/src/containers/project-saver.jsx
@@ -3,6 +3,8 @@ import PropTypes from 'prop-types';
 import React from 'react';
 import {connect} from 'react-redux';
 import storage from '../lib/storage';
+import {projectTitleInitialState} from '../reducers/project-title';
+
 
 /**
  * Project saver component passes a saveProject function to its child.
@@ -33,21 +35,15 @@ class ProjectSaver extends React.Component {
         document.body.appendChild(saveLink);
 
         this.props.saveProjectSb3().then(content => {
-            // TODO user-friendly project name
-            // File name: project-DATE-TIME
-            const date = new Date();
-            const timestamp = `${date.toLocaleDateString()}-${date.toLocaleTimeString()}`;
-            const filename = `untitled-project-${timestamp}.sb3`;
-
             // Use special ms version if available to get it working on Edge.
             if (navigator.msSaveOrOpenBlob) {
-                navigator.msSaveOrOpenBlob(content, filename);
+                navigator.msSaveOrOpenBlob(content, this.props.projectFilename);
                 return;
             }
 
             const url = window.URL.createObjectURL(content);
             saveLink.href = url;
-            saveLink.download = filename;
+            saveLink.download = this.props.projectFilename;
             saveLink.click();
             window.URL.revokeObjectURL(url);
             document.body.removeChild(saveLink);
@@ -86,14 +82,24 @@ class ProjectSaver extends React.Component {
     }
 }
 
+const getProjectFilename = (curTitle, defaultTitle) => {
+    let filenameTitle = curTitle;
+    if (!filenameTitle || filenameTitle.length === 0) {
+        filenameTitle = defaultTitle;
+    }
+    return `${filenameTitle.substring(0, 100)}.sb3`;
+};
+
 ProjectSaver.propTypes = {
     children: PropTypes.func,
+    projectFilename: PropTypes.string,
     projectId: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
     saveProjectSb3: PropTypes.func
 };
 
 const mapStateToProps = state => ({
     saveProjectSb3: state.scratchGui.vm.saveProjectSb3.bind(state.scratchGui.vm),
+    projectFilename: getProjectFilename(state.scratchGui.projectTitle, projectTitleInitialState),
     projectId: state.scratchGui.projectId
 });
 
diff --git a/src/css/colors.css b/src/css/colors.css
index 37ffa62227a6f6ccb42a6170d4652db037f4f769..2222608922a9df36770fb8330fb663b2ea1e43c7 100644
--- a/src/css/colors.css
+++ b/src/css/colors.css
@@ -6,6 +6,7 @@ $ui-modal-overlay: hsla(215, 100%, 65%, 0.9); /* 90% transparent version of moti
 
 $ui-white: hsla(0, 100%, 100%, 1); /* #FFFFFF */
 $ui-white-transparent: hsla(0, 100%, 100%, 0.25); /* 25% transparent version of ui-white */
+$ui-transparent: hsla(0, 100%, 100%, 0); /* 25% transparent version of ui-white */
 
 $ui-black-transparent: hsla(0, 0%, 0%, 0.15); /* 15% transparent version of black */
 
diff --git a/src/lib/project-loader-hoc.jsx b/src/lib/project-loader-hoc.jsx
index eafd49cb32606437837a1e3837ae1812d9507f51..571f220fdea8786a24f8d557ae6c72fde38b37de 100644
--- a/src/lib/project-loader-hoc.jsx
+++ b/src/lib/project-loader-hoc.jsx
@@ -72,7 +72,6 @@ const ProjectLoaderHOC = function (WrappedComponent) {
                 assetHost,
                 projectHost,
                 projectId,
-                reduxProjectId,
                 setProjectId: setProjectIdProp,
                 /* eslint-enable no-unused-vars */
                 ...componentProps
diff --git a/src/lib/titled-hoc.jsx b/src/lib/titled-hoc.jsx
new file mode 100644
index 0000000000000000000000000000000000000000..2534d96bd61ba9f4cb4db89a51e33c031f1be6c4
--- /dev/null
+++ b/src/lib/titled-hoc.jsx
@@ -0,0 +1,54 @@
+import React from 'react';
+import bindAll from 'lodash.bindall';
+import {defineMessages, intlShape, injectIntl} from 'react-intl';
+
+const messages = defineMessages({
+    defaultProjectTitle: {
+        id: 'gui.gui.defaultProjectTitle',
+        description: 'Default title for project',
+        defaultMessage: 'Scratch Project'
+    }
+});
+
+/* Higher Order Component to get and set the project title
+ * @param {React.Component} WrappedComponent component to receive project title related props
+ * @returns {React.Component} component with project loading behavior
+ */
+const TitledHOC = function (WrappedComponent) {
+    class TitledComponent extends React.Component {
+        constructor (props) {
+            super(props);
+            bindAll(this, [
+                'handleUpdateProjectTitle'
+            ]);
+            this.state = {
+                projectTitle: this.props.intl.formatMessage(messages.defaultProjectTitle)
+            };
+        }
+        handleUpdateProjectTitle (newTitle) {
+            this.setState({projectTitle: newTitle});
+        }
+        render () {
+            return (
+                <WrappedComponent
+                    projectTitle={this.state.projectTitle}
+                    onUpdateProjectTitle={this.handleUpdateProjectTitle}
+                    {...this.props}
+                />
+            );
+        }
+    }
+
+    TitledComponent.propTypes = {
+        intl: intlShape.isRequired
+    };
+
+    // return TitledComponent;
+    const IntlTitledComponent = injectIntl(TitledComponent);
+    return IntlTitledComponent;
+
+};
+
+export {
+    TitledHOC as default
+};
diff --git a/src/playground/player.jsx b/src/playground/player.jsx
index 6c2833f1b8108d08022e9d6c85bac9357f1f497f..e236de667eb4b12919548908daad377dfa827fe9 100644
--- a/src/playground/player.jsx
+++ b/src/playground/player.jsx
@@ -8,6 +8,7 @@ import Box from '../components/box/box.jsx';
 import GUI from '../containers/gui.jsx';
 import HashParserHOC from '../lib/hash-parser-hoc.jsx';
 import AppStateHOC from '../lib/app-state-hoc.jsx';
+import TitledHOC from '../lib/titled-hoc.jsx';
 
 import {setPlayer} from '../reducers/mode';
 
@@ -48,7 +49,7 @@ const mapDispatchToProps = dispatch => ({
 });
 
 const ConnectedPlayer = connect(mapStateToProps, mapDispatchToProps)(Player);
-const WrappedPlayer = HashParserHOC(AppStateHOC(ConnectedPlayer));
+const WrappedPlayer = HashParserHOC(AppStateHOC(TitledHOC(ConnectedPlayer)));
 
 const appTarget = document.createElement('div');
 document.body.appendChild(appTarget);
diff --git a/src/playground/render-gui.jsx b/src/playground/render-gui.jsx
index adec922b8c206c9211888da34a8c426362b9a10b..562e57eac9c4c60ad4aea322adeec8b907067761 100644
--- a/src/playground/render-gui.jsx
+++ b/src/playground/render-gui.jsx
@@ -4,6 +4,7 @@ import ReactDOM from 'react-dom';
 import AppStateHOC from '../lib/app-state-hoc.jsx';
 import GUI from '../containers/gui.jsx';
 import HashParserHOC from '../lib/hash-parser-hoc.jsx';
+import TitledHOC from '../lib/titled-hoc.jsx';
 
 /*
  * Render the GUI playground. This is a separate function because importing anything
@@ -12,7 +13,7 @@ import HashParserHOC from '../lib/hash-parser-hoc.jsx';
  */
 export default appTarget => {
     GUI.setAppElement(appTarget);
-    const WrappedGui = HashParserHOC(AppStateHOC(GUI));
+    const WrappedGui = HashParserHOC(AppStateHOC(TitledHOC(GUI)));
 
     // TODO a hack for testing the backpack, allow backpack host to be set by url param
     const backpackHostMatches = window.location.href.match(/[?&]backpack_host=([^&]*)&?/);
diff --git a/src/reducers/gui.js b/src/reducers/gui.js
index 9340af56e1177485aa02c361c404070b6b4c6bf3..cfad8f6825ddad3a07a56b4c62757ffbc3a97909 100644
--- a/src/reducers/gui.js
+++ b/src/reducers/gui.js
@@ -12,6 +12,7 @@ import modeReducer, {modeInitialState} from './mode';
 import monitorReducer, {monitorsInitialState} from './monitors';
 import monitorLayoutReducer, {monitorLayoutInitialState} from './monitor-layout';
 import projectIdReducer, {projectIdInitialState} from './project-id';
+import projectTitleReducer, {projectTitleInitialState} from './project-title';
 import restoreDeletionReducer, {restoreDeletionInitialState} from './restore-deletion';
 import stageSizeReducer, {stageSizeInitialState} from './stage-size';
 import targetReducer, {targetsInitialState} from './targets';
@@ -37,6 +38,7 @@ const guiInitialState = {
     monitors: monitorsInitialState,
     monitorLayout: monitorLayoutInitialState,
     projectId: projectIdInitialState,
+    projectTitle: projectTitleInitialState,
     restoreDeletion: restoreDeletionInitialState,
     targets: targetsInitialState,
     toolbox: toolboxInitialState,
@@ -80,6 +82,7 @@ const guiReducer = combineReducers({
     monitors: monitorReducer,
     monitorLayout: monitorLayoutReducer,
     projectId: projectIdReducer,
+    projectTitle: projectTitleReducer,
     restoreDeletion: restoreDeletionReducer,
     targets: targetReducer,
     toolbox: toolboxReducer,
diff --git a/src/reducers/project-title.js b/src/reducers/project-title.js
new file mode 100644
index 0000000000000000000000000000000000000000..09bf6c4eab417e626b8a719326ac685c25d46cdd
--- /dev/null
+++ b/src/reducers/project-title.js
@@ -0,0 +1,25 @@
+const SET_PROJECT_TITLE = 'projectTitle/SET_PROJECT_TITLE';
+
+// we are initializing to a blank string instead of an actual title,
+// because it would be hard to localize here
+const initialState = '';
+
+const reducer = function (state, action) {
+    if (typeof state === 'undefined') state = initialState;
+    switch (action.type) {
+    case SET_PROJECT_TITLE:
+        return action.title;
+    default:
+        return state;
+    }
+};
+const setProjectTitle = title => ({
+    type: SET_PROJECT_TITLE,
+    title: title
+});
+
+export {
+    reducer as default,
+    initialState as projectTitleInitialState,
+    setProjectTitle
+};
diff --git a/test/integration/blocks.test.js b/test/integration/blocks.test.js
index 83b72620bcc04dad457f157071a54016f74381aa..b3ec62e2f6042c69ccb5f7ea985d8267e73da93e 100644
--- a/test/integration/blocks.test.js
+++ b/test/integration/blocks.test.js
@@ -68,11 +68,11 @@ describe('Working with the blocks', () => {
         await findByText('0', scope.reportedValue);
 
         await clickText('Make a Variable');
-        let el = await findByXpath("//input[@placeholder='']");
+        let el = await findByXpath("//input[@name='New variable name:']");
         await el.sendKeys('score');
         await clickButton('OK');
         await clickText('Make a Variable');
-        el = await findByXpath("//input[@placeholder='']");
+        el = await findByXpath("//input[@name='New variable name:']");
         await el.sendKeys('second variable');
         await clickButton('OK');
 
@@ -98,7 +98,7 @@ describe('Working with the blocks', () => {
         await new Promise(resolve => setTimeout(resolve, 1000)); // Wait for scroll animation
 
         await clickText('Make a List');
-        let el = await findByXpath("//input[@placeholder='']");
+        let el = await findByXpath("//input[@name='New list name:']");
         await el.sendKeys('list1');
         await clickButton('OK');
 
diff --git a/test/integration/examples.test.js b/test/integration/examples.test.js
index ecaeb56f2fa36f9bd83095563395f72a7759426f..8408c88f39bc949ef360095c1997252acf35428f 100644
--- a/test/integration/examples.test.js
+++ b/test/integration/examples.test.js
@@ -87,11 +87,11 @@ describe('blocks example', () => {
         await clickText('Variables');
         await new Promise(resolve => setTimeout(resolve, 1000)); // Wait for scroll animation
         await clickText('Make a Variable');
-        let el = await findByXpath("//input[@placeholder='']");
+        let el = await findByXpath("//input[@name='New variable name:']");
         await el.sendKeys('score');
         await clickButton('OK');
         await clickText('Make a Variable');
-        el = await findByXpath("//input[@placeholder='']");
+        el = await findByXpath("//input[@name='New variable name:']");
         await el.sendKeys('second variable');
         await clickButton('OK');
         const logs = await getLogs();