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..cd8efa9f7 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