diff --git a/test/fixtures/cloud_variables_exceeded_limit.sb2 b/test/fixtures/cloud_variables_exceeded_limit.sb2
new file mode 100644
index 000000000..cf1cc0850
Binary files /dev/null and b/test/fixtures/cloud_variables_exceeded_limit.sb2 differ
diff --git a/test/fixtures/cloud_variables_exceeded_limit.sb3 b/test/fixtures/cloud_variables_exceeded_limit.sb3
new file mode 100644
index 000000000..ce1b78ac7
Binary files /dev/null and b/test/fixtures/cloud_variables_exceeded_limit.sb3 differ
diff --git a/test/fixtures/cloud_variables_limit.sb2 b/test/fixtures/cloud_variables_limit.sb2
new file mode 100644
index 000000000..e33de969d
Binary files /dev/null and b/test/fixtures/cloud_variables_limit.sb2 differ
diff --git a/test/fixtures/cloud_variables_limit.sb3 b/test/fixtures/cloud_variables_limit.sb3
new file mode 100644
index 000000000..4907e50f0
Binary files /dev/null and b/test/fixtures/cloud_variables_limit.sb3 differ
diff --git a/test/fixtures/cloud_variables_local.sb2 b/test/fixtures/cloud_variables_local.sb2
new file mode 100644
index 000000000..61e4556e3
Binary files /dev/null and b/test/fixtures/cloud_variables_local.sb2 differ
diff --git a/test/fixtures/cloud_variables_local.sb3 b/test/fixtures/cloud_variables_local.sb3
new file mode 100644
index 000000000..35df5de92
Binary files /dev/null and b/test/fixtures/cloud_variables_local.sb3 differ
diff --git a/test/fixtures/cloud_variables_simple.sb2 b/test/fixtures/cloud_variables_simple.sb2
new file mode 100644
index 000000000..ddbf782d7
Binary files /dev/null and b/test/fixtures/cloud_variables_simple.sb2 differ
diff --git a/test/fixtures/cloud_variables_simple.sb3 b/test/fixtures/cloud_variables_simple.sb3
new file mode 100644
index 000000000..a25bd6252
Binary files /dev/null and b/test/fixtures/cloud_variables_simple.sb3 differ
diff --git a/test/integration/cloud_variables_sb2.js b/test/integration/cloud_variables_sb2.js
new file mode 100644
index 000000000..e42cbe499
--- /dev/null
+++ b/test/integration/cloud_variables_sb2.js
@@ -0,0 +1,152 @@
+const path = require('path');
+const test = require('tap').test;
+const makeTestStorage = require('../fixtures/make-test-storage');
+const readFileToBuffer = require('../fixtures/readProjectFile').readFileToBuffer;
+const VirtualMachine = require('../../src/index');
+
+const cloudVarSimpleUri = path.resolve(__dirname, '../fixtures/cloud_variables_simple.sb2');
+const cloudVarLimitUri = path.resolve(__dirname, '../fixtures/cloud_variables_limit.sb2');
+const cloudVarExceededLimitUri = path.resolve(__dirname, '../fixtures/cloud_variables_exceeded_limit.sb2');
+const cloudVarLocalUri = path.resolve(__dirname, '../fixtures/cloud_variables_local.sb2');
+
+const cloudVarSimple = readFileToBuffer(cloudVarSimpleUri);
+const cloudVarLimit = readFileToBuffer(cloudVarLimitUri);
+const cloudVarExceededLimit = readFileToBuffer(cloudVarExceededLimitUri);
+const cloudVarLocal = readFileToBuffer(cloudVarLocalUri);
+
+test('importing an sb2 project with cloud variables', t => {
+    const vm = new VirtualMachine();
+    vm.attachStorage(makeTestStorage());
+
+    // Start VM, load project, and run
+    vm.start();
+    vm.clear();
+    vm.setCompatibilityMode(false);
+    vm.setTurboMode(false);
+    vm.loadProject(cloudVarSimple).then(() => {
+        t.equal(vm.runtime.hasCloudData, true);
+
+        const stage = vm.runtime.targets[0];
+        const stageVars = Object.values(stage.variables);
+        t.equal(stageVars.length, 1);
+
+        const variable = stageVars[0];
+        t.equal(variable.name, '☁ firstCloud');
+        t.equal(Number(variable.value), 100); // Though scratch 2 requires
+        // cloud variables to be numbers, this is something that happens
+        // when the message is being sent to the server rather than on the client
+        t.equal(variable.isCloud, true);
+
+        t.end();
+    });
+});
+
+test('importing an sb2 project with cloud variables at the limit for a project', t => {
+    const vm = new VirtualMachine();
+    vm.attachStorage(makeTestStorage());
+
+    // Start VM, load project, and run
+    vm.start();
+    vm.clear();
+    vm.setCompatibilityMode(false);
+    vm.setTurboMode(false);
+    vm.loadProject(cloudVarLimit).then(() => {
+        t.equal(vm.runtime.hasCloudData, true);
+
+        const stage = vm.runtime.targets[0];
+        const stageVars = Object.values(stage.variables);
+
+        t.equal(stageVars.length, 8);
+        // All of the 8 stage variables should be cloud variables
+        t.equal(stageVars.filter(v => v.isCloud).length, 8);
+
+        t.end();
+    });
+});
+
+test('importing an sb2 project with cloud variables exceeding the limit for a project', t => {
+    // This tests a hacked project where additional cloud variables exceeding
+    // the project limit have been added.
+    const vm = new VirtualMachine();
+    vm.attachStorage(makeTestStorage());
+
+    // Start VM, load project, and run
+    vm.start();
+    vm.clear();
+    vm.setCompatibilityMode(false);
+    vm.setTurboMode(false);
+    vm.loadProject(cloudVarExceededLimit).then(() => {
+        t.equal(vm.runtime.hasCloudData, true);
+
+        const stage = vm.runtime.targets[0];
+        const stageVars = Object.values(stage.variables);
+
+        t.equal(stageVars.length, 15);
+        // Only 8 of the variables should have the isCloud flag set to true
+        t.equal(stageVars.filter(v => v.isCloud).length, 8);
+
+        t.end();
+    });
+});
+
+test('importing one project after the other resets cloud variable limit', t => {
+    const vm = new VirtualMachine();
+    vm.attachStorage(makeTestStorage());
+
+    // Start VM, load project, and run
+    vm.start();
+    vm.clear();
+    vm.setCompatibilityMode(false);
+    vm.setTurboMode(false);
+    vm.loadProject(cloudVarExceededLimit).then(() => {
+        t.equal(vm.runtime.canAddNewCloudVariable(), false);
+
+        vm.loadProject(cloudVarSimple).then(() => {
+            const stage = vm.runtime.targets[0];
+            const stageVars = Object.values(stage.variables);
+            t.equal(stageVars.length, 1);
+
+            const variable = stageVars[0];
+            t.equal(variable.name, '☁ firstCloud');
+            t.equal(Number(variable.value), 100); // Though scratch 2 requires
+            // cloud variables to be numbers, this is something that happens
+            // when the message is being sent to the server rather than on the client
+            t.equal(variable.isCloud, true);
+
+            t.equal(vm.runtime.canAddNewCloudVariable(), true);
+
+            t.end();
+        });
+    });
+});
+
+test('local cloud variables get imported as regular variables', t => {
+    // This tests a hacked project where additional cloud variables exceeding
+    // the project limit have been added.
+    const vm = new VirtualMachine();
+    vm.attachStorage(makeTestStorage());
+
+    // Start VM, load project, and run
+    vm.start();
+    vm.clear();
+    vm.setCompatibilityMode(false);
+    vm.setTurboMode(false);
+    vm.loadProject(cloudVarLocal).then(() => {
+        t.equal(vm.runtime.hasCloudData, false);
+
+        const stage = vm.runtime.targets[0];
+        const stageVars = Object.values(stage.variables);
+
+        t.equal(stageVars.length, 0);
+
+        const sprite = vm.runtime.targets[1];
+        const spriteVars = Object.values(sprite.variables);
+
+        t.equal(spriteVars.length, 1);
+        t.equal(spriteVars[0].isCloud, false);
+
+        t.end();
+
+        process.nextTick(process.exit); // This is needed because this is the end of the last test in this file!!!
+    });
+});
diff --git a/test/integration/cloud_variables_sb3.js b/test/integration/cloud_variables_sb3.js
new file mode 100644
index 000000000..385af3aaa
--- /dev/null
+++ b/test/integration/cloud_variables_sb3.js
@@ -0,0 +1,148 @@
+const path = require('path');
+const test = require('tap').test;
+const makeTestStorage = require('../fixtures/make-test-storage');
+const readFileToBuffer = require('../fixtures/readProjectFile').readFileToBuffer;
+const VirtualMachine = require('../../src/index');
+
+const cloudVarSimpleUri = path.resolve(__dirname, '../fixtures/cloud_variables_simple.sb3');
+const cloudVarLimitUri = path.resolve(__dirname, '../fixtures/cloud_variables_limit.sb3');
+const cloudVarExceededLimitUri = path.resolve(__dirname, '../fixtures/cloud_variables_exceeded_limit.sb3');
+const cloudVarLocalUri = path.resolve(__dirname, '../fixtures/cloud_variables_local.sb3');
+
+const cloudVarSimple = readFileToBuffer(cloudVarSimpleUri);
+const cloudVarLimit = readFileToBuffer(cloudVarLimitUri);
+const cloudVarExceededLimit = readFileToBuffer(cloudVarExceededLimitUri);
+const cloudVarLocal = readFileToBuffer(cloudVarLocalUri);
+
+test('importing an sb3 project with cloud variables', t => {
+    const vm = new VirtualMachine();
+    vm.attachStorage(makeTestStorage());
+
+    // Start VM, load project, and run
+    vm.start();
+    vm.clear();
+    vm.setCompatibilityMode(false);
+    vm.setTurboMode(false);
+    vm.loadProject(cloudVarSimple).then(() => {
+        t.equal(vm.runtime.hasCloudData, true);
+
+        const stage = vm.runtime.targets[0];
+        const stageVars = Object.values(stage.variables);
+        t.equal(stageVars.length, 1);
+
+        const variable = stageVars[0];
+        t.equal(variable.name, '☁ firstCloud');
+        t.equal(Number(variable.value), 100);
+        t.equal(variable.isCloud, true);
+
+        t.end();
+    });
+});
+
+test('importing an sb3 project with cloud variables at the limit for a project', t => {
+    const vm = new VirtualMachine();
+    vm.attachStorage(makeTestStorage());
+
+    // Start VM, load project, and run
+    vm.start();
+    vm.clear();
+    vm.setCompatibilityMode(false);
+    vm.setTurboMode(false);
+    vm.loadProject(cloudVarLimit).then(() => {
+        t.equal(vm.runtime.hasCloudData, true);
+
+        const stage = vm.runtime.targets[0];
+        const stageVars = Object.values(stage.variables);
+
+        t.equal(stageVars.length, 8);
+        // All of the 8 stage variables should be cloud variables
+        t.equal(stageVars.filter(v => v.isCloud).length, 8);
+
+        t.end();
+    });
+});
+
+test('importing an sb3 project with cloud variables exceeding the limit for a project', t => {
+    // This tests a hacked project where additional cloud variables exceeding
+    // the project limit have been added.
+    const vm = new VirtualMachine();
+    vm.attachStorage(makeTestStorage());
+
+    // Start VM, load project, and run
+    vm.start();
+    vm.clear();
+    vm.setCompatibilityMode(false);
+    vm.setTurboMode(false);
+    vm.loadProject(cloudVarExceededLimit).then(() => {
+        t.equal(vm.runtime.hasCloudData, true);
+
+        const stage = vm.runtime.targets[0];
+        const stageVars = Object.values(stage.variables);
+
+        t.equal(stageVars.length, 15);
+        // Only 8 of the variables should have the isCloud flag set to true
+        t.equal(stageVars.filter(v => v.isCloud).length, 8);
+
+        t.end();
+    });
+});
+
+test('importing one project after the other resets cloud variable limit', t => {
+    const vm = new VirtualMachine();
+    vm.attachStorage(makeTestStorage());
+
+    // Start VM, load project, and run
+    vm.start();
+    vm.clear();
+    vm.setCompatibilityMode(false);
+    vm.setTurboMode(false);
+    vm.loadProject(cloudVarExceededLimit).then(() => {
+        t.equal(vm.runtime.canAddNewCloudVariable(), false);
+
+        vm.loadProject(cloudVarSimple).then(() => {
+            const stage = vm.runtime.targets[0];
+            const stageVars = Object.values(stage.variables);
+            t.equal(stageVars.length, 1);
+
+            const variable = stageVars[0];
+            t.equal(variable.name, '☁ firstCloud');
+            t.equal(Number(variable.value), 100);
+            t.equal(variable.isCloud, true);
+
+            t.equal(vm.runtime.canAddNewCloudVariable(), true);
+
+            t.end();
+        });
+    });
+});
+
+test('local cloud variables get imported as regular variables', t => {
+    // This tests a hacked project where additional cloud variables exceeding
+    // the project limit have been added.
+    const vm = new VirtualMachine();
+    vm.attachStorage(makeTestStorage());
+
+    // Start VM, load project, and run
+    vm.start();
+    vm.clear();
+    vm.setCompatibilityMode(false);
+    vm.setTurboMode(false);
+    vm.loadProject(cloudVarLocal).then(() => {
+        t.equal(vm.runtime.hasCloudData, false);
+
+        const stage = vm.runtime.targets[0];
+        const stageVars = Object.values(stage.variables);
+
+        t.equal(stageVars.length, 0);
+
+        const sprite = vm.runtime.targets[1];
+        const spriteVars = Object.values(sprite.variables);
+
+        t.equal(spriteVars.length, 1);
+        t.equal(spriteVars[0].isCloud, false);
+
+        t.end();
+
+        process.nextTick(process.exit); // This is needed because this is the end of the last test in this file!!!
+    });
+});
diff --git a/test/unit/engine_runtime.js b/test/unit/engine_runtime.js
index a82900191..c5788c240 100644
--- a/test/unit/engine_runtime.js
+++ b/test/unit/engine_runtime.js
@@ -127,3 +127,38 @@ test('Project loaded emits runtime event', t => {
         t.end();
     });
 });
+
+test('Cloud variable limit allows only 8 cloud variables', t => {
+    // This is a test of just the cloud variable limit mechanism
+    // The functions being tested below need to be used when
+    // creating and deleting cloud variables in the runtime.
+
+    const rt = new Runtime();
+
+    for (let i = 0; i < 8; i++) {
+        t.equal(rt.canAddNewCloudVariable(), true);
+    }
+
+    // We should be at the cloud variable limit now
+    t.equal(rt.canAddNewCloudVariable(), false);
+
+    // Removing a cloud variable should allow the addition of exactly one more
+    // when we are at the cloud variable limit
+    rt.removeExistingCloudVariable();
+
+    t.equal(rt.canAddNewCloudVariable(), true);
+    t.equal(rt.canAddNewCloudVariable(), false);
+
+    // Disposing of the runtime should reset the cloud variable limitations
+    rt.dispose();
+
+    for (let i = 0; i < 8; i++) {
+        t.equal(rt.canAddNewCloudVariable(), true);
+    }
+
+    // We should be at the cloud variable limit now
+    t.equal(rt.canAddNewCloudVariable(), false);
+
+    t.end();
+
+});