Compare commits

..

2 Commits

Author SHA1 Message Date
Alexander Whitestone
f3fddc0585 feat: implement Project Mnemosyne spatial memory visuals 2026-04-08 23:58:35 -04:00
Alexander Whitestone
84f75e1e51 feat: add formal spatial memory schema JSON for Project Mnemosyne 2026-04-08 23:56:42 -04:00
2 changed files with 217 additions and 0 deletions

183
app.js
View File

@@ -22,6 +22,146 @@ const NEXUS = {
}
};
class MnemosyneManager {
constructor(scene) {
this.scene = scene;
this.schema = null;
this.memoryObjects = new Map(); // factId -> Mesh
this.rooms = {}; // category -> { center, color }
}
async init() {
try {
const response = await fetch('./mnemosyne_schema.json');
this.schema = await response.json();
this._setupRooms();
console.log('Mnemosyne initialized');
} catch (e) {
console.error('Mnemosyne init failed:', e);
}
}
_setupRooms() {
const roomConfigs = this.schema.rooms;
const roomNames = Object.keys(roomConfigs);
// Arrange rooms in a circle around the center
const radius = 15;
roomNames.forEach((cat, i) => {
const angle = (i / roomNames.length) * Math.PI * 2;
this.rooms[cat] = {
name: roomConfigs[cat].name,
center: new THREE.Vector3(Math.cos(angle) * radius, 0, Math.sin(angle) * radius),
color: new THREE.Color(roomConfigs[cat].visual_accent)
};
// Add a visual marker for the room (a floating holographic sign)
this._createRoomMarker(cat);
});
}
_createRoomMarker(cat) {
const room = this.rooms[cat];
const group = new THREE.Group();
const geo = new THREE.TorusGeometry(2, 0.05, 16, 100);
const mat = new THREE.MeshBasicMaterial({ color: room.color, transparent: true, opacity: 0.4 });
const ring = new THREE.Mesh(geo, mat);
ring.rotation.x = Math.PI / 2;
group.add(ring);
group.position.copy(room.center);
group.position.y = 0.1;
this.scene.add(group);
}
spawnFact(fact) {
const { id, category, trust, importance, content } = fact;
const room = this.rooms[category];
if (!room) return;
// Visuals based on schema
const geo = this._getGeometryForFact(content);
const mat = new THREE.MeshPhysicalMaterial({
color: room.color,
emissive: room.color,
emissiveIntensity: trust * 2,
transparent: true,
opacity: 0.8,
transmission: 0.5,
thickness: 1,
});
const mesh = new THREE.Mesh(geo, mat);
// Position: random jitter around room center
mesh.position.copy(room.center);
mesh.position.x += (Math.random() - 0.5) * 4;
mesh.position.z += (Math.random() - 0.5) * 4;
mesh.position.y = 1 + Math.random() * 2;
// Scale based on importance
const s = 0.1 + importance * 0.4;
mesh.scale.setScalar(s);
this.memoryObjects.set(id, mesh);
this.scene.add(mesh);
// Animation: Fade in
mesh.scale.setScalar(0);
new Promise(resolve => {
const start = performance.now();
const duration = 1000;
const animate = (now) => {
const progress = Math.min((now - start) / duration, 1);
mesh.scale.setScalar(s * progress);
if (progress < 1) requestAnimationFrame(animate);
};
requestAnimationFrame(animate);
});
}
_getGeometryForFact(content) {
const rand = Math.random();
if (rand < 0.33) return new THREE.SphereGeometry(0.5, 16, 16);
if (rand < 0.66) return new THREE.BoxGeometry(0.5, 0.5, 0.5);
return new THREE.ConeGeometry(0.4, 0.7, 16);
}
updateFact(fact) {
const mesh = this.memoryObjects.get(fact.id);
if (!mesh) return;
// Update luminosity based on trust
mesh.material.emissiveIntensity = fact.trust * 2;
// Update scale based on importance
const s = 0.1 + fact.importance * 0.4;
mesh.scale.setScalar(s);
}
removeFact(id) {
const mesh = this.memoryObjects.get(id);
if (!mesh) return;
// Animation: Fade out/shrink
const start = performance.now();
const duration = 500;
const originalScale = mesh.scale.x;
const animate = (now) => {
const progress = Math.min((now - start) / duration, 1);
mesh.scale.setScalar(originalScale * (1 - progress));
if (progress < 1) {
requestAnimationFrame(animate);
} else {
this.scene.remove(mesh);
this.memoryObjects.delete(id);
}
};
requestAnimationFrame(animate);
}
}
// ═══ STATE ═══
let camera, scene, renderer, composer;
let clock, playerPos, playerRot;
@@ -99,6 +239,9 @@ async function init() {
updateLoad(10);
scene = new THREE.Scene();
window.mnemosyne = new MnemosyneManager(scene);
await mnemosyne.init();
simulateMemoryStream();
scene.fog = new THREE.FogExp2(0x050510, 0.012);
camera = new THREE.PerspectiveCamera(65, window.innerWidth / window.innerHeight, 0.1, 1000);
@@ -1760,6 +1903,46 @@ function closeVisionOverlay() {
let lastThoughtTime = 0;
let pulseTimer = 0;
// ═══ MNEMOSYNE SIMULATION ═══
function simulateMemoryStream() {
const categories = ['user_pref', 'project', 'tool', 'general'];
const facts = [];
setInterval(() => {
const id = 'fact_' + Math.random().toString(36).substr(2, 9);
const category = categories[Math.floor(Math.random() * categories.length)];
const fact = {
id,
category,
trust: Math.random(),
importance: Math.random(),
content: 'Simulated holographic memory fragment ' + id
};
// We need access to the mnemosyne instance.
// I'll make it a global in app.js for simplicity in this prototype.
if (window.mnemosyne) {
window.mnemosyne.spawnFact(fact);
facts.push(fact);
}
// Cleanup old facts
if (facts.length > 30) {
const oldFact = facts.shift();
if (window.mnemosyne) window.mnemosyne.removeFact(oldFact.id);
}
// Occasionally update a random fact
if (Math.random() > 0.7 && facts.length > 0) {
const target = facts[Math.floor(Math.random() * facts.length)];
target.trust = Math.random();
target.importance = Math.random();
if (window.mnemosyne) window.mnemosyne.updateFact(target);
}
}, 7000);
}
function gameLoop() {
requestAnimationFrame(gameLoop);
const delta = Math.min(clock.getDelta(), 0.1);

34
mnemosyne_schema.json Normal file
View File

@@ -0,0 +1,34 @@
{
"rooms": {
"user_pref": {
"name": "The Library",
"theme": "Archive shelves, soft lighting, velvet",
"visual_accent": "#C2B280"
},
"project": {
"name": "The Workshop",
"theme": "Drafting tables, holographic blueprints, metal",
"visual_accent": "#4682B4"
},
"tool": {
"name": "The Armory",
"theme": "Server racks, neon circuitry, chrome",
"visual_accent": "#00FF00"
},
"general": {
"name": "The Commons",
"theme": "Open garden, floating islands, eclectic",
"visual_accent": "#FFD700"
}
},
"object_mapping": {
"trust_score": {
"property": "luminosity",
"range": [0.0, 1.0]
},
"connectivity": {
"property": "scale",
"range": [0.5, 2.0]
}
}
}