diff --git a/.babelrc b/.babelrc
index 66742d7038c163433869430388c7ff25601b0aaf..8f910cdf7a6c0ef4465e1f823877bed5a3f58996 100644
--- a/.babelrc
+++ b/.babelrc
@@ -6,5 +6,5 @@
         ["react-intl", {
             "messagesDir": "./translations/messages/"
         }]],
-    "presets": [["env", {"targets": {browsers: ["last 3 versions", "Safari >= 8", "iOS >= 8"]}}], "react"]
+    "presets": [["env", {"targets": {"browsers": ["last 3 versions", "Safari >= 8", "iOS >= 8"]}}], "react"]
 }
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
index 0b7c6752bbb44a476fc0a4c4468d99761ef67bb5..f502d934ada49b0e0b094b03b7b33c37244cd2d3 100644
--- a/.github/PULL_REQUEST_TEMPLATE.md
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -15,6 +15,8 @@ _Explain why these changes should be made_
 _Please show how you have added tests to cover your changes_
 
 ### Browser Coverage
+Check the OS/browser combinations tested (At least 2)
+
 Mac
  * [ ] Chrome 
  * [ ] Firefox 
diff --git a/package.json b/package.json
index 3d39030d0ef54406dffbf233afbe4b60a3231d1d..2a5e742a78ea9d08a2edee82fc5fef431ae86224 100644
--- a/package.json
+++ b/package.json
@@ -84,6 +84,7 @@
     "react-tabs": "2.2.1",
     "react-test-renderer": "16.2.0",
     "react-tooltip": "3.4.0",
+    "react-virtualized": "9.18.5",
     "redux": "3.7.2",
     "redux-mock-store": "^1.2.3",
     "redux-throttle": "0.1.1",
@@ -91,10 +92,10 @@
     "scratch-audio": "0.1.0-prerelease.1523977528",
     "scratch-blocks": "0.1.0-prerelease.1526329848",
     "scratch-l10n": "2.0.20180108132626",
-    "scratch-paint": "0.2.0-prerelease.20180504180528",
-    "scratch-render": "0.1.0-prerelease.20180508205430",
+    "scratch-paint": "0.2.0-prerelease.20180514184134",
+    "scratch-render": "0.1.0-prerelease.20180514172756",
     "scratch-storage": "0.4.1",
-    "scratch-svg-renderer": "0.1.0-prerelease.20180423193917",
+    "scratch-svg-renderer": "0.1.0-prerelease.20180514170126",
     "scratch-vm": "0.1.0-prerelease.1526320682",
     "selenium-webdriver": "3.6.0",
     "startaudiocontext": "1.2.1",
diff --git a/src/.eslintrc.js b/src/.eslintrc.js
index 8dab3c69cf9190d315e6c0851ed3760cbf583c8b..14b082cdfe7746734f72a2bb5cc2cefa6a0096d2 100644
--- a/src/.eslintrc.js
+++ b/src/.eslintrc.js
@@ -13,5 +13,10 @@ module.exports = {
         'import/no-amd': 'error',
         'import/no-nodejs-modules': 'error',
         'react/jsx-no-literals': 'error'
+    },
+    settings: {
+        react: {
+            version: '16.2' // Prevent 16.3 lifecycle method errors
+        }
     }
 };
diff --git a/src/components/monitor-list/monitor-list.jsx b/src/components/monitor-list/monitor-list.jsx
index 50263220dd5bbbc164cee2c95bdfbbdf75a5c935..07b6a817be90020956891f340b73a344f5b21afd 100644
--- a/src/components/monitor-list/monitor-list.jsx
+++ b/src/components/monitor-list/monitor-list.jsx
@@ -24,6 +24,7 @@ const MonitorList = props => (
                     opcode={monitorData.opcode}
                     params={monitorData.params}
                     spriteName={monitorData.spriteName}
+                    targetId={monitorData.targetId}
                     value={monitorData.value}
                     width={monitorData.width}
                     x={monitorData.x}
diff --git a/src/components/monitor/list-monitor-scroller.jsx b/src/components/monitor/list-monitor-scroller.jsx
new file mode 100644
index 0000000000000000000000000000000000000000..fd6da9babbef90a2d491e51dca96968b67461c62
--- /dev/null
+++ b/src/components/monitor/list-monitor-scroller.jsx
@@ -0,0 +1,109 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import classNames from 'classnames';
+import bindAll from 'lodash.bindall';
+
+import styles from './monitor.css';
+import {List} from 'react-virtualized';
+
+class ListMonitorScroller extends React.Component {
+    constructor (props) {
+        super(props);
+        bindAll(this, [
+            'rowRenderer',
+            'noRowsRenderer',
+            'handleEventFactory'
+        ]);
+    }
+    handleEventFactory (index) {
+        return () => this.props.onActivate(index);
+    }
+    noRowsRenderer () {
+        return (
+            <div className={styles.listEmpty}>
+                {'(empty)' /* TODO waiting for design before translation */}
+            </div>
+        );
+    }
+    rowRenderer ({index, key, style}) {
+        return (
+            <div
+                className={styles.listRow}
+                key={key}
+                style={style}
+            >
+                <div className={styles.listIndex}>{index + 1 /* one indexed */}</div>
+                <div
+                    className={styles.listValue}
+                    dataIndex={index}
+                    style={{background: this.props.categoryColor}}
+                    onClick={this.handleEventFactory(index)}
+                >
+                    {this.props.activeIndex === index ? (
+                        <div className={styles.inputWrapper}>
+                            <input
+                                autoFocus
+                                autoComplete={false}
+                                className={classNames(styles.listInput, 'no-drag')}
+                                spellCheck={false}
+                                type="text"
+                                value={this.props.activeValue}
+                                onBlur={this.props.onDeactivate}
+                                onChange={this.props.onInput}
+                                onFocus={this.props.onFocus}
+                                onKeyDown={this.props.onKeyPress} // key down to get ahead of blur
+                            />
+                            <div
+                                className={styles.removeButton}
+                                onMouseDown={this.props.onRemove} // mousedown to get ahead of blur
+                            >
+                                {'✖︎'}
+                            </div>
+                        </div>
+
+                    ) : (
+                        <div className={styles.valueInner}>{this.props.values[index]}</div>
+                    )}
+                </div>
+            </div>
+        );
+    }
+    render () {
+        const {height, values, width, activeIndex, activeValue} = this.props;
+        // Keep the active index in view if defined, else must be undefined for List component
+        const scrollToIndex = activeIndex === null ? undefined : activeIndex; /* eslint-disable-line no-undefined */
+        return (
+            <List
+                activeIndex={activeIndex}
+                activeValue={activeValue}
+                height={(height) - 44 /* Header/footer size, approx */}
+                noRowsRenderer={this.noRowsRenderer}
+                rowCount={values.length}
+                rowHeight={24 /* Row size is same for all rows */}
+                rowRenderer={this.rowRenderer}
+                scrollToIndex={scrollToIndex} /* eslint-disable-line no-undefined */
+                values={values}
+                width={width}
+            />
+        );
+    }
+}
+
+ListMonitorScroller.propTypes = {
+    activeIndex: PropTypes.number,
+    activeValue: PropTypes.string,
+    categoryColor: PropTypes.string,
+    height: PropTypes.number,
+    onActivate: PropTypes.func,
+    onDeactivate: PropTypes.func,
+    onFocus: PropTypes.func,
+    onInput: PropTypes.func,
+    onKeyPress: PropTypes.func,
+    onRemove: PropTypes.func,
+    values: PropTypes.arrayOf(PropTypes.oneOfType([
+        PropTypes.string,
+        PropTypes.number
+    ])),
+    width: PropTypes.number
+};
+export default ListMonitorScroller;
diff --git a/src/components/monitor/list-monitor.jsx b/src/components/monitor/list-monitor.jsx
index c50cc5703cf806a294eb7aea39fc57c6f9a3fef7..a29afa283a395da93256321e30df72cd5e379dd9 100644
--- a/src/components/monitor/list-monitor.jsx
+++ b/src/components/monitor/list-monitor.jsx
@@ -1,67 +1,69 @@
 import React from 'react';
 import PropTypes from 'prop-types';
+import classNames from 'classnames';
 import styles from './monitor.css';
+import ListMonitorScroller from './list-monitor-scroller.jsx';
 
-const ListMonitor = ({categoryColor, label, width, height, value}) => (
+const ListMonitor = ({label, width, height, value, onResizeMouseDown, onAdd, ...rowProps}) => (
     <div
         className={styles.listMonitor}
         style={{
-            width: `${width || 80}px`,
-            height: `${height || 200}px`
+            width: `${width}px`,
+            height: `${height}px`
         }}
     >
         <div className={styles.listHeader}>
             {label}
         </div>
         <div className={styles.listBody}>
-            {!value || value.length === 0 ? (
-                <div className={styles.listEmpty}>
-                    {'(empty)' /* @todo not translating, awaiting design */}
-                </div>
-            ) : value.map((v, i) => (
-                <div
-                    className={styles.listRow}
-                    key={`label-${i}`}
-                >
-                    <div className={styles.listIndex}>{i + 1 /* one indexed */}</div>
-                    <div
-                        className={styles.listValue}
-                        style={{background: categoryColor}}
-                    >
-                        <div className={styles.valueInner}>{v}</div>
-                    </div>
-                </div>
-            ))}
+            <ListMonitorScroller
+                height={height}
+                values={value}
+                width={width}
+                {...rowProps}
+            />
         </div>
         <div className={styles.listFooter}>
-            <div className={styles.footerButton}>
-                {/* @todo add button here */}
+            <div
+                className={styles.addButton}
+                onClick={onAdd}
+            >
+                {'+' /* TODO waiting on asset */}
             </div>
             <div className={styles.footerLength}>
-                <span className={styles.lengthNumber}>
-                    {value.length}
-                </span>
+                {`length ${value.length}`}
             </div>
-            <div className={styles.resizeHandle}>
-                {/* @todo resize handle */}
+            <div
+                className={classNames(styles.resizeHandle, 'no-drag')}
+                onMouseDown={onResizeMouseDown}
+            >
+                {'=' /* TODO waiting on asset */}
             </div>
         </div>
     </div>
 );
 
 ListMonitor.propTypes = {
+    activeIndex: PropTypes.number,
     categoryColor: PropTypes.string.isRequired,
     height: PropTypes.number,
     label: PropTypes.string.isRequired,
-    value: PropTypes.arrayOf(PropTypes.oneOfType([
+    onActivate: PropTypes.func,
+    onAdd: PropTypes.func,
+    onResizeMouseDown: PropTypes.func,
+    value: PropTypes.oneOfType([
         PropTypes.string,
-        PropTypes.number
-    ])),
+        PropTypes.number,
+        PropTypes.arrayOf(PropTypes.oneOfType([
+            PropTypes.string,
+            PropTypes.number
+        ]))
+    ]),
     width: PropTypes.number
 };
 
 ListMonitor.defaultProps = {
-    width: 80,
+    width: 110,
     height: 200
 };
 
diff --git a/src/components/monitor/monitor.css b/src/components/monitor/monitor.css
index 783cec7068671c87e8011fb21140929b64afacd1..348f11e0cdee8f91d279438f12c6a2a3548573bc 100644
--- a/src/components/monitor/monitor.css
+++ b/src/components/monitor/monitor.css
@@ -61,7 +61,6 @@
 }
 
 .list-monitor {
-    min-width: 80px;
     display: flex;
     flex-direction: column;
 }
@@ -83,6 +82,7 @@
     display: flex;
     flex-direction: column;
     overflow-y: scroll;
+    overflow-x: hidden;
     height: calc(100% - 44px);
 }
 
@@ -109,6 +109,7 @@
     border-radius: calc($space / 2);
     border: 1px solid $ui-black-transparent;
     flex-grow: 1;
+    height: 22px;
 }
 
 .list-footer {
@@ -137,3 +138,35 @@
     padding: 3px 5px;
     min-height: 22px;
 }
+
+.list-input {
+    padding: 3px 5px;
+    border: 0;
+    background: rgba(0, 0, 0, 0.1);
+    color: $ui-white;
+    outline: none;
+    font-size: 0.75rem;
+    font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
+}
+
+.remove-button {
+    position: absolute;
+    top: 4px;
+    right: 3px;
+    cursor: pointer;
+    color: $ui-white;
+}
+
+.add-button {
+    cursor: pointer;
+    margin-right: 3px;
+}
+
+.resize-handle {
+    cursor: nwse-resize;
+    margin-left: 3px;
+}
+
+.footer-length {
+    text-align: center;
+}
diff --git a/src/components/monitor/monitor.jsx b/src/components/monitor/monitor.jsx
index 95387a1ec78370be823f1bd05ca0d2b670147c39..a45fe52142a26090245023d4e558b720fd3d95cb 100644
--- a/src/components/monitor/monitor.jsx
+++ b/src/components/monitor/monitor.jsx
@@ -7,8 +7,8 @@ import {ContextMenu, MenuItem} from '../context-menu/context-menu.jsx';
 import Box from '../box/box.jsx';
 import DefaultMonitor from './default-monitor.jsx';
 import LargeMonitor from './large-monitor.jsx';
-import SliderMonitor from './slider-monitor.jsx';
-import ListMonitor from './list-monitor.jsx';
+import SliderMonitor from '../../containers/slider-monitor.jsx';
+import ListMonitor from '../../containers/list-monitor.jsx';
 
 import styles from './monitor.css';
 
@@ -29,7 +29,10 @@ const modes = {
 };
 
 const MonitorComponent = props => (
-    <ContextMenuTrigger id={`monitor-${props.label}`}>
+    <ContextMenuTrigger
+        holdToDisplay={props.mode === 'slider' ? -1 : 1000}
+        id={`monitor-${props.label}`}
+    >
         <Draggable
             bounds=".monitor-overlay" // Class for monitor container
             cancel=".no-drag" // Class used for slider input to prevent drag
@@ -41,14 +44,9 @@ const MonitorComponent = props => (
                 componentRef={props.componentRef}
                 onDoubleClick={props.mode === 'list' ? null : props.onNextMode}
             >
-                {(modes[props.mode] || modes.default)({ // Use default until other modes arrive
+                {React.createElement(modes[props.mode], {
                     categoryColor: categories[props.category],
-                    label: props.label,
-                    value: props.value,
-                    width: props.width,
-                    height: props.height,
-                    min: props.min,
-                    max: props.max
+                    ...props
                 })}
             </Box>
         </Draggable>
@@ -90,25 +88,13 @@ const monitorModes = Object.keys(modes);
 MonitorComponent.propTypes = {
     category: PropTypes.oneOf(Object.keys(categories)),
     componentRef: PropTypes.func.isRequired,
-    height: PropTypes.number,
     label: PropTypes.string.isRequired,
-    max: PropTypes.number,
-    min: PropTypes.number,
     mode: PropTypes.oneOf(monitorModes),
     onDragEnd: PropTypes.func.isRequired,
     onNextMode: PropTypes.func.isRequired,
     onSetModeToDefault: PropTypes.func.isRequired,
     onSetModeToLarge: PropTypes.func.isRequired,
-    onSetModeToSlider: PropTypes.func,
-    value: PropTypes.oneOfType([
-        PropTypes.string,
-        PropTypes.number,
-        PropTypes.arrayOf(PropTypes.oneOfType([
-            PropTypes.string,
-            PropTypes.number
-        ]))
-    ]),
-    width: PropTypes.number
+    onSetModeToSlider: PropTypes.func
 };
 
 MonitorComponent.defaultProps = {
diff --git a/src/components/monitor/slider-monitor.jsx b/src/components/monitor/slider-monitor.jsx
index a6c410898edc6325df5d3cfafd060a9935eee34c..f8187cfee6a9be6fe0811ddfb8751c00bf0eaa64 100644
--- a/src/components/monitor/slider-monitor.jsx
+++ b/src/components/monitor/slider-monitor.jsx
@@ -2,7 +2,7 @@ import React from 'react';
 import PropTypes from 'prop-types';
 import styles from './monitor.css';
 
-const SliderMonitor = ({categoryColor, label, min, max, value}) => (
+const SliderMonitor = ({categoryColor, label, min, max, value, onSliderUpdate}) => (
     <div className={styles.defaultMonitor}>
         <div className={styles.row}>
             <div className={styles.label}>
@@ -22,6 +22,7 @@ const SliderMonitor = ({categoryColor, label, min, max, value}) => (
                 min={min}
                 type="range"
                 value={value}
+                onChange={onSliderUpdate}
                 // @todo onChange callback
             />
         </div>
@@ -34,7 +35,7 @@ SliderMonitor.propTypes = {
     label: PropTypes.string.isRequired,
     max: PropTypes.number,
     min: PropTypes.number,
-    // @todo callback for change events
+    onSliderUpdate: PropTypes.func.isRequired,
     value: PropTypes.oneOfType([
         PropTypes.string,
         PropTypes.number
diff --git a/src/containers/list-monitor.jsx b/src/containers/list-monitor.jsx
new file mode 100644
index 0000000000000000000000000000000000000000..4b7cc80dbc299c0830b135a11e4318aad645688f
--- /dev/null
+++ b/src/containers/list-monitor.jsx
@@ -0,0 +1,183 @@
+import bindAll from 'lodash.bindall';
+import PropTypes from 'prop-types';
+import React from 'react';
+import VM from 'scratch-vm';
+import {connect} from 'react-redux';
+import {getEventXY} from '../lib/touch-utils';
+import {getVariableValue, setVariableValue} from '../lib/variable-utils';
+import ListMonitorComponent from '../components/monitor/list-monitor.jsx';
+
+class ListMonitor extends React.Component {
+    constructor (props) {
+        super(props);
+        bindAll(this, [
+            'handleActivate',
+            'handleDeactivate',
+            'handleInput',
+            'handleRemove',
+            'handleKeyPress',
+            'handleFocus',
+            'handleAdd',
+            'handleResizeMouseDown'
+        ]);
+
+        this.state = {
+            activeIndex: null,
+            activeValue: null,
+            // TODO These will need to be sent back to the VM for saving
+            width: props.width || 100,
+            height: props.height || 200
+        };
+    }
+
+    handleActivate (index) {
+        this.setState({
+            activeIndex: index,
+            activeValue: this.props.value[index]
+        });
+    }
+
+    handleDeactivate () {
+        // Submit any in-progress value edits on blur
+        if (this.state.activeIndex !== null) {
+            const {vm, targetId, id: variableId} = this.props;
+            const newListValue = getVariableValue(vm, targetId, variableId);
+            newListValue[this.state.activeIndex] = this.state.activeValue;
+            setVariableValue(vm, targetId, variableId, newListValue);
+            this.setState({activeIndex: null, activeValue: null});
+        }
+    }
+
+    handleFocus (e) {
+        // Select all the text in the input when it is focused.
+        e.target.select();
+    }
+
+    handleKeyPress (e) {
+        // Special case for tab, arrow keys and enter.
+        // Tab / shift+tab navigate down / up the list.
+        // Arrow down / arrow up navigate down / up the list.
+        // Enter / shift+enter insert new blank item below / above.
+        const previouslyActiveIndex = this.state.activeIndex;
+        const {vm, targetId, id: variableId} = this.props;
+
+        let navigateDirection = 0;
+        if (e.key === 'Tab') navigateDirection = e.shiftKey ? -1 : 1;
+        else if (e.key === 'ArrowUp') navigateDirection = -1;
+        else if (e.key === 'ArrowDown') navigateDirection = 1;
+        if (navigateDirection) {
+            this.handleDeactivate(); // Submit in-progress edits
+            const newIndex = (previouslyActiveIndex + navigateDirection) % this.props.value.length;
+            this.setState({
+                activeIndex: newIndex,
+                activeValue: this.props.value[newIndex]
+            });
+            e.preventDefault(); // Stop default tab behavior, handled by this state change
+        } else if (e.key === 'Enter') {
+            this.handleDeactivate(); // Submit in-progress edits
+            const newListItemValue = ''; // Enter adds a blank item
+            const newValueOffset = e.shiftKey ? 0 : 1; // Shift-enter inserts above
+            const listValue = getVariableValue(vm, targetId, variableId);
+            const newListValue = listValue.slice(0, previouslyActiveIndex + newValueOffset)
+                .concat([newListItemValue])
+                .concat(listValue.slice(previouslyActiveIndex + newValueOffset));
+            setVariableValue(vm, targetId, variableId, newListValue);
+            const newIndex = (previouslyActiveIndex + newValueOffset) % newListValue.length;
+            this.setState({
+                activeIndex: newIndex,
+                activeValue: newListItemValue
+            });
+        }
+    }
+
+    handleInput (e) {
+        this.setState({activeValue: e.target.value});
+    }
+
+    handleRemove (e) {
+        e.preventDefault(); // Default would blur input, prevent that.
+        e.stopPropagation(); // Bubbling would activate, which will be handled here
+        const {vm, targetId, id: variableId} = this.props;
+        const listValue = getVariableValue(vm, targetId, variableId);
+        const newListValue = listValue.slice(0, this.state.activeIndex)
+            .concat(listValue.slice(this.state.activeIndex + 1));
+        setVariableValue(vm, targetId, variableId, newListValue);
+        this.handleActivate(Math.min(newListValue.length - 1, this.state.activeIndex));
+    }
+
+    handleAdd () {
+        // Add button appends a blank value and switches to it
+        const {vm, targetId, id: variableId} = this.props;
+        const newListValue = getVariableValue(vm, targetId, variableId).concat(['']);
+        setVariableValue(vm, targetId, variableId, newListValue);
+        this.setState({activeIndex: newListValue.length - 1, activeValue: ''});
+    }
+
+    handleResizeMouseDown (e) {
+        this.initialPosition = getEventXY(e);
+        this.initialWidth = this.state.width;
+        this.initialHeight = this.state.height;
+
+        const onMouseMove = ev => {
+            const newPosition = getEventXY(ev);
+            const dx = newPosition.x - this.initialPosition.x;
+            const dy = newPosition.y - this.initialPosition.y;
+            this.setState({
+                width: Math.max(Math.min(this.initialWidth + dx, 480), 100),
+                height: Math.max(Math.min(this.initialHeight + dy, 360), 60)
+            });
+        };
+
+        const onMouseUp = ev => {
+            onMouseMove(ev); // Make sure width/height are up-to-date
+            // TODO send these new sizes to the VM for saving
+            window.removeEventListener('mousemove', onMouseMove);
+            window.removeEventListener('mouseup', onMouseUp);
+        };
+
+        window.addEventListener('mousemove', onMouseMove);
+        window.addEventListener('mouseup', onMouseUp);
+
+    }
+    render () {
+        const {
+            vm, // eslint-disable-line no-unused-vars
+            ...props
+        } = this.props;
+        return (
+            <ListMonitorComponent
+                {...props}
+                activeIndex={this.state.activeIndex}
+                activeValue={this.state.activeValue}
+                height={this.state.height}
+                width={this.state.width}
+                onActivate={this.handleActivate}
+                onAdd={this.handleAdd}
+                onDeactivate={this.handleDeactivate}
+                onFocus={this.handleFocus}
+                onInput={this.handleInput}
+                onKeyPress={this.handleKeyPress}
+                onRemove={this.handleRemove}
+                onResizeMouseDown={this.handleResizeMouseDown}
+            />
+        );
+    }
+}
+
+ListMonitor.propTypes = {
+    height: PropTypes.number,
+    id: PropTypes.string,
+    targetId: PropTypes.string,
+    value: PropTypes.oneOfType([
+        PropTypes.number,
+        PropTypes.string
+    ]),
+    vm: PropTypes.instanceOf(VM),
+    width: PropTypes.number,
+    x: PropTypes.number,
+    y: PropTypes.number
+};
+
+const mapStateToProps = state => ({vm: state.vm});
+
+export default connect(mapStateToProps)(ListMonitor);
diff --git a/src/containers/monitor-list.jsx b/src/containers/monitor-list.jsx
index 85a3b20e3ec840808e27b99f7c81b6a338022cf4..b3dcd5b7c7cfb2943f27caab43a0ff80191037ce 100644
--- a/src/containers/monitor-list.jsx
+++ b/src/containers/monitor-list.jsx
@@ -5,6 +5,8 @@ import PropTypes from 'prop-types';
 import {connect} from 'react-redux';
 import {moveMonitorRect} from '../reducers/monitor-layout';
 
+import errorBoundaryHOC from '../lib/error-boundary-hoc.jsx';
+
 import MonitorListComponent from '../components/monitor-list/monitor-list.jsx';
 
 class MonitorList extends React.Component {
@@ -37,7 +39,9 @@ const mapDispatchToProps = dispatch => ({
     moveMonitorRect: (id, x, y) => dispatch(moveMonitorRect(id, x, y))
 });
 
-export default connect(
-    mapStateToProps,
-    mapDispatchToProps
-)(MonitorList);
+export default errorBoundaryHOC('Monitors')(
+    connect(
+        mapStateToProps,
+        mapDispatchToProps
+    )(MonitorList)
+);
diff --git a/src/containers/monitor.jsx b/src/containers/monitor.jsx
index cdb87f20eacea93fcafa446ee379ea4ba61f1ca8..b2480a41299ed592a11a0fa115eea292ba81c6fb 100644
--- a/src/containers/monitor.jsx
+++ b/src/containers/monitor.jsx
@@ -107,6 +107,7 @@ class Monitor extends React.Component {
                 max={this.props.max}
                 min={this.props.min}
                 mode={this.state.mode}
+                targetId={this.props.targetId}
                 width={this.props.width}
                 onDragEnd={this.handleDragEnd}
                 onNextMode={this.handleNextMode}
@@ -135,6 +136,7 @@ Monitor.propTypes = {
     removeMonitorRect: PropTypes.func.isRequired,
     resizeMonitorRect: PropTypes.func.isRequired,
     spriteName: PropTypes.string, // eslint-disable-line react/no-unused-prop-types
+    targetId: PropTypes.string,
     value: PropTypes.oneOfType([
         PropTypes.string,
         PropTypes.number,
diff --git a/src/containers/slider-monitor.jsx b/src/containers/slider-monitor.jsx
new file mode 100644
index 0000000000000000000000000000000000000000..8df6f92a9879c7aaa9ee364424ac0dd955b3e688
--- /dev/null
+++ b/src/containers/slider-monitor.jsx
@@ -0,0 +1,59 @@
+import bindAll from 'lodash.bindall';
+import PropTypes from 'prop-types';
+import React from 'react';
+import VM from 'scratch-vm';
+import {setVariableValue} from '../lib/variable-utils';
+import {connect} from 'react-redux';
+
+import SliderMonitorComponent from '../components/monitor/slider-monitor.jsx';
+
+class SliderMonitor extends React.Component {
+    constructor (props) {
+        super(props);
+        bindAll(this, [
+            'handleSliderUpdate'
+        ]);
+
+        this.state = {
+            value: Number(props.value)
+        };
+    }
+    componentWillReceiveProps (nextProps) {
+        if (this.state.value !== nextProps.value) {
+            this.setState({value: nextProps.value});
+        }
+    }
+    handleSliderUpdate (e) {
+        this.setState({value: Number(e.target.value)});
+        const {vm, targetId, id: variableId} = this.props;
+        setVariableValue(vm, targetId, variableId, Number(e.target.value));
+    }
+    render () {
+        const {
+            vm, // eslint-disable-line no-unused-vars
+            value, // eslint-disable-line no-unused-vars
+            ...props
+        } = this.props;
+        return (
+            <SliderMonitorComponent
+                {...props}
+                value={this.state.value}
+                onSliderUpdate={this.handleSliderUpdate}
+            />
+        );
+    }
+}
+
+SliderMonitor.propTypes = {
+    id: PropTypes.string,
+    targetId: PropTypes.string,
+    value: PropTypes.oneOfType([
+        PropTypes.number,
+        PropTypes.string
+    ]),
+    vm: PropTypes.instanceOf(VM)
+};
+
+const mapStateToProps = state => ({vm: state.vm});
+
+export default connect(mapStateToProps)(SliderMonitor);
diff --git a/src/containers/stage.jsx b/src/containers/stage.jsx
index 57614135f9b15eb2c799193f6c515623892bb34a..ee3154a2207aa8769815a78950044a6ec2cbf768 100644
--- a/src/containers/stage.jsx
+++ b/src/containers/stage.jsx
@@ -7,6 +7,7 @@ import {connect} from 'react-redux';
 
 import {getEventXY} from '../lib/touch-utils';
 import VideoProvider from '../lib/video/video-provider';
+import {SVGRenderer as V2SVGAdapter} from 'scratch-svg-renderer';
 
 import StageComponent from '../components/stage/stage.jsx';
 
@@ -57,6 +58,7 @@ class Stage extends React.Component {
         this.updateRect();
         this.renderer = new Renderer(this.canvas);
         this.props.vm.attachRenderer(this.renderer);
+        this.props.vm.attachV2SVGAdapter(new V2SVGAdapter());
         this.props.vm.runtime.addListener('QUESTION', this.questionListener);
         this.props.vm.setVideoProvider(new VideoProvider());
     }
@@ -81,6 +83,7 @@ class Stage extends React.Component {
         this.detachMouseEvents(this.canvas);
         this.detachRectEvents();
         this.stopColorPickingLoop();
+        this.props.vm.runtime.removeListener('QUESTION', this.questionListener);
     }
     questionListener (question) {
         this.setState({question: question});
diff --git a/src/lib/default-project/index.js b/src/lib/default-project/index.js
index 4f4b3f113cd36ccd78d7375bd99e6711f2ea2777..f1cacd3659abfc82fd8a10ca9bbc73fbcd69db59 100644
--- a/src/lib/default-project/index.js
+++ b/src/lib/default-project/index.js
@@ -19,12 +19,12 @@ export default [{
     id: '83a9787d4cb6f3b7632b4ddfebf74367',
     assetType: 'Sound',
     dataFormat: 'WAV',
-    data: new Uint16Array(popWav)
+    data: new Uint8Array(popWav)
 }, {
     id: '83c36d806dc92327b9e7049a565c6bff',
     assetType: 'Sound',
     dataFormat: 'WAV',
-    data: new Uint16Array(meowWav)
+    data: new Uint8Array(meowWav)
 }, {
     id: '739b5e2a2435f6e1ec2993791b423146',
     assetType: 'ImageBitmap',
diff --git a/src/lib/variable-utils.js b/src/lib/variable-utils.js
new file mode 100644
index 0000000000000000000000000000000000000000..12bcf4704fb343b0fdc826f5cd2dd0058c00cdbd
--- /dev/null
+++ b/src/lib/variable-utils.js
@@ -0,0 +1,24 @@
+// Utility functions for updating variables in the VM
+// TODO (VM#1145) these should be moved to top-level VM API
+const getVariable = (vm, targetId, variableId) => {
+    const target = targetId ?
+        vm.runtime.getTargetById(targetId) :
+        vm.runtime.getTargetForStage();
+    return target.variables[variableId];
+};
+
+const getVariableValue = (vm, targetId, variableId) => {
+    const variable = getVariable(vm, targetId, variableId);
+    // If array, return a new copy for mutating, ensuring that updates stay immutable.
+    if (variable.value instanceof Array) return variable.value.slice();
+    return variable.value;
+};
+
+const setVariableValue = (vm, targetId, variableId, value) => {
+    getVariable(vm, targetId, variableId).value = value;
+};
+
+export {
+    getVariableValue,
+    setVariableValue
+};