diff --git a/.npmignore b/.npmignore
index 61247064a407888b80af36682d29f7f93d687143..dee8a6a5d92f38f72bce5e6383a990b01a5fd28d 100644
--- a/.npmignore
+++ b/.npmignore
@@ -5,6 +5,9 @@
 /node_modules
 npm-*
 
+# Double copies of all the static assets and tutorial gifs
+/src
+
 # Testing
 /.nyc_output
 /coverage
diff --git a/.travis.yml b/.travis.yml
index 38af9f8b26444e4ca3701e262f9fdd1038bfd879..2a9152628ca2b0ac80edcd6226517556a2282111 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -57,6 +57,7 @@ deploy:
     - master
     - develop
     - smoke
+    - hotfix/*
     condition: $TRAVIS_EVENT_TYPE != cron
   skip_cleanup: true
   script: if npm info scratch-gui | grep -q $RELEASE_VERSION; then git tag $RELEASE_VERSION && git push https://${GH_TOKEN}@github.com/${TRAVIS_REPO_SLUG}.git $RELEASE_VERSION; fi
diff --git a/package-lock.json b/package-lock.json
index 9d1229decad3e5335764816f6149dc96497d95cd..dd51feeb208bf336b7cf863b107f9aa9711b7aa6 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -2699,9 +2699,9 @@
       }
     },
     "chromedriver": {
-      "version": "77.0.0",
-      "resolved": "https://registry.npmjs.org/chromedriver/-/chromedriver-77.0.0.tgz",
-      "integrity": "sha512-mZa1IVx4HD8rDaItWbnS470mmypgiWsDiu98r0NkiT4uLm3qrANl4vOU6no6vtWtLQiW5kt1POcIbjeNpsLbXA==",
+      "version": "78.0.1",
+      "resolved": "https://registry.npmjs.org/chromedriver/-/chromedriver-78.0.1.tgz",
+      "integrity": "sha512-eOsyFk4xb9EECs1VMrDbxO713qN+Bu1XUE8K9AuePc3839TPdAegg72kpXSzkeNqRNZiHbnJUItIVCLFkDqceA==",
       "dev": true,
       "requires": {
         "del": "^4.1.1",
@@ -11460,9 +11460,9 @@
       }
     },
     "scratch-blocks": {
-      "version": "0.1.0-prerelease.1570730963",
-      "resolved": "https://registry.npmjs.org/scratch-blocks/-/scratch-blocks-0.1.0-prerelease.1570730963.tgz",
-      "integrity": "sha512-4rTLas2jImF/H3WE93CXwBYGuzVXgiLfF654EL96HXxViuBDNg35UE5VAyextKMxj4AGGULoVdnZaGIV9AfrjQ==",
+      "version": "0.1.0-prerelease.1572384380",
+      "resolved": "https://registry.npmjs.org/scratch-blocks/-/scratch-blocks-0.1.0-prerelease.1572384380.tgz",
+      "integrity": "sha512-DTmX9wcShFV/cFNQt1rmexttJIe4hr11nz/omVKLovRsVUKaKJqSxpwbfjduqI9lj/xzCgYzfpC0EtjZFZmbRQ==",
       "dev": true,
       "requires": {
         "exports-loader": "0.6.3",
@@ -11470,9 +11470,9 @@
       }
     },
     "scratch-l10n": {
-      "version": "3.6.20191008224547",
-      "resolved": "https://registry.npmjs.org/scratch-l10n/-/scratch-l10n-3.6.20191008224547.tgz",
-      "integrity": "sha512-J6AgB6oB+/ibamUNeyTdtSvkPzJchiD50xoUqzzlcPOv+AWOFSTOakGPzJnGE6/WeZ0njBThvy1rh23lamqiow==",
+      "version": "3.6.20191029224007",
+      "resolved": "https://registry.npmjs.org/scratch-l10n/-/scratch-l10n-3.6.20191029224007.tgz",
+      "integrity": "sha512-12LprZ35Z7j/h14CtmU0ZW+aHwOUme58bwIpF/dINil9bbUhasLzGd1Ov60y3eOmP0LO+EE4oebUbK6KV1rguQ==",
       "dev": true,
       "requires": {
         "@babel/cli": "^7.1.2",
@@ -11482,9 +11482,9 @@
       }
     },
     "scratch-paint": {
-      "version": "0.2.0-prerelease.20191010161144",
-      "resolved": "https://registry.npmjs.org/scratch-paint/-/scratch-paint-0.2.0-prerelease.20191010161144.tgz",
-      "integrity": "sha512-mItiFbEOWMblwJnFjbM1VBp5bGGkGCM4uc4fM2ej40hjQotD2SAhHfGPHhLRhtk3vEq+mcyq4iJfJo2h3Hqmkg==",
+      "version": "0.2.0-prerelease.20191104214909",
+      "resolved": "https://registry.npmjs.org/scratch-paint/-/scratch-paint-0.2.0-prerelease.20191104214909.tgz",
+      "integrity": "sha512-MYGnFgK0EVQn26yy01fZhjDvVHc/jjjzg19/7PF1sTHnligflTMeticRv3AesqjjVZzUbWmUdswQAnfDHb54Wg==",
       "dev": true,
       "requires": {
         "@scratch/paper": "0.11.20190729152410",
@@ -11495,7 +11495,7 @@
         "minilog": "3.1.0",
         "parse-color": "1.0.0",
         "prop-types": "^15.5.10",
-        "scratch-svg-renderer": "0.2.0-prerelease.20190822202608"
+        "scratch-svg-renderer": "0.2.0-prerelease.20191104164753"
       },
       "dependencies": {
         "classnames": {
@@ -11597,9 +11597,9 @@
       }
     },
     "scratch-render": {
-      "version": "0.1.0-prerelease.20191001200908",
-      "resolved": "https://registry.npmjs.org/scratch-render/-/scratch-render-0.1.0-prerelease.20191001200908.tgz",
-      "integrity": "sha512-9vgCM/C44Pg6zd3jnRZXGaxytlrXSlI7lYgXfllCiSgMxfuBs3pgQKmEFQFj4gwBYs3mykp+j6WFpqU09hAwwA==",
+      "version": "0.1.0-prerelease.20191104214419",
+      "resolved": "https://registry.npmjs.org/scratch-render/-/scratch-render-0.1.0-prerelease.20191104214419.tgz",
+      "integrity": "sha512-EmXRP5nyY/gayC/Gb02ZQ6fvqOQs9C8fDoaO05WLEIjLa2QRQHVaueOM6FO00p04KeNh98LMaNcqYOeqv7f9kg==",
       "dev": true,
       "requires": {
         "grapheme-breaker": "0.3.2",
@@ -11609,7 +11609,7 @@
         "minilog": "3.1.0",
         "raw-loader": "^0.5.1",
         "scratch-storage": "^1.0.0",
-        "scratch-svg-renderer": "0.2.0-prerelease.20190822202608",
+        "scratch-svg-renderer": "0.2.0-prerelease.20191104164753",
         "twgl.js": "4.4.0"
       }
     },
@@ -11657,9 +11657,9 @@
       }
     },
     "scratch-svg-renderer": {
-      "version": "0.2.0-prerelease.20190822202608",
-      "resolved": "https://registry.npmjs.org/scratch-svg-renderer/-/scratch-svg-renderer-0.2.0-prerelease.20190822202608.tgz",
-      "integrity": "sha512-7y1+XJW+1GrqWxV8c91Cla8sbKL18YKnqNplg3enfQKy7UhZ4G+HJVhFH/SbTqdWW5qpj02eAfgGwBHwvy7tng==",
+      "version": "0.2.0-prerelease.20191104164753",
+      "resolved": "https://registry.npmjs.org/scratch-svg-renderer/-/scratch-svg-renderer-0.2.0-prerelease.20191104164753.tgz",
+      "integrity": "sha512-K++VXmapGZ9pxlBT8KNHQqOQQ5xfXR3K7Wocj8G+Ap8CJ/qxfirvorGCpVW7KrdHj912a1Logw1Pwx+16/fl7A==",
       "dev": true,
       "requires": {
         "base64-js": "1.2.1",
diff --git a/package.json b/package.json
index de3cfdeff634a40ac25df6cc5de20b94fc45e870..121d3432add1b5f2b41b077128cbaa7af0184b5f 100644
--- a/package.json
+++ b/package.json
@@ -44,7 +44,7 @@
     "babel-loader": "^8.0.4",
     "base64-loader": "1.0.0",
     "bowser": "1.9.4",
-    "chromedriver": "77.0.0",
+    "chromedriver": "78.0.1",
     "classnames": "2.2.6",
     "computed-style-to-inline-style": "3.0.0",
     "copy-webpack-plugin": "^4.5.1",
@@ -108,12 +108,12 @@
     "redux-throttle": "0.1.1",
     "rimraf": "^2.6.1",
     "scratch-audio": "0.1.0-prerelease.20190925183642",
-    "scratch-l10n": "3.6.20191008224547",
-    "scratch-blocks": "0.1.0-prerelease.1570730963",
-    "scratch-paint": "0.2.0-prerelease.20191010161144",
-    "scratch-render": "0.1.0-prerelease.20191001200908",
+    "scratch-l10n": "3.6.20191029224007",
+    "scratch-blocks": "0.1.0-prerelease.1572384380",
+    "scratch-paint": "0.2.0-prerelease.20191104214909",
+    "scratch-render": "0.1.0-prerelease.20191104214419",
     "scratch-storage": "1.3.2",
-    "scratch-svg-renderer": "0.2.0-prerelease.20190822202608",
+    "scratch-svg-renderer": "0.2.0-prerelease.20191104164753",
     "scratch-vm": "0.2.0-prerelease.20191018142546",
     "selenium-webdriver": "3.6.0",
     "startaudiocontext": "1.2.1",
diff --git a/src/components/gui/gui.jsx b/src/components/gui/gui.jsx
index 3c68eec90bf6d96b467f5bc18cf5790b0cce350e..9846eed7566356ed1e5395def9b59388bb870958 100644
--- a/src/components/gui/gui.jsx
+++ b/src/components/gui/gui.jsx
@@ -94,7 +94,6 @@ const GUIComponent = props => {
         onLogOut,
         onOpenRegistration,
         onToggleLoginOpen,
-        onUpdateProjectTitle,
         onActivateCostumesTab,
         onActivateSoundsTab,
         onActivateTab,
@@ -228,7 +227,6 @@ const GUIComponent = props => {
                     onSeeCommunity={onSeeCommunity}
                     onShare={onShare}
                     onToggleLoginOpen={onToggleLoginOpen}
-                    onUpdateProjectTitle={onUpdateProjectTitle}
                 />
                 <Box className={styles.bodyWrapper}>
                     <Box className={styles.flexWrapper}>
@@ -406,7 +404,6 @@ GUIComponent.propTypes = {
     onTelemetryModalOptIn: PropTypes.func,
     onTelemetryModalOptOut: PropTypes.func,
     onToggleLoginOpen: PropTypes.func,
-    onUpdateProjectTitle: PropTypes.func,
     renderLogin: PropTypes.func,
     showComingSoon: PropTypes.bool,
     soundsTabVisible: PropTypes.bool,
@@ -433,7 +430,6 @@ GUIComponent.defaultProps = {
     isCreating: false,
     isShared: false,
     loading: false,
-    onUpdateProjectTitle: () => {},
     showComingSoon: false,
     stageSizeMode: STAGE_SIZE_MODES.large
 };
diff --git a/src/components/menu-bar/menu-bar.jsx b/src/components/menu-bar/menu-bar.jsx
index 1fa7a01cc1974236104490d11086e64183850073..98bc128a92f11fe272d674513930e53f897d58bb 100644
--- a/src/components/menu-bar/menu-bar.jsx
+++ b/src/components/menu-bar/menu-bar.jsx
@@ -395,7 +395,6 @@ class MenuBar extends React.Component {
                                         <SBFileUploader
                                             canSave={this.props.canSave}
                                             userOwnsProject={this.props.userOwnsProject}
-                                            onUpdateProjectTitle={this.props.onUpdateProjectTitle}
                                         >
                                             {(className, renderFileInput, handleLoadProject) => (
                                                 <MenuItem
@@ -495,7 +494,6 @@ class MenuBar extends React.Component {
                             >
                                 <ProjectTitleInput
                                     className={classNames(styles.titleFieldGrowable)}
-                                    onUpdateProjectTitle={this.props.onUpdateProjectTitle}
                                 />
                             </MenuBarItemTooltip>
                         </div>
@@ -746,7 +744,6 @@ MenuBar.propTypes = {
     onSeeCommunity: PropTypes.func,
     onShare: PropTypes.func,
     onToggleLoginOpen: PropTypes.func,
-    onUpdateProjectTitle: PropTypes.func,
     projectTitle: PropTypes.string,
     renderLogin: PropTypes.func,
     sessionExists: PropTypes.bool,
diff --git a/src/components/menu-bar/project-title-input.jsx b/src/components/menu-bar/project-title-input.jsx
index b9b5aa6d8382e55c8483a4dde3290ae1ed6ad44a..fe7647cb0c9ae89002799ea5cbd11233431c7c40 100644
--- a/src/components/menu-bar/project-title-input.jsx
+++ b/src/components/menu-bar/project-title-input.jsx
@@ -1,9 +1,9 @@
 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 {setProjectTitle} from '../../reducers/project-title';
 
 import BufferedInputHOC from '../forms/buffered-input-hoc.jsx';
 import Input from '../forms/input.jsx';
@@ -19,39 +19,27 @@ const messages = defineMessages({
     }
 });
 
-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, this.props.className)}
-                maxLength="100"
-                placeholder={this.props.intl.formatMessage(messages.projectTitlePlaceholder)}
-                tabIndex="0"
-                type="text"
-                value={this.props.projectTitle}
-                onSubmit={this.handleUpdateProjectTitle}
-            />
-        );
-    }
-}
+const ProjectTitleInput = ({
+    className,
+    intl,
+    onSubmit,
+    projectTitle
+}) => (
+    <BufferedInput
+        className={classNames(styles.titleField, className)}
+        maxLength="100"
+        placeholder={intl.formatMessage(messages.projectTitlePlaceholder)}
+        tabIndex="0"
+        type="text"
+        value={projectTitle}
+        onSubmit={onSubmit}
+    />
+);
 
 ProjectTitleInput.propTypes = {
     className: PropTypes.string,
     intl: intlShape.isRequired,
-    onUpdateProjectTitle: PropTypes.func,
+    onSubmit: PropTypes.func,
     projectTitle: PropTypes.string
 };
 
@@ -59,7 +47,9 @@ const mapStateToProps = state => ({
     projectTitle: state.scratchGui.projectTitle
 });
 
-const mapDispatchToProps = () => ({});
+const mapDispatchToProps = dispatch => ({
+    onSubmit: title => dispatch(setProjectTitle(title))
+});
 
 export default injectIntl(connect(
     mapStateToProps,
diff --git a/src/components/monitor/monitor.css b/src/components/monitor/monitor.css
index 97a62e02d23916200db10035b79b42f2adc4aa3d..521c39279aacda5896cf48c9ad1b3f9515af77a1 100644
--- a/src/components/monitor/monitor.css
+++ b/src/components/monitor/monitor.css
@@ -43,6 +43,7 @@
     margin: 0 5px;
     border-radius: calc($space / 2);
     padding: 0 2px;
+    white-space: pre-wrap;
     transform: translateZ(0); /* Fixes flickering in Safari */
 }
 
@@ -53,6 +54,7 @@
     text-align: center;
     color: white;
     font-size: 1rem;
+    white-space: pre-wrap;
     transform: translateZ(0); /* Fixes flickering in Safari */
 }
 
@@ -149,7 +151,7 @@
     overflow: hidden; /* Don't let long values escape container */
     text-overflow: ellipsis;
     user-select: text; /* Allow selecting list values for 2.0 compatibility, only relevant in player */
-    white-space: nowrap;
+    white-space: pre;
 }
 
 .list-input {
diff --git a/src/containers/gui.jsx b/src/containers/gui.jsx
index 2012c61e555cf8bf4a13160c430db09fea320e34..314c38893518b0a8e5a8bce6a847dc008a6ac5b6 100644
--- a/src/containers/gui.jsx
+++ b/src/containers/gui.jsx
@@ -4,14 +4,13 @@ import {compose} from 'redux';
 import {connect} from 'react-redux';
 import ReactModal from 'react-modal';
 import VM from 'scratch-vm';
-import {defineMessages, injectIntl, intlShape} from 'react-intl';
+import {injectIntl, intlShape} from 'react-intl';
 
 import ErrorBoundaryHOC from '../lib/error-boundary-hoc.jsx';
 import {
     getIsError,
     getIsShowingProject
 } from '../reducers/project-state';
-import {setProjectTitle} from '../reducers/project-title';
 import {
     activateTab,
     BLOCKS_TAB_INDEX,
@@ -29,6 +28,7 @@ import {
 import FontLoaderHOC from '../lib/font-loader-hoc.jsx';
 import LocalizationHOC from '../lib/localization-hoc.jsx';
 import ProjectFetcherHOC from '../lib/project-fetcher-hoc.jsx';
+import TitledHOC from '../lib/titled-hoc.jsx';
 import ProjectSaverHOC from '../lib/project-saver-hoc.jsx';
 import QueryParserHOC from '../lib/query-parser-hoc.jsx';
 import storage from '../lib/storage';
@@ -39,18 +39,9 @@ import cloudManagerHOC from '../lib/cloud-manager-hoc.jsx';
 import GUIComponent from '../components/gui/gui.jsx';
 import {setIsScratchDesktop} from '../lib/isScratchDesktop.js';
 
-const messages = defineMessages({
-    defaultProjectTitle: {
-        id: 'gui.gui.defaultProjectTitle',
-        description: 'Default title for project',
-        defaultMessage: 'Scratch Project'
-    }
-});
-
 class GUI extends React.Component {
     componentDidMount () {
         setIsScratchDesktop(this.props.isScratchDesktop);
-        this.setReduxTitle(this.props.projectTitle);
         this.props.onStorageInit(storage);
         this.props.onVmInit(this.props.vm);
     }
@@ -58,24 +49,12 @@ class GUI extends React.Component {
         if (this.props.projectId !== prevProps.projectId && this.props.projectId !== null) {
             this.props.onUpdateProjectId(this.props.projectId);
         }
-        if (this.props.projectTitle !== prevProps.projectTitle) {
-            this.setReduxTitle(this.props.projectTitle);
-        }
         if (this.props.isShowingProject && !prevProps.isShowingProject) {
             // this only notifies container when a project changes from not yet loaded to loaded
             // At this time the project view in www doesn't need to know when a project is unloaded
             this.props.onProjectLoaded();
         }
     }
-    setReduxTitle (newTitle) {
-        if (newTitle === null || typeof newTitle === 'undefined') {
-            this.props.onUpdateReduxProjectTitle(
-                this.props.intl.formatMessage(messages.defaultProjectTitle)
-            );
-        } else {
-            this.props.onUpdateReduxProjectTitle(newTitle);
-        }
-    }
     render () {
         if (this.props.isError) {
             throw new Error(
@@ -92,11 +71,9 @@ class GUI extends React.Component {
             onProjectLoaded,
             onStorageInit,
             onUpdateProjectId,
-            onUpdateReduxProjectTitle,
             onVmInit,
             projectHost,
             projectId,
-            projectTitle,
             /* eslint-enable no-unused-vars */
             children,
             fetchingProject,
@@ -131,12 +108,9 @@ GUI.propTypes = {
     onSeeCommunity: PropTypes.func,
     onStorageInit: PropTypes.func,
     onUpdateProjectId: PropTypes.func,
-    onUpdateProjectTitle: PropTypes.func,
-    onUpdateReduxProjectTitle: PropTypes.func,
     onVmInit: PropTypes.func,
     projectHost: PropTypes.string,
     projectId: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
-    projectTitle: PropTypes.string,
     telemetryModalVisible: PropTypes.bool,
     vm: PropTypes.instanceOf(VM).isRequired
 };
@@ -186,8 +160,7 @@ const mapDispatchToProps = dispatch => ({
     onActivateSoundsTab: () => dispatch(activateTab(SOUNDS_TAB_INDEX)),
     onRequestCloseBackdropLibrary: () => dispatch(closeBackdropLibrary()),
     onRequestCloseCostumeLibrary: () => dispatch(closeCostumeLibrary()),
-    onRequestCloseTelemetryModal: () => dispatch(closeTelemetryModal()),
-    onUpdateReduxProjectTitle: title => dispatch(setProjectTitle(title))
+    onRequestCloseTelemetryModal: () => dispatch(closeTelemetryModal())
 });
 
 const ConnectedGUI = injectIntl(connect(
@@ -204,6 +177,7 @@ const WrappedGui = compose(
     FontLoaderHOC,
     QueryParserHOC,
     ProjectFetcherHOC,
+    TitledHOC,
     ProjectSaverHOC,
     vmListenerHOC,
     vmManagerHOC,
diff --git a/src/containers/sb-file-uploader.jsx b/src/containers/sb-file-uploader.jsx
index ae5f4291536338a5b39f1a7be38a071bc95a2b9e..2019f719e978a9c485b540488f873cb8046644ee 100644
--- a/src/containers/sb-file-uploader.jsx
+++ b/src/containers/sb-file-uploader.jsx
@@ -3,6 +3,7 @@ import PropTypes from 'prop-types';
 import React from 'react';
 import {connect} from 'react-redux';
 import {defineMessages, injectIntl, intlShape} from 'react-intl';
+import {setProjectTitle} from '../reducers/project-title';
 
 import log from '../lib/log';
 import sharedMessages from '../lib/shared-messages';
@@ -131,7 +132,7 @@ class SBFileUploader extends React.Component {
                     // This is necessary in case the user wants to reload a project
                     if (filename) {
                         const uploadedProjectTitle = this.getProjectTitleFromFilename(filename);
-                        this.props.onUpdateProjectTitle(uploadedProjectTitle);
+                        this.props.onReceivedProjectTitle(uploadedProjectTitle);
                     }
                     this.resetFileInput();
                 })
@@ -179,9 +180,9 @@ SBFileUploader.propTypes = {
     loadingState: PropTypes.oneOf(LoadingStates),
     onLoadingFinished: PropTypes.func,
     onLoadingStarted: PropTypes.func,
-    onUpdateProjectTitle: PropTypes.func,
     projectChanged: PropTypes.bool,
     requestProjectUpload: PropTypes.func,
+    onReceivedProjectTitle: PropTypes.func,
     userOwnsProject: PropTypes.bool,
     vm: PropTypes.shape({
         loadProject: PropTypes.func
@@ -209,7 +210,8 @@ const mapDispatchToProps = (dispatch, ownProps) => ({
         dispatch(closeFileMenu());
     },
     requestProjectUpload: loadingState => dispatch(requestProjectUpload(loadingState)),
-    onLoadingStarted: () => dispatch(openLoadingProject())
+    onLoadingStarted: () => dispatch(openLoadingProject()),
+    onReceivedProjectTitle: title => dispatch(setProjectTitle(title))
 });
 
 // Allow incoming props to override redux-provided props. Used to mock in tests.
diff --git a/src/lib/libraries/decks/en-steps.js b/src/lib/libraries/decks/en-steps.js
index ce74ef4c365866f4b31d8b6740f1a616461889f1..d9af8c33ed941d2bb35518d9809618e9993937cd 100644
--- a/src/lib/libraries/decks/en-steps.js
+++ b/src/lib/libraries/decks/en-steps.js
@@ -170,6 +170,34 @@ import moveArrowKeysUpDown from './steps/move-arrow-keys-up-down.en.png';
 import glideAroundBackAndForth from './steps/glide-around-back-and-forth.en.png';
 import glideAroundPoint from './steps/glide-around-point.en.png';
 
+// Code a Cartoon
+import codeCartoonSaySomething from './steps/code-cartoon-01-say-something.en.png';
+import codeCartoonAnimate from './steps/code-cartoon-02-animate.en.gif';
+import codeCartoonSelectDifferentCharacter from './steps/code-cartoon-03-select-different-character.en.gif';
+import codeCartoonUseMinusSign from './steps/code-cartoon-04-use-minus-sign.en.gif';
+import codeCartoonGrowShrink from './steps/code-cartoon-05-grow-shrink.en.gif';
+import codeCartoonSelectDifferentCharacter2 from './steps/code-cartoon-06-select-another-different-character.en.gif';
+import codeCartoonJump from './steps/code-cartoon-07-jump.en.gif';
+import codeCartoonChangeScenes from './steps/code-cartoon-08-change-scenes.en.gif';
+import codeCartoonGlideAround from './steps/code-cartoon-09-glide-around.en.gif';
+import codeCartoonChangeCostumes from './steps/code-cartoon-10-change-costumes.en.gif';
+import codeCartoonChooseMoreCharacters from './steps/code-cartoon-11-choose-more-characters.en.jpg';
+
+// Talking Tales
+import talesAddExtension from './steps/talking-1-add-extension.en.gif';
+import talesChooseSprite from './steps/talking-2-choose-sprite.en.png';
+import talesSaySomething from './steps/talking-3-say-something.en.gif';
+import talesChooseBackdrop from './steps/talking-4-choose-backdrop.en.png';
+import talesSwitchBackdrop from './steps/talking-5-switch-backdrop.en.gif';
+import talesChooseAnotherSprite from './steps/talking-6-choose-another-sprite.en.png';
+import talesMoveAround from './steps/talking-7-move-around.en.gif';
+import talesChooseAnotherBackdrop from './steps/talking-8-choose-another-backdrop.en.png';
+import talesAnimateTalking from './steps/talking-9-animate.en.gif';
+import talesChooseThirdBackdrop from './steps/talking-10-choose-third-backdrop.en.png';
+import talesChooseSound from './steps/talking-11-choose-sound.en.gif';
+import talesDanceMoves from './steps/talking-12-dance-moves.en.gif';
+import talesAskAnswer from './steps/talking-13-ask-and-answer.en.gif';
+
 const enImages = {
     // Intro
     introMove: introMove,
@@ -276,7 +304,7 @@ const enImages = {
     flyAddScenery: flyAddScenery,
     flyMoveScenery: flyMoveScenery,
     flySwitchLooks: flySwitchLooks,
-    
+
     // Pong
     pongAddBackdrop: pongAddBackdrop,
     pongAddBallSprite: pongAddBallSprite,
@@ -291,7 +319,7 @@ const enImages = {
     pongResetScore: pongResetScore,
     pongAddLineSprite: pongAddLineSprite,
     pongGameOver: pongGameOver,
-    
+
     // Imagine a World
     imagineTypeWhatYouWant: imagineTypeWhatYouWant,
     imagineClickGreenFlag: imagineClickGreenFlag,
@@ -308,26 +336,26 @@ const enImages = {
     imagineSwitchBackdrops: imagineSwitchBackdrops,
     imagineRecordASound: imagineRecordASound,
     imagineChooseSound: imagineChooseSound,
-    
+
     // Add a Backdrop
     addBackdrop: addBackdrop,
-    
+
     // Add Effects
     addEffects: addEffects,
-    
+
     // Hide and Show
     hideAndShow: hideAndShow,
-    
+
     // Switch Costumes
     switchCostumes: switchCostumes,
-    
+
     // Change Size
     changeSize: changeSize,
-    
+
     // Spin
     spinTurn: spinTurn,
     spinPointInDirection: spinPointInDirection,
-    
+
     // Record a Sound
     recordASoundSoundsTab: recordASoundSoundsTab,
     recordASoundClickRecord: recordASoundClickRecord,
@@ -341,9 +369,35 @@ const enImages = {
 
     // Glide Around
     glideAroundBackAndForth: glideAroundBackAndForth,
-    glideAroundPoint: glideAroundPoint
-    
+    glideAroundPoint: glideAroundPoint,
+
+    // Code a Cartoon
+    codeCartoonSaySomething: codeCartoonSaySomething,
+    codeCartoonAnimate: codeCartoonAnimate,
+    codeCartoonSelectDifferentCharacter: codeCartoonSelectDifferentCharacter,
+    codeCartoonUseMinusSign: codeCartoonUseMinusSign,
+    codeCartoonGrowShrink: codeCartoonGrowShrink,
+    codeCartoonSelectDifferentCharacter2: codeCartoonSelectDifferentCharacter2,
+    codeCartoonJump: codeCartoonJump,
+    codeCartoonChangeScenes: codeCartoonChangeScenes,
+    codeCartoonGlideAround: codeCartoonGlideAround,
+    codeCartoonChangeCostumes: codeCartoonChangeCostumes,
+    codeCartoonChooseMoreCharacters: codeCartoonChooseMoreCharacters,
+
+    // Talking Tales
+    talesAddExtension: talesAddExtension,
+    talesChooseSprite: talesChooseSprite,
+    talesSaySomething: talesSaySomething,
+    talesAskAnswer: talesAskAnswer,
+    talesChooseBackdrop: talesChooseBackdrop,
+    talesSwitchBackdrop: talesSwitchBackdrop,
+    talesChooseAnotherSprite: talesChooseAnotherSprite,
+    talesMoveAround: talesMoveAround,
+    talesChooseAnotherBackdrop: talesChooseAnotherBackdrop,
+    talesAnimateTalking: talesAnimateTalking,
+    talesChooseThirdBackdrop: talesChooseThirdBackdrop,
+    talesChooseSound: talesChooseSound,
+    talesDanceMoves: talesDanceMoves
 };
 
-
 export {enImages};
diff --git a/src/lib/libraries/decks/index.jsx b/src/lib/libraries/decks/index.jsx
index b7412abdb6d8d7968f7db400c5b3375b1ef839aa..3e09346f1e1ac54893b241c5c42969f28d9d2537 100644
--- a/src/lib/libraries/decks/index.jsx
+++ b/src/lib/libraries/decks/index.jsx
@@ -45,6 +45,12 @@ import libraryPong from './thumbnails/pong.jpg';
 // Imagine a World
 import libraryImagine from './thumbnails/imagine.jpg';
 
+// Code a Cartoon
+import libraryCodeCartoon from './thumbnails/code-a-cartoon.jpg';
+
+// Talking Tales
+import libraryTalking from './thumbnails/talking.png';
+
 // Videos
 import recordASound from './thumbnails/record-a-sound.jpg';
 import glideAroundThumb from './thumbnails/glide-around.jpg';
@@ -173,7 +179,7 @@ export default {
         ],
         urlId: 'name'
     },
-    
+
     'imagine': {
         name: (
             <FormattedMessage
@@ -330,7 +336,7 @@ export default {
         ],
         urlId: 'imagine'
     },
-    
+
     'Make-Music': {
         name: (
             <FormattedMessage
@@ -400,7 +406,7 @@ export default {
         ],
         urlId: 'music'
     },
- 
+
     'Tell-A-Story': {
         name: (
             <FormattedMessage
@@ -513,7 +519,7 @@ export default {
         ],
         urlId: 'tell-a-story'
     },
-    
+
     'Chase-Game': {
         name: (
             <FormattedMessage
@@ -711,7 +717,7 @@ export default {
         ],
         urlId: 'animate-a-character'
     },
-       
+
     'Make-A-Game': {
         name: (
             <FormattedMessage
@@ -798,7 +804,7 @@ export default {
         ],
         urlId: 'clicker-game'
     },
-    
+
     'make-it-fly': {
         name: (
             <FormattedMessage
@@ -928,7 +934,7 @@ export default {
         ],
         urlId: 'make-it-fly'
     },
-    
+
     'pong': {
         name: (
             <FormattedMessage
@@ -1069,6 +1075,287 @@ export default {
         urlId: 'pong'
     },
 
+    'code-cartoon': {
+        name: (
+            <FormattedMessage
+                defaultMessage="Code a Cartoon"
+                description="Name for the 'Code a Cartoon' how-to"
+                id="gui.howtos.code-cartoon"
+            />
+        ),
+        tags: ['code-cartoon'],
+        requiredProjectId: '331474033',
+        img: libraryCodeCartoon,
+        steps: [{
+            video: 'code-cartoon'
+        }, {
+            title: (
+                <FormattedMessage
+                    defaultMessage="Say Something When You Click the Green Flag"
+                    description="Step name for 'Say Something When You Click the Green Flag' step"
+                    id="gui.howtos.code-cartoon.step_codeCartoonSaySomething"
+                />
+            ),
+            image: 'codeCartoonSaySomething'
+        }, {
+            title: (
+                <FormattedMessage
+                    defaultMessage="Animate a Character When You Click It"
+                    description="Step name for 'Animate a Character When You Click It' step"
+                    id="gui.howtos.code-cartoon.step_codeCartoonAnimate"
+                />
+            ),
+            image: 'codeCartoonAnimate'
+        }, {
+            title: (
+                <FormattedMessage
+                    defaultMessage="Select a Different Character"
+                    description="Step name for 'Select a Different Character' step"
+                    id="gui.howtos.code-cartoon.step_codeCartoonSelectDifferentCharacter"
+                />
+            ),
+            image: 'codeCartoonSelectDifferentCharacter'
+        }, {
+            title: (
+                <FormattedMessage
+                    defaultMessage="Use a Minus Sign to Get Smaller"
+                    description="Step name for 'Use a Minus Sign to Get Smaller' step"
+                    id="gui.howtos.code-cartoon.step_codeCartoonUseMinusSign"
+                />
+            ),
+            image: 'codeCartoonUseMinusSign'
+        }, {
+            title: (
+                <FormattedMessage
+                    defaultMessage="Make a Character Grow and Shrink"
+                    description="Step name for 'Make a Character Grow and Shrink' step"
+                    id="gui.howtos.code-cartoon.step_codeCartoonGrowShrink"
+                />
+            ),
+            image: 'codeCartoonGrowShrink'
+        }, {
+            title: (
+                <FormattedMessage
+                    defaultMessage="Select a Different Character"
+                    description="Step name for 'Select a Different Character' step"
+                    id="gui.howtos.code-cartoon.step_codeCartoonSelectDifferentCharacter2"
+                />
+            ),
+            image: 'codeCartoonSelectDifferentCharacter2'
+        }, {
+            title: (
+                <FormattedMessage
+                    defaultMessage="Jump Up and Down"
+                    description="Step name for 'Jump Up and Down' step"
+                    id="gui.howtos.code-cartoon.step_codeCartoonJump"
+                />
+            ),
+            image: 'codeCartoonJump'
+        }, {
+            title: (
+                <FormattedMessage
+                    defaultMessage="Click a Character to Change Scenes"
+                    description="Step name for 'Click a Character to Change Scenes' step"
+                    id="gui.howtos.code-cartoon.step_codeCartoonChangeScenes"
+                />
+            ),
+            image: 'codeCartoonChangeScenes'
+        }, {
+            title: (
+                <FormattedMessage
+                    defaultMessage="Glide Around"
+                    description="Step name for 'Glide Around' step"
+                    id="gui.howtos.code-cartoon.step_codeCartoonGlideAround"
+                />
+            ),
+            image: 'codeCartoonGlideAround'
+        }, {
+            title: (
+                <FormattedMessage
+                    defaultMessage="Change Costumes"
+                    description="Step name for 'Change Costumes' step"
+                    id="gui.howtos.code-cartoon.step_codeCartoonChangeCostumes"
+                />
+            ),
+            image: 'codeCartoonChangeCostumes'
+        }, {
+            title: (
+                <FormattedMessage
+                    defaultMessage="Choose More Characters to Add to Your Cartoon"
+                    description="Step name for 'Choose More Characters to Add to Your Cartoon' step"
+                    id="gui.howtos.code-cartoon.step_codeCartoonChooseMoreCharacters"
+                />
+            ),
+            image: 'codeCartoonChooseMoreCharacters'
+        }, {
+            deckIds: [
+                'Chase-Game',
+                'Tell-A-Story'
+            ]
+        }
+        ],
+        urlId: 'code-cartoon'
+    },
+
+    'cartoon-network': {
+        name: (
+            <FormattedMessage
+                defaultMessage="Animate an Adventure Game"
+                description="Animate an Adventure Game' how-to"
+                id="gui.howtos.cartoon-network"
+            />
+        ),
+        requiredProjectId: '249143200',
+        img: libraryCartoonNetwork,
+        steps: [{
+            video: 'uz5oz5h9yg',
+            trackingPixel: (
+                <img src="https://code.org/api/hour/begin_scratch_adventure.png" />
+            )
+        }, {
+            title: (
+                <FormattedMessage
+                    defaultMessage="Choose a Character to Show"
+                    description="Step name for 'Choose a Character to Show' step"
+                    id="gui.howtos.cartoon-network.step_CNcharacter"
+                />
+            ),
+            image: 'cnShowCharacter'
+        }, {
+            title: (
+                <FormattedMessage
+                    defaultMessage="Say Something"
+                    description="Step name for 'Say Something' step"
+                    id="gui.howtos.cartoon-network.step_CNsay"
+                />
+            ),
+            image: 'cnSay'
+        }, {
+            title: (
+                <FormattedMessage
+                    defaultMessage="Glide Around"
+                    description="Step name for 'Glide Around' step"
+                    id="gui.howtos.cartoon-network.step_CNglide"
+                />
+            ),
+            image: 'cnGlide'
+        }, {
+            title: (
+                <FormattedMessage
+                    defaultMessage=" Choose an Object to Chase "
+                    description="Step name for 'Choose an Object to Chase' step"
+                    id="gui.howtos.cartoon-network.step_CNpicksprite"
+                />
+            ),
+            image: 'cnPickSprite'
+        }, {
+            title: (
+                <FormattedMessage
+                    defaultMessage="Collect Objects"
+                    description="Step name for 'Collect Objects' step"
+                    id="gui.howtos.cartoon-network.step_CNcollect"
+                />
+            ),
+            image: 'cnCollect'
+        }, {
+            title: (
+                <FormattedMessage
+                    defaultMessage="Make a Score Variable"
+                    description="Step name for 'Make a Score Variable' step"
+                    id="gui.howtos.cartoon-network.step_CNvariable"
+                />
+            ),
+            image: 'cnVariable'
+        }, {
+            title: (
+                <FormattedMessage
+                    defaultMessage="Keep Score"
+                    description="Step name for 'Keep Score' step"
+                    id="gui.howtos.cartoon-network.step_CNscore"
+                />
+            ),
+            image: 'cnScore'
+        }, {
+            title: (
+                <FormattedMessage
+                    defaultMessage="Level Up: Change Backdrop"
+                    description="Step name for 'Level Up: Change Backdrop' step"
+                    id="gui.howtos.cartoon-network.step_CNbackdrop"
+                />
+            ),
+            image: 'cnBackdrop'
+        },
+        {
+            video: '6o76f5ivo1'
+        },
+        {
+            deckIds: [
+                'switch-costume',
+                'add-effects'
+            ]
+        }
+        ],
+        urlId: 'animate-an-adventure-game'
+    },
+
+    'Video-Sensing': {
+        name: (
+            <FormattedMessage
+                defaultMessage="Video Sensing"
+                description="Name for the 'Video Sensing' how-to"
+                id="gui.howtos.videosens.name"
+            />
+        ),
+        img: libraryVideoSens,
+        steps: [{
+            video: '3pd1z110d6'
+        },
+        {
+            title: (
+                <FormattedMessage
+                    defaultMessage="Add Extension"
+                    description="Step name for 'Add Extension' step"
+                    id="gui.howtos.videosens.step_addextension"
+                />
+            ),
+            image: 'videoAddExtension'
+        }, {
+            title: (
+                <FormattedMessage
+                    defaultMessage="Pet the Cat"
+                    description="Step name for 'Pet the Cat' step"
+                    id="gui.howtos.videosens.step_pet"
+                />
+            ),
+            image: 'videoPet'
+        }, {
+            title: (
+                <FormattedMessage
+                    defaultMessage="Animate"
+                    description="Step name for 'Animate' step"
+                    id="gui.howtos.videosens.step_animate"
+                />
+            ),
+            image: 'videoAnimate'
+        }, {
+            title: (
+                <FormattedMessage
+                    defaultMessage="Pop a Balloon"
+                    description="Step name for 'Pop a Balloon' step"
+                    id="gui.howtos.videosens.step_pop"
+                />
+            ),
+            image: 'videoPop'
+        }, {
+            deckIds: [
+                'Make-Music',
+                'add-effects'
+            ]
+        }
+        ],
+        urlId: 'video-sensing'
+    },
+
     'say-it-out-loud': {
         name: (
             <FormattedMessage
@@ -1183,163 +1470,143 @@ export default {
         urlId: 'animations-that-talk'
     },
 
-    'Video-Sensing': {
+    'talking': {
         name: (
             <FormattedMessage
-                defaultMessage="Video Sensing"
-                description="Name for the 'Video Sensing' how-to"
-                id="gui.howtos.videosens.name"
+                defaultMessage="Talking Tales"
+                description="Name for the 'Talking Tales' how-to"
+                id="gui.howtos.talking"
             />
         ),
-        img: libraryVideoSens,
+        tags: ['talking'],
+        img: libraryTalking,
         steps: [{
-            video: '3pd1z110d6'
-        },
-        {
+            video: 'talking'
+        }, {
             title: (
                 <FormattedMessage
-                    defaultMessage="Add Extension"
-                    description="Step name for 'Add Extension' step"
-                    id="gui.howtos.videosens.step_addextension"
+                    defaultMessage="Click to Add the Text-to-Speech Blocks"
+                    description="Step name for 'Click to Add the Text-to-Speech Blocks' step"
+                    id="gui.howtos.talking.step_talesAddExtension"
                 />
             ),
-            image: 'videoAddExtension'
+            image: 'talesAddExtension'
         }, {
             title: (
                 <FormattedMessage
-                    defaultMessage="Pet the Cat"
-                    description="Step name for 'Pet the Cat' step"
-                    id="gui.howtos.videosens.step_pet"
+                    defaultMessage="Choose a Sprite"
+                    description="Step name for 'Choose a Sprite' step"
+                    id="gui.howtos.talking.step_talesChooseSprite"
                 />
             ),
-            image: 'videoPet'
+            image: 'talesChooseSprite'
         }, {
             title: (
                 <FormattedMessage
-                    defaultMessage="Animate"
-                    description="Step name for 'Animate' step"
-                    id="gui.howtos.videosens.step_animate"
+                    defaultMessage="Make a Character Speak"
+                    description="Step name for 'Make a Character Speak' step"
+                    id="gui.howtos.talking.step_talesSaySomething"
                 />
             ),
-            image: 'videoAnimate'
+            image: 'talesSaySomething'
         }, {
             title: (
                 <FormattedMessage
-                    defaultMessage="Pop a Balloon"
-                    description="Step name for 'Pop a Balloon' step"
-                    id="gui.howtos.videosens.step_pop"
+                    defaultMessage="Choose a Backdrop"
+                    description="Step name for 'Choose a Backdrop' step"
+                    id="gui.howtos.talking.step_talesChooseBackdrop"
                 />
             ),
-            image: 'videoPop'
+            image: 'talesChooseBackdrop'
         }, {
-            deckIds: [
-                'Make-Music',
-                'add-effects'
-            ]
-        }
-        ],
-        urlId: 'video-sensing'
-    },
-    
-    'cartoon-network': {
-        name: (
-            <FormattedMessage
-                defaultMessage="Animate an Adventure Game"
-                description="Animate an Adventure Game' how-to"
-                id="gui.howtos.cartoon-network"
-            />
-        ),
-        requiredProjectId: '249143200',
-        img: libraryCartoonNetwork,
-        steps: [{
-            video: 'uz5oz5h9yg',
-            trackingPixel: (
-                <img src="https://code.org/api/hour/begin_scratch_adventure.png" />
-            )
+            title: (
+                <FormattedMessage
+                    defaultMessage="Click a Character to Go to the Next Backdrop"
+                    description="Step name for 'Click a Character to Go to the Next Backdrop' step"
+                    id="gui.howtos.talking.step_talesSwitchBackdrop"
+                />
+            ),
+            image: 'talesSwitchBackdrop'
         }, {
             title: (
                 <FormattedMessage
-                    defaultMessage="Choose a Character to Show"
-                    description="Step name for 'Choose a Character to Show' step"
-                    id="gui.howtos.cartoon-network.step_CNcharacter"
+                    defaultMessage="Choose Another Sprite"
+                    description="Step name for 'Choose Another Sprite' step"
+                    id="gui.howtos.talking.step_talesChooseAnotherSprite"
                 />
             ),
-            image: 'cnShowCharacter'
+            image: 'talesChooseAnotherSprite'
         }, {
             title: (
                 <FormattedMessage
-                    defaultMessage="Say Something"
-                    description="Step name for 'Say Something' step"
-                    id="gui.howtos.cartoon-network.step_CNsay"
+                    defaultMessage="Move Around"
+                    description="Step name for 'Move Around' step"
+                    id="gui.howtos.talking.step_talesMoveAround"
                 />
             ),
-            image: 'cnSay'
+            image: 'talesMoveAround'
         }, {
             title: (
                 <FormattedMessage
-                    defaultMessage="Glide Around"
-                    description="Step name for 'Glide Around' step"
-                    id="gui.howtos.cartoon-network.step_CNglide"
+                    defaultMessage="Choose Another Backdrop"
+                    description="Step name for 'Choose Another Backdrop' step"
+                    id="gui.howtos.talking.step_talesChooseAnotherBackdrop"
                 />
             ),
-            image: 'cnGlide'
+            image: 'talesChooseAnotherBackdrop'
         }, {
             title: (
                 <FormattedMessage
-                    defaultMessage=" Choose an Object to Chase "
-                    description="Step name for 'Choose an Object to Chase' step"
-                    id="gui.howtos.cartoon-network.step_CNpicksprite"
+                    defaultMessage="Animate Talking"
+                    description="Step name for 'Animate Talking' step"
+                    id="gui.howtos.talking.step_talesAnimateTalking"
                 />
             ),
-            image: 'cnPickSprite'
+            image: 'talesAnimateTalking'
         }, {
             title: (
                 <FormattedMessage
-                    defaultMessage="Collect Objects"
-                    description="Step name for 'Collect Objects' step"
-                    id="gui.howtos.cartoon-network.step_CNcollect"
+                    defaultMessage="Choose Another Backdrop"
+                    description="Step name for 'Choose Another Backdrop' step"
+                    id="gui.howtos.talking.step_talesChooseThirdBackdrop"
                 />
             ),
-            image: 'cnCollect'
+            image: 'talesChooseThirdBackdrop'
         }, {
             title: (
                 <FormattedMessage
-                    defaultMessage="Make a Score Variable"
-                    description="Step name for 'Make a Score Variable' step"
-                    id="gui.howtos.cartoon-network.step_CNvariable"
+                    defaultMessage="Choose a Song to Dance To"
+                    description="Step name for 'Choose a Song to Dance To' step"
+                    id="gui.howtos.talking.step_talesChooseSound"
                 />
             ),
-            image: 'cnVariable'
+            image: 'talesChooseSound'
         }, {
             title: (
                 <FormattedMessage
-                    defaultMessage="Keep Score"
-                    description="Step name for 'Keep Score' step"
-                    id="gui.howtos.cartoon-network.step_CNscore"
+                    defaultMessage="Dance Moves"
+                    description="Step name for 'Dance Moves' step"
+                    id="gui.howtos.talking.step_talesDanceMoves"
                 />
             ),
-            image: 'cnScore'
+            image: 'talesDanceMoves'
         }, {
             title: (
                 <FormattedMessage
-                    defaultMessage="Level Up: Change Backdrop"
-                    description="Step name for 'Level Up: Change Backdrop' step"
-                    id="gui.howtos.cartoon-network.step_CNbackdrop"
+                    defaultMessage="Get the Ask and Answer Blocks from the Sensing Category"
+                    description="Step name for 'Get the Ask and Answer Blocks from the Sensing Category' step"
+                    id="gui.howtos.talking.step_talesAskAnswer"
                 />
             ),
-            image: 'cnBackdrop'
-        },
-        {
-            video: '6o76f5ivo1'
-        },
-        {
+            image: 'talesAskAnswer'
+        }, {
             deckIds: [
-                'switch-costume',
-                'add-effects'
+                'Tell-A-Story',
+                'Animate-A-Character'
             ]
         }
         ],
-        urlId: 'animate-an-adventure-game'
+        urlId: 'talking'
     },
 
     'add-sprite': {
@@ -1372,7 +1639,7 @@ export default {
         ],
         urlId: 'add-a-sprite'
     },
-    
+
     'add-a-backdrop': {
         name: (
             <FormattedMessage
@@ -1404,7 +1671,7 @@ export default {
         }],
         urlId: 'add-a-backdrop'
     },
-    
+
     'change-size': {
         name: (
             <FormattedMessage
@@ -1434,7 +1701,7 @@ export default {
         }],
         urlId: 'change-size'
     },
-    
+
     'glide-around': {
         name: (
             <FormattedMessage
@@ -1539,7 +1806,7 @@ export default {
         }],
         urlId: 'record-a-sound'
     },
-    
+
     'spin-video': {
         name: (
             <FormattedMessage
@@ -1578,7 +1845,7 @@ export default {
         }],
         urlId: 'make-it-spin'
     },
-    
+
     'hide-and-show': {
         name: (
             <FormattedMessage
@@ -1677,7 +1944,7 @@ export default {
         }],
         urlId: 'arrow-keys'
     },
-    
+
     'add-effects': {
         name: (
             <FormattedMessage
@@ -1708,7 +1975,7 @@ export default {
         }],
         urlId: 'add-effects'
     },
-    
+
     'wedo2-getting-started': {
         steps: [{
             video: '4im7iizv47'
@@ -1716,7 +1983,7 @@ export default {
         urlId: 'wedo',
         hidden: true
     },
-    
+
     'ev3-getting-started': {
         steps: [{
             video: 'qgu78c5y7d'
@@ -1724,7 +1991,7 @@ export default {
         urlId: 'ev3',
         hidden: true
     },
-    
+
     'whats-new': {
         steps: [{
             video: 'mtqymxg0qq'
diff --git a/src/lib/libraries/decks/steps/code-cartoon-01-say-something.en.png b/src/lib/libraries/decks/steps/code-cartoon-01-say-something.en.png
new file mode 100644
index 0000000000000000000000000000000000000000..f2a1570c3876d023b8e341c2a3910bb05b312f48
Binary files /dev/null and b/src/lib/libraries/decks/steps/code-cartoon-01-say-something.en.png differ
diff --git a/src/lib/libraries/decks/steps/code-cartoon-02-animate.en.gif b/src/lib/libraries/decks/steps/code-cartoon-02-animate.en.gif
new file mode 100644
index 0000000000000000000000000000000000000000..801670c67ad47303ead6f49b238efaa14feb44f8
Binary files /dev/null and b/src/lib/libraries/decks/steps/code-cartoon-02-animate.en.gif differ
diff --git a/src/lib/libraries/decks/steps/code-cartoon-03-select-different-character.en.gif b/src/lib/libraries/decks/steps/code-cartoon-03-select-different-character.en.gif
new file mode 100644
index 0000000000000000000000000000000000000000..9881aac5d22c16cef25c4c15b53a20acdb7db13c
Binary files /dev/null and b/src/lib/libraries/decks/steps/code-cartoon-03-select-different-character.en.gif differ
diff --git a/src/lib/libraries/decks/steps/code-cartoon-04-use-minus-sign.en.gif b/src/lib/libraries/decks/steps/code-cartoon-04-use-minus-sign.en.gif
new file mode 100644
index 0000000000000000000000000000000000000000..783384fc49fa624f915bebfea5b1773fdfda9ad7
Binary files /dev/null and b/src/lib/libraries/decks/steps/code-cartoon-04-use-minus-sign.en.gif differ
diff --git a/src/lib/libraries/decks/steps/code-cartoon-05-grow-shrink.en.gif b/src/lib/libraries/decks/steps/code-cartoon-05-grow-shrink.en.gif
new file mode 100644
index 0000000000000000000000000000000000000000..e4bbf5dad36d51c12dbe2f3db84f73057afd7ab8
Binary files /dev/null and b/src/lib/libraries/decks/steps/code-cartoon-05-grow-shrink.en.gif differ
diff --git a/src/lib/libraries/decks/steps/code-cartoon-06-select-another-different-character.en.gif b/src/lib/libraries/decks/steps/code-cartoon-06-select-another-different-character.en.gif
new file mode 100644
index 0000000000000000000000000000000000000000..6aa589a6751268a6ab05ad2e0027a78197992cf7
Binary files /dev/null and b/src/lib/libraries/decks/steps/code-cartoon-06-select-another-different-character.en.gif differ
diff --git a/src/lib/libraries/decks/steps/code-cartoon-07-jump.en.gif b/src/lib/libraries/decks/steps/code-cartoon-07-jump.en.gif
new file mode 100644
index 0000000000000000000000000000000000000000..e000306b208aacb70d19525dbc1cb98ce45d2cef
Binary files /dev/null and b/src/lib/libraries/decks/steps/code-cartoon-07-jump.en.gif differ
diff --git a/src/lib/libraries/decks/steps/code-cartoon-08-change-scenes.en.gif b/src/lib/libraries/decks/steps/code-cartoon-08-change-scenes.en.gif
new file mode 100644
index 0000000000000000000000000000000000000000..fb27d72112262354de6227526994928158afa854
Binary files /dev/null and b/src/lib/libraries/decks/steps/code-cartoon-08-change-scenes.en.gif differ
diff --git a/src/lib/libraries/decks/steps/code-cartoon-09-glide-around.en.gif b/src/lib/libraries/decks/steps/code-cartoon-09-glide-around.en.gif
new file mode 100644
index 0000000000000000000000000000000000000000..2a63ac116b5258c76a1ced3e5f0c9660ad0de59c
Binary files /dev/null and b/src/lib/libraries/decks/steps/code-cartoon-09-glide-around.en.gif differ
diff --git a/src/lib/libraries/decks/steps/code-cartoon-10-change-costumes.en.gif b/src/lib/libraries/decks/steps/code-cartoon-10-change-costumes.en.gif
new file mode 100644
index 0000000000000000000000000000000000000000..e521e45f3297044025c85885caf2abb4a81c1196
Binary files /dev/null and b/src/lib/libraries/decks/steps/code-cartoon-10-change-costumes.en.gif differ
diff --git a/src/lib/libraries/decks/steps/code-cartoon-11-choose-more-characters.en.jpg b/src/lib/libraries/decks/steps/code-cartoon-11-choose-more-characters.en.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..5a88acc29d7964f44c91d0d0de1dfa2f96b79d24
Binary files /dev/null and b/src/lib/libraries/decks/steps/code-cartoon-11-choose-more-characters.en.jpg differ
diff --git a/src/lib/libraries/decks/steps/talking-1-add-extension.en.gif b/src/lib/libraries/decks/steps/talking-1-add-extension.en.gif
new file mode 100644
index 0000000000000000000000000000000000000000..b3e5b2f12d04b8cdd95e1ca1f1302df563536862
Binary files /dev/null and b/src/lib/libraries/decks/steps/talking-1-add-extension.en.gif differ
diff --git a/src/lib/libraries/decks/steps/talking-10-choose-third-backdrop.en.png b/src/lib/libraries/decks/steps/talking-10-choose-third-backdrop.en.png
new file mode 100755
index 0000000000000000000000000000000000000000..e266486bc283cd054a86be3e3c125ddb2e333da0
Binary files /dev/null and b/src/lib/libraries/decks/steps/talking-10-choose-third-backdrop.en.png differ
diff --git a/src/lib/libraries/decks/steps/talking-11-choose-sound.en.gif b/src/lib/libraries/decks/steps/talking-11-choose-sound.en.gif
new file mode 100644
index 0000000000000000000000000000000000000000..8bec863371aaeffcae7a79abc4ff80d6953f6cc4
Binary files /dev/null and b/src/lib/libraries/decks/steps/talking-11-choose-sound.en.gif differ
diff --git a/src/lib/libraries/decks/steps/talking-12-dance-moves.en.gif b/src/lib/libraries/decks/steps/talking-12-dance-moves.en.gif
new file mode 100644
index 0000000000000000000000000000000000000000..042935be889550d22f5aef5c8bd612e1d2796d95
Binary files /dev/null and b/src/lib/libraries/decks/steps/talking-12-dance-moves.en.gif differ
diff --git a/src/lib/libraries/decks/steps/talking-13-ask-and-answer.en.gif b/src/lib/libraries/decks/steps/talking-13-ask-and-answer.en.gif
new file mode 100644
index 0000000000000000000000000000000000000000..78a5c2f9e611cd4a2b481b6cecf2388437e42c24
Binary files /dev/null and b/src/lib/libraries/decks/steps/talking-13-ask-and-answer.en.gif differ
diff --git a/src/lib/libraries/decks/steps/talking-2-choose-sprite.en.png b/src/lib/libraries/decks/steps/talking-2-choose-sprite.en.png
new file mode 100755
index 0000000000000000000000000000000000000000..9eeea61ad6ae2fea50704cf76f43bf077f77fcd9
Binary files /dev/null and b/src/lib/libraries/decks/steps/talking-2-choose-sprite.en.png differ
diff --git a/src/lib/libraries/decks/steps/talking-3-say-something.en.gif b/src/lib/libraries/decks/steps/talking-3-say-something.en.gif
new file mode 100644
index 0000000000000000000000000000000000000000..19a1735cad219f0e34b026e9f8986f74a848b6e9
Binary files /dev/null and b/src/lib/libraries/decks/steps/talking-3-say-something.en.gif differ
diff --git a/src/lib/libraries/decks/steps/talking-4-choose-backdrop.en.png b/src/lib/libraries/decks/steps/talking-4-choose-backdrop.en.png
new file mode 100755
index 0000000000000000000000000000000000000000..6a2c8d72220921eeb22bc2bc30fe057c31203dd2
Binary files /dev/null and b/src/lib/libraries/decks/steps/talking-4-choose-backdrop.en.png differ
diff --git a/src/lib/libraries/decks/steps/talking-5-switch-backdrop.en.gif b/src/lib/libraries/decks/steps/talking-5-switch-backdrop.en.gif
new file mode 100644
index 0000000000000000000000000000000000000000..6012a7303236d3b3a6bacfff6646427249ede3ac
Binary files /dev/null and b/src/lib/libraries/decks/steps/talking-5-switch-backdrop.en.gif differ
diff --git a/src/lib/libraries/decks/steps/talking-6-choose-another-sprite.en.png b/src/lib/libraries/decks/steps/talking-6-choose-another-sprite.en.png
new file mode 100755
index 0000000000000000000000000000000000000000..cf01d27558ad4f5b549cdc89b0dc93b9cb15e82e
Binary files /dev/null and b/src/lib/libraries/decks/steps/talking-6-choose-another-sprite.en.png differ
diff --git a/src/lib/libraries/decks/steps/talking-7-move-around.en.gif b/src/lib/libraries/decks/steps/talking-7-move-around.en.gif
new file mode 100644
index 0000000000000000000000000000000000000000..025477326b98eedc316aa9f0497a3a5839bd877b
Binary files /dev/null and b/src/lib/libraries/decks/steps/talking-7-move-around.en.gif differ
diff --git a/src/lib/libraries/decks/steps/talking-8-choose-another-backdrop.en.png b/src/lib/libraries/decks/steps/talking-8-choose-another-backdrop.en.png
new file mode 100755
index 0000000000000000000000000000000000000000..607d8672c6bce91eecac2bf4e8bb330959102665
Binary files /dev/null and b/src/lib/libraries/decks/steps/talking-8-choose-another-backdrop.en.png differ
diff --git a/src/lib/libraries/decks/steps/talking-9-animate.en.gif b/src/lib/libraries/decks/steps/talking-9-animate.en.gif
new file mode 100644
index 0000000000000000000000000000000000000000..5e937abd75707b12bcd0408b704c1e2ae5c82795
Binary files /dev/null and b/src/lib/libraries/decks/steps/talking-9-animate.en.gif differ
diff --git a/src/lib/libraries/decks/thumbnails/code-a-cartoon.jpg b/src/lib/libraries/decks/thumbnails/code-a-cartoon.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..b043b3f5e3374aef7b7a99f0a335cdde7a670387
Binary files /dev/null and b/src/lib/libraries/decks/thumbnails/code-a-cartoon.jpg differ
diff --git a/src/lib/libraries/decks/thumbnails/talking.png b/src/lib/libraries/decks/thumbnails/talking.png
new file mode 100644
index 0000000000000000000000000000000000000000..51c06c6ef009bd738a4d9725806a97aed64445a7
Binary files /dev/null and b/src/lib/libraries/decks/thumbnails/talking.png differ
diff --git a/src/lib/libraries/decks/translate-video.js b/src/lib/libraries/decks/translate-video.js
index 3a5e219c618bd8a3bf65ce09bd8da305435d9d4c..8b58c08da7d7960a06ce30576513d4a6bc11bf49 100644
--- a/src/lib/libraries/decks/translate-video.js
+++ b/src/lib/libraries/decks/translate-video.js
@@ -123,6 +123,12 @@ const videos = {
     },
     'imagine': {
         en: '1ndh08yiso'
+    },
+    'code-cartoon': {
+        en: 'fpfuky3x6g'
+    },
+    'talking': {
+        en: 'j0208mq4qi'
     }
 };
 
diff --git a/src/lib/titled-hoc.jsx b/src/lib/titled-hoc.jsx
index 377d6150d2513ea1cf2bff65d2cd5cd35a08972d..6115990087deef53433ee043a9616e9ef4a499b4 100644
--- a/src/lib/titled-hoc.jsx
+++ b/src/lib/titled-hoc.jsx
@@ -1,5 +1,18 @@
+import PropTypes from 'prop-types';
 import React from 'react';
-import bindAll from 'lodash.bindall';
+import {connect} from 'react-redux';
+import {defineMessages, injectIntl, intlShape} from 'react-intl';
+
+import {getIsShowingWithoutId} from '../reducers/project-state';
+import {setProjectTitle} from '../reducers/project-title';
+
+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
@@ -7,31 +20,79 @@ import bindAll from 'lodash.bindall';
  */
 const TitledHOC = function (WrappedComponent) {
     class TitledComponent extends React.Component {
-        constructor (props) {
-            super(props);
-            bindAll(this, [
-                'handleUpdateProjectTitle'
-            ]);
-            this.state = {
-                projectTitle: null
-            };
+        componentDidMount () {
+            this.handleReceivedProjectTitle(this.props.projectTitle);
+        }
+        componentDidUpdate (prevProps) {
+            if (this.props.projectTitle !== prevProps.projectTitle) {
+                this.handleReceivedProjectTitle(this.props.projectTitle);
+            }
+            // if the projectTitle hasn't changed, but the reduxProjectTitle
+            // HAS changed, we need to report that change to the projectTitle's owner
+            if (this.props.reduxProjectTitle !== prevProps.reduxProjectTitle &&
+                this.props.reduxProjectTitle !== this.props.projectTitle) {
+                this.props.onUpdateProjectTitle(this.props.reduxProjectTitle);
+            }
         }
-        handleUpdateProjectTitle (newTitle) {
-            this.setState({projectTitle: newTitle});
+        handleReceivedProjectTitle (requestedTitle) {
+            let newTitle = requestedTitle;
+            if (newTitle === null || typeof newTitle === 'undefined') {
+                newTitle = this.props.intl.formatMessage(messages.defaultProjectTitle);
+            }
+            this.props.onChangedProjectTitle(newTitle);
         }
         render () {
+            const {
+                /* eslint-disable no-unused-vars */
+                intl,
+                isShowingWithoutId,
+                onChangedProjectTitle,
+                // for children, we replace onUpdateProjectTitle with our own
+                onUpdateProjectTitle,
+                // we don't pass projectTitle prop to children -- they must use
+                // redux value
+                projectTitle,
+                reduxProjectTitle,
+                /* eslint-enable no-unused-vars */
+                ...componentProps
+            } = this.props;
             return (
                 <WrappedComponent
-                    canEditTitle
-                    projectTitle={this.state.projectTitle}
-                    onUpdateProjectTitle={this.handleUpdateProjectTitle}
-                    {...this.props}
+                    {...componentProps}
                 />
             );
         }
     }
 
-    return TitledComponent;
+    TitledComponent.propTypes = {
+        intl: intlShape,
+        isShowingWithoutId: PropTypes.bool,
+        onChangedProjectTitle: PropTypes.func,
+        onUpdateProjectTitle: PropTypes.func,
+        projectTitle: PropTypes.string,
+        reduxProjectTitle: PropTypes.string
+    };
+
+    TitledComponent.defaultProps = {
+        onUpdateProjectTitle: () => {}
+    };
+
+    const mapStateToProps = state => {
+        const loadingState = state.scratchGui.projectState.loadingState;
+        return {
+            isShowingWithoutId: getIsShowingWithoutId(loadingState),
+            reduxProjectTitle: state.scratchGui.projectTitle
+        };
+    };
+
+    const mapDispatchToProps = dispatch => ({
+        onChangedProjectTitle: title => dispatch(setProjectTitle(title))
+    });
+
+    return injectIntl(connect(
+        mapStateToProps,
+        mapDispatchToProps,
+    )(TitledComponent));
 };
 
 export {
diff --git a/src/playground/player.jsx b/src/playground/player.jsx
index 8224ad6489c57ce5b071704de1adcdb776345b95..ae4cba1c9e7c39f43f5fba45c1763f8ce3a9f1b1 100644
--- a/src/playground/player.jsx
+++ b/src/playground/player.jsx
@@ -9,7 +9,6 @@ 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';
 
@@ -24,6 +23,7 @@ const Player = ({isPlayerOnly, onSeeInside, projectId}) => (
     <Box className={classNames(isPlayerOnly ? styles.stageOnly : styles.editor)}>
         {isPlayerOnly && <button onClick={onSeeInside}>{'See inside'}</button>}
         <GUI
+            canEditTitle
             enableCommunity
             isPlayerOnly={isPlayerOnly}
             projectId={projectId}
@@ -55,8 +55,7 @@ const ConnectedPlayer = connect(
 // ability to compose reducers.
 const WrappedPlayer = compose(
     AppStateHOC,
-    HashParserHOC,
-    TitledHOC
+    HashParserHOC
 )(ConnectedPlayer);
 
 const appTarget = document.createElement('div');
diff --git a/src/playground/render-gui.jsx b/src/playground/render-gui.jsx
index 78a79b6ee1f60981ea264a8b9670fca6ec017934..0f15fbfa707f322c7d478e56cfb590debdc00a42 100644
--- a/src/playground/render-gui.jsx
+++ b/src/playground/render-gui.jsx
@@ -5,7 +5,6 @@ import {compose} from 'redux';
 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';
 import log from '../lib/log.js';
 
 const onClickLogo = () => {
@@ -37,8 +36,7 @@ export default appTarget => {
     // ability to compose reducers.
     const WrappedGui = compose(
         AppStateHOC,
-        HashParserHOC,
-        TitledHOC
+        HashParserHOC
     )(GUI);
 
     // TODO a hack for testing the backpack, allow backpack host to be set by url param
@@ -67,6 +65,7 @@ export default appTarget => {
         // important: this is checking whether `simulateScratchDesktop` is truthy, not just defined!
         simulateScratchDesktop ?
             <WrappedGui
+                canEditTitle
                 isScratchDesktop
                 showTelemetryModal
                 canSave={false}
@@ -75,6 +74,7 @@ export default appTarget => {
                 onTelemetryModalOptOut={handleTelemetryModalOptOut}
             /> :
             <WrappedGui
+                canEditTitle
                 backpackVisible
                 showComingSoon
                 backpackHost={backpackHost}
diff --git a/test/helpers/selenium-helper.js b/test/helpers/selenium-helper.js
index 1072cb0d6f33d0f551de1d47cab392361ca183c7..84e6c355ae460a2580f3093035de9c6c8e6918e9 100644
--- a/test/helpers/selenium-helper.js
+++ b/test/helpers/selenium-helper.js
@@ -57,6 +57,10 @@ class SeleniumHelper {
         // Stub getUserMedia to always not allow access
         args.push('--use-fake-ui-for-media-stream=deny');
 
+        // Suppress complaints about AudioContext starting before a user gesture
+        // This is especially important on Windows, where Selenium directs JS console messages to stdout
+        args.push('--autoplay-policy=no-user-gesture-required');
+
         chromeCapabilities.set('chromeOptions', {args});
         chromeCapabilities.setLoggingPrefs({
             performance: 'ALL'
diff --git a/test/unit/containers/sb-file-uploader.test.jsx b/test/unit/containers/sb-file-uploader.test.jsx
index 083dbfa2da12f4d3a7c946ea54e0e26b00518553..c757114b4ca7e387c78e7477931cf31a04c91a9a 100644
--- a/test/unit/containers/sb-file-uploader.test.jsx
+++ b/test/unit/containers/sb-file-uploader.test.jsx
@@ -11,7 +11,6 @@ describe('SBFileUploader Container', () => {
     const mockStore = configureStore();
     let onLoadingFinished;
     let onLoadingStarted;
-    let onUpdateProjectTitle;
     let store;
 
     // Wrap this in a function so it gets test specific states and can be reused.
@@ -20,7 +19,6 @@ describe('SBFileUploader Container', () => {
             <SBFileUploader
                 onLoadingFinished={onLoadingFinished}
                 onLoadingStarted={onLoadingStarted}
-                onUpdateProjectTitle={onUpdateProjectTitle}
             >
                 {(renderFileInput, loadProject) => (
                     <div
@@ -40,7 +38,6 @@ describe('SBFileUploader Container', () => {
                 vm: {}
             }
         });
-        onUpdateProjectTitle = jest.fn();
         onLoadingFinished = jest.fn();
         onLoadingStarted = jest.fn();
     });
diff --git a/webpack.config.js b/webpack.config.js
index b134cd09014339f4a8558dc5db42467b1693ddc7..283d65706b5581768180d92d16cdd70c4ed847d9 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -219,6 +219,12 @@ module.exports = [
                 new CopyWebpackPlugin([{
                     from: 'extension-worker.{js,js.map}',
                     context: 'node_modules/scratch-vm/dist/web'
+                }]),
+                // Include library JSON files for scratch-desktop to use for downloading
+                new CopyWebpackPlugin([{
+                    from: 'src/lib/libraries/*.json',
+                    to: 'libraries',
+                    flatten: true
                 }])
             ])
         })) : []