diff --git a/src/engine/runtime.js b/src/engine/runtime.js
index 8761c66b3..d3a1723a4 100644
--- a/src/engine/runtime.js
+++ b/src/engine/runtime.js
@@ -392,6 +392,13 @@ class Runtime extends EventEmitter {
          * @type {function}
          */
         this.removeCloudVariable = this._initializeRemoveCloudVariable(newCloudDataManager);
+
+        /**
+         * A string representing the origin of the current project from outside of the
+         * Scratch community, such as CSFirst.
+         * @type {?string}
+         */
+        this.origin = null;
     }
 
     /**
diff --git a/src/serialization/sb3.js b/src/serialization/sb3.js
index bb82390a1..6989229c9 100644
--- a/src/serialization/sb3.js
+++ b/src/serialization/sb3.js
@@ -561,6 +561,9 @@ const serialize = function (runtime, targetId) {
     const meta = Object.create(null);
     meta.semver = '3.0.0';
     meta.vm = vmPackage.version;
+    if (runtime.origin) {
+        meta.origin = runtime.origin;
+    }
 
     // Attach full user agent string to metadata if available
     meta.agent = 'none';
@@ -1235,6 +1238,13 @@ const deserialize = function (json, runtime, zip, isSingleSprite) {
         extensionURLs: new Map()
     };
 
+    // Store the origin field (e.g. project originated at CSFirst) so that we can save it again.
+    if (json.meta && json.meta.origin) {
+        runtime.origin = json.meta.origin;
+    } else {
+        runtime.origin = null;
+    }
+
     // First keep track of the current target order in the json,
     // then sort by the layer order property before parsing the targets
     // so that their corresponding render drawables can be created in
diff --git a/test/fixtures/origin-absent.sb3 b/test/fixtures/origin-absent.sb3
new file mode 100644
index 000000000..c6c7755d8
Binary files /dev/null and b/test/fixtures/origin-absent.sb3 differ
diff --git a/test/fixtures/origin.sb3 b/test/fixtures/origin.sb3
new file mode 100644
index 000000000..b8356a1f0
Binary files /dev/null and b/test/fixtures/origin.sb3 differ
diff --git a/test/unit/serialization_sb3.js b/test/unit/serialization_sb3.js
index baeb23543..efba61e92 100644
--- a/test/unit/serialization_sb3.js
+++ b/test/unit/serialization_sb3.js
@@ -11,6 +11,8 @@ const commentsSB3NoDupeIds = path.resolve(__dirname, '../fixtures/comments_no_du
 const variableReporterSB2ProjectPath = path.resolve(__dirname, '../fixtures/top-level-variable-reporter.sb2');
 const topLevelReportersProjectPath = path.resolve(__dirname, '../fixtures/top-level-reporters.sb3');
 const draggableSB3ProjectPath = path.resolve(__dirname, '../fixtures/draggable.sb3');
+const originSB3ProjectPath = path.resolve(__dirname, '../fixtures/origin.sb3');
+const originAbsentSB3ProjectPath = path.resolve(__dirname, '../fixtures/origin-absent.sb3');
 const FakeRenderer = require('../fixtures/fake-renderer');
 
 test('serialize', t => {
@@ -324,3 +326,38 @@ test('(#1850) sprite draggability state read when loading SB3 file', t => {
             t.end();
         });
 });
+
+test('load origin value from SB3 file json metadata', t => {
+    const vm = new VirtualMachine();
+    vm.loadProject(readFileToBuffer(originSB3ProjectPath))
+        .then(() => {
+            t.type(vm.runtime.origin, 'string');
+        })
+        .then(() => vm.loadProject(readFileToBuffer(originAbsentSB3ProjectPath)))
+        .then(() => {
+            // After loading a project with an origin, then loading one without an origin,
+            // origin value should no longer be set.
+            t.equal(vm.runtime.origin, null);
+            t.end();
+        });
+});
+
+test('serialize origin value if it is present', t => {
+    const vm = new VirtualMachine();
+    vm.loadProject(readFileToBuffer(originSB3ProjectPath))
+        .then(() => {
+            const result = sb3.serialize(vm.runtime);
+            t.type(result.meta.origin, 'string');
+            t.end();
+        });
+});
+
+test('do not serialize origin value if it is not present', t => {
+    const vm = new VirtualMachine();
+    vm.loadProject(readFileToBuffer(originAbsentSB3ProjectPath))
+        .then(() => {
+            const result = sb3.serialize(vm.runtime);
+            t.equal(result.meta.origin, undefined);
+            t.end();
+        });
+});