Skip to content

How to effeciently Store/Load the entire structure of A-Scene into/from the DB? #5714

@hazho

Description

@hazho

I have tried my best to store and load the same structure that I visually see into the DB, but the component's schema keys and values are not being included, also, even if I store the HTML content into the DB, when loaded, most of the entities' component values lost, the following are simplified examples tried with:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>A-Frame Scene Saver</title>
    <script src="https://aframe.io/releases/1.7.1/aframe.min.js"></script>
</head>
<body>
		<div class="html">
			<h2>Scene Management</h2>
			<button onclick="createScene()">Add a Scene</button>
			<select id="sceneSelect">
					<option value="">Select a saved scene...</option>
			</select>
			<button onclick="loadScene()">Load Scene</button>
			<button onclick="saveScene()">Save Scene</button>
			<input type="text" id="sceneName" placeholder="Enter scene name to save">
		</div>
    <div id="sceneContainer"></div>

    <script>
        document.addEventListener("DOMContentLoaded", populateScenes);

        function createScene() {
            const sceneContainer = document.getElementById("sceneContainer");
            if (!document.querySelector("a-scene")) {
                sceneContainer.innerHTML = `
                    <a-scene>
                        <a-entity camera look-controls position="0 1.6 0"></a-entity>
                    </a-scene>
                `;
            }
        }

        function populateScenes() {
            const sceneSelect = document.getElementById("sceneSelect");
            const scenes = JSON.parse(localStorage.getItem("scenes") || "{}");

            sceneSelect.innerHTML = '<option value="">Select a saved scene...</option>';
            for (let name in scenes) {
                let option = document.createElement("option");
                option.value = name;
                option.textContent = name;
                sceneSelect.appendChild(option);
            }
        }

        function saveScene() {
            const sceneElement = document.querySelector("a-scene");
            const sceneName = document.getElementById("sceneName").value.trim();
            if (!sceneElement || !sceneName) return alert("No scene or name provided!");

            let scenes = JSON.parse(localStorage.getItem("scenes") || "{}");
            scenes[sceneName] = sceneElement.innerHTML;
            localStorage.setItem("scenes", JSON.stringify(scenes));

            populateScenes();
            alert("Scene saved!");
        }

        function loadScene() {
            const sceneName = document.getElementById("sceneSelect").value;
            if (!sceneName) return alert("Select a scene!");

            const scenes = JSON.parse(localStorage.getItem("scenes") || "{}");
            const sceneContent = scenes[sceneName];

            if (sceneContent) {
                const sceneContainer = document.getElementById("sceneContainer");
                sceneContainer.innerHTML = `<a-scene>${sceneContent}</a-scene>`;
            }
        }
    </script>
    <style>.html{z-index:999999999999;position:fixed;left:40%}</style>
</body>
</html>

and

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <title>A-Frame Scene Saver</title>
  <script src="https://aframe.io/releases/1.7.1/aframe.min.js"></script>
  <style>
    #controls {
      position: fixed;
      top: 10px;
      left: 10px;
      z-index: 1000;
      background: white;
      padding: 10px;
      border-radius: 5px;
    }
  </style>
</head>
<body>

<div id="controls">
  <label for="sceneSelector">Choose Scene:</label>
  <select id="sceneSelector"></select>
  <button onclick="saveCurrentScene()">Save Current Scene</button>
</div>

<a-scene id="mainScene">
  <!-- Default scene -->
  <a-box position="-1 0.5 -3" rotation="0 45 0" color="#4CC3D9"></a-box>
  <a-sphere position="0 1.25 -5" radius="1.25" color="#EF2D5E"></a-sphere>
  <a-cylinder position="1 0.75 -3" radius="0.5" height="1.5" color="#FFC65D"></a-cylinder>
  <a-plane position="0 0 -4" rotation="-90 0 0" width="4" height="4" color="#7BC8A4"></a-plane>
  <a-sky color="#ECECEC"></a-sky>
</a-scene>

<script>
  const DB_NAME = "AFRAME_SCENE_DB";
  const STORE_NAME = "scenes";

  let db;

  // Open IndexedDB
  function openDB() {
    return new Promise((resolve, reject) => {
      const request = indexedDB.open(DB_NAME, 1);
      request.onupgradeneeded = function (event) {
        db = event.target.result;
        if (!db.objectStoreNames.contains(STORE_NAME)) {
          db.createObjectStore(STORE_NAME, { keyPath: "name" });
        }
      };
      request.onsuccess = function (event) {
        db = event.target.result;
        resolve(db);
      };
      request.onerror = function (event) {
        reject("Error opening IndexedDB");
      };
    });
  }

  // Save scene to IndexedDB
  function saveScene(name, sceneHTML) {
    const transaction = db.transaction([STORE_NAME], "readwrite");
    const store = transaction.objectStore(STORE_NAME);
    store.put({ name, sceneHTML });
  }

  // Load all scenes from IndexedDB
  function loadScenes() {
    const selector = document.getElementById("sceneSelector");
    const transaction = db.transaction([STORE_NAME], "readonly");
    const store = transaction.objectStore(STORE_NAME);
    const getAllRequest = store.getAll();

    getAllRequest.onsuccess = function () {
      const scenes = getAllRequest.result;
      selector.innerHTML = ""; // Clear existing options

      // Add default option
      let defaultOption = document.createElement("option");
      defaultOption.value = "default";
      defaultOption.text = "Default Scene";
      selector.appendChild(defaultOption);

      scenes.forEach((scene) => {
        let option = document.createElement("option");
        option.value = scene.name;
        option.text = scene.name;
        selector.appendChild(option);
      });
    };
  }

  // Load a specific scene by name
  function loadSceneByName(name) {
		const mainScene = document.getElementById("mainScene");

		if (name === "default") {
			const defaultHTML = getDefaultSceneHTML();
			replaceSceneHTML(mainScene, defaultHTML);
			return;
		}

		const transaction = db.transaction([STORE_NAME], "readonly");
		const store = transaction.objectStore(STORE_NAME);
		const getRequest = store.get(name);

		getRequest.onsuccess = function () {
			const result = getRequest.result;
			if (result) {
				replaceSceneHTML(mainScene, result.sceneHTML);
			}
		};
	}
  // Get default scene HTML as string (only children)
  function getDefaultSceneHTML() {
    return `
      <a-box position="-1 0.5 -3" rotation="0 45 0" color="#4CC3D9"></a-box>
      <a-sphere position="0 1.25 -5" radius="1.25" color="#EF2D5E"></a-sphere>
      <a-cylinder position="1 0.75 -3" radius="0.5" height="1.5" color="#FFC65D"></a-cylinder>
      <a-plane position="0 0 -4" rotation="-90 0 0" width="4" height="4" color="#7BC8A4"></a-plane>
      <a-sky color="#ECECEC"></a-sky>
    `;
  }

  // Event listener for select
  document.getElementById("sceneSelector").addEventListener("change", function (e) {
    loadSceneByName(e.target.value);
  });

  // Save current scene
  window.saveCurrentScene = function () {
    const sceneName = prompt("Enter scene name:");
    if (!sceneName) return;

    const mainScene = document.getElementById("mainScene");

    // Only save child elements, not the <a-scene> itself
    const sceneHTML = Array.from(mainScene.children)
      .map(el => el.outerHTML)
      .join("");

    saveScene(sceneName, sceneHTML);
    loadScenes(); // Refresh selector
  };

  // Initialize
  openDB().then(() => {
    loadScenes();
  });
	function replaceSceneHTML(sceneEl, htmlString) {
		// Clear existing scene content
		while (sceneEl.firstChild) {
			sceneEl.removeChild(sceneEl.firstChild);
		}

		// Convert HTML string to DOM elements
		const div = document.createElement('div');
		div.innerHTML = htmlString.trim();

		// Append each child to <a-scene>
		Array.from(div.children).forEach(child => {
			sceneEl.appendChild(child);
		});
	}
</script>

</body>
</html>

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions