Skip to content

Instantly share code, notes, and snippets.

@bfollington
Created March 24, 2025 18:08
Show Gist options
  • Save bfollington/93d3d4b312a959a5fe16b8ba45c50665 to your computer and use it in GitHub Desktop.
Save bfollington/93d3d4b312a959a5fe16b8ba45c50665 to your computer and use it in GitHub Desktop.
/**
* Multi-Timescale Explorer
*
* A framework for creative technological exploration that balances
* present engagement with future direction.
*/
namespace MultiTimescaleExplorer {
// ======== Core Types ========
/**
* Represents an open-ended inquiry or area of exploration
*/
interface Inquiry {
id: string;
question: string;
connections: string[]; // IDs of related inquiries
notes: string;
dateCreated: Date;
lastEngaged: Date;
}
/**
* A concrete experiment to explore an Inquiry
*/
interface Experiment {
id: string;
title: string;
inquiryIds: string[]; // Which inquiries does this experiment address?
hypothesis: string;
approach: string;
status: "planned" | "in-progress" | "completed" | "abandoned";
results?: string;
reflections?: string;
dateCreated: Date;
lastUpdated: Date;
timeInvested: number; // In minutes
}
/**
* A tangible outcome that can be shared with others
*/
interface Artifact {
id: string;
title: string;
description: string;
experimentIds: string[]; // Which experiments led to this?
type: "tool" | "prototype" | "writing" | "visualization" | "other";
feedback: Interaction[];
dateCreated: Date;
lastUpdated: Date;
narrativeFraming?: string; // How this fits into your broader story
audienceContext?: string; // How to present this to specific audiences
}
/**
* Record of someone interacting with your work
*/
interface Interaction {
id: string;
artifactId: string;
person: string;
notes: string;
insights: string;
followupInquiryIds?: string[]; // New inquiries sparked
date: Date;
}
// Additional types for external-facing elements
/**
* A cohesive theme that ties together multiple artifacts
*/
interface Narrative {
id: string;
name: string;
description: string;
keyFraming: string; // The core story you're telling
associatedArtifactIds: string[];
targetAudiences: Audience[];
dateCreated: Date;
lastUpdated: Date;
}
/**
* Represents a specific audience you might present your work to
*/
interface Audience {
id: string;
name: string; // e.g., "Tech startups", "Game developers", "Academic researchers"
interests: string[];
painPoints: string[];
keyValues: string[];
communicationChannels: string[];
relevantExperiences: string[];
}
// ======== Core System ========
class Explorer {
private inquiries: Map<string, Inquiry> = new Map();
private experiments: Map<string, Experiment> = new Map();
private artifacts: Map<string, Artifact> = new Map();
private interactions: Map<string, Interaction> = new Map();
private narratives: Map<string, Narrative> = new Map();
private audiences: Map<string, Audience> = new Map();
// Timescale management
private currentFocus?: string; // ID of current inquiry/experiment
private todaysMicroGoal?: string;
private weeklyThemes: string[] = [];
private quarterlyDirections: string[] = [];
/**
* Records time spent in active exploration mode
*/
trackExplorationSession(experimentId: string, minutes: number, notes: string): void {
const experiment = this.experiments.get(experimentId);
if (!experiment) throw new Error("Experiment not found");
experiment.timeInvested += minutes;
experiment.lastUpdated = new Date();
if (notes) {
experiment.results = experiment.results
? `${experiment.results}\n\n${new Date().toISOString()}: ${notes}`
: `${new Date().toISOString()}: ${notes}`;
}
// Update related inquiries' lastEngaged date
experiment.inquiryIds.forEach(id => {
const inquiry = this.inquiries.get(id);
if (inquiry) inquiry.lastEngaged = new Date();
});
}
/**
* Core method for embracing serendipity - creates a new inquiry
* from an unexpected insight
*/
captureNewInquiry(question: string, notes: string = "", connections: string[] = []): string {
const id = generateId();
const now = new Date();
this.inquiries.set(id, {
id,
question,
connections,
notes,
dateCreated: now,
lastEngaged: now
});
return id;
}
/**
* Creates an experiment to explore one or more inquiries
*/
designExperiment(title: string, inquiryIds: string[], hypothesis: string, approach: string): string {
const id = generateId();
const now = new Date();
// Validate that inquiries exist
inquiryIds.forEach(id => {
if (!this.inquiries.has(id)) throw new Error(`Inquiry ${id} not found`);
});
this.experiments.set(id, {
id,
title,
inquiryIds,
hypothesis,
approach,
status: "planned",
dateCreated: now,
lastUpdated: now,
timeInvested: 0
});
return id;
}
/**
* Creates a shareable artifact from experiment results
*/
createArtifact(title: string, description: string, experimentIds: string[], type: Artifact["type"]): string {
const id = generateId();
const now = new Date();
// Validate that experiments exist
experimentIds.forEach(id => {
if (!this.experiments.has(id)) throw new Error(`Experiment ${id} not found`);
});
this.artifacts.set(id, {
id,
title,
description,
experimentIds,
type,
feedback: [],
dateCreated: now,
lastUpdated: now
});
return id;
}
/**
* Records interaction and feedback from someone engaging with your work
*/
recordInteraction(artifactId: string, person: string, notes: string, insights: string): string {
const id = generateId();
if (!this.artifacts.has(artifactId)) throw new Error(`Artifact ${artifactId} not found`);
const interaction: Interaction = {
id,
artifactId,
person,
notes,
insights,
date: new Date()
};
this.interactions.set(id, interaction);
// Add to artifact's feedback
const artifact = this.artifacts.get(artifactId)!;
artifact.feedback.push(interaction);
return id;
}
// ======== Narrative Management Methods ========
/**
* Creates a narrative that ties together multiple artifacts
* into a cohesive story for external audiences
*/
createNarrative(name: string, description: string, keyFraming: string): string {
const id = generateId();
const now = new Date();
this.narratives.set(id, {
id,
name,
description,
keyFraming,
associatedArtifactIds: [],
targetAudiences: [],
dateCreated: now,
lastUpdated: now
});
return id;
}
/**
* Adds an artifact to a narrative
*/
addArtifactToNarrative(narrativeId: string, artifactId: string, framingContext?: string): void {
const narrative = this.narratives.get(narrativeId);
const artifact = this.artifacts.get(artifactId);
if (!narrative) throw new Error(`Narrative ${narrativeId} not found`);
if (!artifact) throw new Error(`Artifact ${artifactId} not found`);
narrative.associatedArtifactIds.push(artifactId);
narrative.lastUpdated = new Date();
// Add narrative framing to the artifact if provided
if (framingContext) {
artifact.narrativeFraming = framingContext;
}
}
/**
* Defines a specific audience you might present work to
*/
defineAudience(name: string, interests: string[], painPoints: string[],
keyValues: string[], communicationChannels: string[]): string {
const id = generateId();
this.audiences.set(id, {
id,
name,
interests,
painPoints,
keyValues,
communicationChannels,
relevantExperiences: []
});
return id;
}
/**
* Associates an audience with a narrative
*/
addAudienceToNarrative(narrativeId: string, audienceId: string): void {
const narrative = this.narratives.get(narrativeId);
const audience = this.audiences.get(audienceId);
if (!narrative) throw new Error(`Narrative ${narrativeId} not found`);
if (!audience) throw new Error(`Audience ${audienceId} not found`);
narrative.targetAudiences.push(audience);
narrative.lastUpdated = new Date();
}
/**
* Customizes how an artifact should be presented to a specific audience
*/
customizeArtifactForAudience(artifactId: string, audienceId: string, presentationContext: string): void {
const artifact = this.artifacts.get(artifactId);
const audience = this.audiences.get(audienceId);
if (!artifact) throw new Error(`Artifact ${artifactId} not found`);
if (!audience) throw new Error(`Audience ${audienceId} not found`);
artifact.audienceContext = presentationContext;
artifact.lastUpdated = new Date();
}
/**
* Generates presentation materials for a narrative targeted at a specific audience
*/
createPresentationForAudience(narrativeId: string, audienceId: string): string {
const narrative = this.narratives.get(narrativeId);
const audience = this.audiences.get(audienceId);
if (!narrative) throw new Error(`Narrative ${narrativeId} not found`);
if (!audience) throw new Error(`Audience ${audienceId} not found`);
// This would be a more complex method in practice
// For now, a simple placeholder that could generate slide decks, pitches, etc.
return `Presentation for ${audience.name} about "${narrative.name}"`;
}
/**
* Analyzes themes across artifacts to suggest potential narrative framings
*/
suggestNarratives(): string[] {
// This would use more sophisticated analysis in practice
// For now, a placeholder
return [
"Tools for sovereign knowledge management",
"Rethinking human-computer interfaces for creativity",
"Decentralized systems for personal agency"
];
}
/**
* Creates a new inquiry based on feedback, connecting the cycle
*/
createInquiryFromInteraction(interactionId: string, question: string, notes: string = ""): string {
const interaction = this.interactions.get(interactionId);
if (!interaction) throw new Error(`Interaction ${interactionId} not found`);
const inquiryId = this.captureNewInquiry(question, notes, []);
// Connect this new inquiry to the interaction
interaction.followupInquiryIds = interaction.followupInquiryIds || [];
interaction.followupInquiryIds.push(inquiryId);
return inquiryId;
}
// ======== Reflection Methods ========
/**
* Identify inquiries that haven't been engaged with recently
*/
findDormantInquiries(daysSinceLastEngagement: number = 30): Inquiry[] {
const cutoffDate = new Date();
cutoffDate.setDate(cutoffDate.getDate() - daysSinceLastEngagement);
return Array.from(this.inquiries.values())
.filter(inquiry => inquiry.lastEngaged < cutoffDate);
}
/**
* Find inquiries that connect to many others - potential rich areas
*/
findHighlyConnectedInquiries(minConnections: number = 3): Inquiry[] {
return Array.from(this.inquiries.values())
.filter(inquiry => inquiry.connections.length >= minConnections);
}
/**
* Calculate time investment across different areas
*/
analyzeTimeAllocation(): Record<string, number> {
const inquiryTimeMap: Record<string, number> = {};
// Initialize with 0 minutes for all inquiries
for (const inquiry of this.inquiries.values()) {
inquiryTimeMap[inquiry.id] = 0;
}
// Add up time from experiments
for (const experiment of this.experiments.values()) {
experiment.inquiryIds.forEach(id => {
// Distribute time equally among all inquiries of this experiment
const timePerInquiry = experiment.timeInvested / experiment.inquiryIds.length;
inquiryTimeMap[id] = (inquiryTimeMap[id] || 0) + timePerInquiry;
});
}
return inquiryTimeMap;
}
/**
* Suggest new connections between inquiries based on common themes
*/
suggestNewConnections(): {inquiry1: Inquiry, inquiry2: Inquiry, reason: string}[] {
const suggestions: {inquiry1: Inquiry, inquiry2: Inquiry, reason: string}[] = [];
const inquiries = Array.from(this.inquiries.values());
// This would use NLP in a real implementation
// For now, just a placeholder that could be built out
return suggestions;
}
// ======== Planning Methods ========
/**
* Set focus for current day based on energy and interest
*/
setDailyFocus(inquiryOrExperimentId: string, microGoal: string): void {
// Validate ID belongs to either an inquiry or experiment
if (!this.inquiries.has(inquiryOrExperimentId) && !this.experiments.has(inquiryOrExperimentId)) {
throw new Error("Invalid inquiry or experiment ID");
}
this.currentFocus = inquiryOrExperimentId;
this.todaysMicroGoal = microGoal;
}
/**
* Define themes for week - more directional than daily focus
*/
setWeeklyThemes(themes: string[]): void {
this.weeklyThemes = themes;
}
/**
* Set broader directional areas for the quarter
*/
setQuarterlyDirections(directions: string[]): void {
this.quarterlyDirections = directions;
}
/**
* Balances immediate focus with long-term direction
*/
suggestNextAction(): string {
// This would be a sophisticated algorithm in practice
// For now, a simple placeholder
const actions = [
"Spend 20 minutes exploring a dormant inquiry",
"Document insights from your most recent experiment",
"Share a recent artifact with someone new",
"Connect two previously unrelated inquiries",
"Design a micro-experiment you can complete today"
];
return actions[Math.floor(Math.random() * actions.length)];
}
}
// Helper function
function generateId(): string {
return Math.random().toString(36).substring(2, 15);
}
}
// ======== Example Usage ========
// Create a new explorer
const explorer = new MultiTimescaleExplorer.Explorer();
// Record some initial inquiries
const toolsForThoughtId = explorer.captureNewInquiry(
"How might we create tools that augment human cognition in non-prescriptive ways?",
"Inspired by Engelbart's work but moving beyond productivity toward creativity and agency"
);
const p2pKnowledgeId = explorer.captureNewInquiry(
"How can peer-to-peer systems enable more sovereign knowledge ecosystems?",
"Want to explore alternatives to centralized knowledge repositories"
);
const gameAsExplorationId = explorer.captureNewInquiry(
"Can games be designed as open-ended exploratory spaces rather than challenge-reward systems?",
"Looking at games as thinking tools rather than entertainment products"
);
// Connect related inquiries
// (In a real implementation, you'd have methods to manage these connections)
// Design an experiment
const experimentId = explorer.designExperiment(
"Prototype: Visual Programming Environment for Personal Knowledge",
[toolsForThoughtId, p2pKnowledgeId],
"A visual programming interface will make knowledge manipulation more intuitive for non-programmers",
"Build a simple prototype that allows spatial arrangement of knowledge fragments with visual rules for connection and transformation"
);
// Record time spent working on it
explorer.trackExplorationSession(
experimentId,
120,
"Initial prototype revealed that visual rule creation is harder than expected. Users gravitated toward spatial arrangement but struggled with formal rule definition."
);
// Create a shareable artifact
const artifactId = explorer.createArtifact(
"Spatial Knowledge Canvas - Alpha",
"A web-based tool for arranging and connecting ideas visually with simple automation rules",
[experimentId],
"tool"
);
// Record feedback from someone who tried it
const interactionId = explorer.recordInteraction(
artifactId,
"Alex",
"Loved the spatial arrangement, found the rule creation confusing but was excited about the possibilities",
"Suggested that instead of formal rules, the system could observe patterns in how they organize information and suggest automations"
);
// Define audiences
const developerAudienceId = explorer.defineAudience(
"Web Developers",
["Building better tools", "Reducing cognitive load", "Visual programming"],
["Complex codebases", "Documentation burden", "Keeping up with changing tech"],
["Elegance", "Efficiency", "Open source"],
["GitHub", "Twitter", "Dev conferences", "Tech blogs"]
);
const designerAudienceId = explorer.defineAudience(
"UX Designers",
["Information architecture", "Visual thinking", "Design systems"],
["Translating ideas to developers", "Complex requirements", "Rigid tools"],
["Creativity", "User empowerment", "Accessibility"],
["Dribbble", "Design conferences", "Medium", "Design podcasts"]
);
// Create a narrative
const knowledgeToolsNarrativeId = explorer.createNarrative(
"Visual Knowledge Systems",
"Spatial interfaces for personal knowledge management that adapt to user behavior",
"We're exploring how spatial interfaces combined with subtle machine learning can create knowledge tools that enhance human cognition without imposing rigid structures."
);
// Add artifacts to the narrative
explorer.addArtifactToNarrative(
knowledgeToolsNarrativeId,
artifactId,
"The Spatial Knowledge Canvas is our first experiment in creating tools that respect the natural ways humans organize information spatially while adding computational power."
);
// Associate audiences with narrative
explorer.addAudienceToNarrative(knowledgeToolsNarrativeId, developerAudienceId);
explorer.addAudienceToNarrative(knowledgeToolsNarrativeId, designerAudienceId);
// Customize presentation for different audiences
explorer.customizeArtifactForAudience(
artifactId,
developerAudienceId,
"For developers, we emphasize the reactive architecture and the pattern recognition system that suggests automations based on user behavior."
);
explorer.customizeArtifactForAudience(
artifactId,
designerAudienceId,
"For designers, we focus on how the spatial canvas enables more intuitive information architecture and creates a new design space for knowledge representation."
);
// This feedback sparks a new inquiry
const newInquiryId = explorer.createInquiryFromInteraction(
interactionId,
"How might we design systems that learn from user behavior without requiring explicit rule creation?",
"Pattern recognition vs. explicit programming - where is the right balance for non-technical users?"
);
// Set focuses at different timescales
explorer.setDailyFocus(newInquiryId, "Research pattern recognition approaches in no-code tools");
explorer.setWeeklyThemes(["Learning system behavior", "Implicit vs explicit knowledge"]);
explorer.setQuarterlyDirections(["Tools for thought", "Agency-enhancing interfaces"]);
// Get a suggestion for what to do next
const nextAction = explorer.suggestNextAction();
console.log(`Suggested next action: ${nextAction}`);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment