This is a planned feature for Phase 3 of the Flower Engine roadmap. Implementation details may change during development.
Overview
Sensory anchoring will ground narrative experiences in concrete physical reality through mandatory object descriptions, environmental clocks that advance world events, and dynamic atmospheric conditions. This system transforms abstract storytelling into vivid, tangible scenes that engage multiple senses.
Core Features
Object-Oriented Narration
Enforce physical grounding in every AI response:
- Mandatory Object Description: Every response must describe at least one physical object
- State Tracking: Objects have conditions that change over time (wet, broken, burning)
- Sensory Details: Touch, smell, sound, temperature beyond just visual description
- Spatial Relationships: Clear positioning of objects and characters in space
- Interactive Potential: Objects should feel manipulable and reactive to player actions
Environmental Clocks
Backend timers that advance world state independently of player action:
- Persistent Timers: Events progress even during player inactivity
- Scheduled Events: Predetermined story beats that trigger at specific times
- Conditional Triggers: Events that occur when certain conditions are met
- Cascading Consequences: Ignored problems escalate naturally
- Session Continuity: Clocks persist across disconnects and resumptions
Atmospheric Anchors
Dynamic ambient conditions that evolve with the narrative:
- Weather Systems: Rain, wind, fog, heat, cold affecting scenes
- Lighting Conditions: Time of day, shadows, light sources, darkness
- Mood Indicators: Emotional tone of environments (oppressive, welcoming, eerie)
- Soundscapes: Ambient noise descriptions (distant thunder, crowd murmur, silence)
- Scent Profiles: Smell descriptions that anchor scenes (sea salt, smoke, rot)
Implementation Details
System Rules Update
Add to AI’s SYSTEM RULES:
OBJECT-ORIENTED NARRATION REQUIREMENTS:
1. MANDATORY PHYSICAL GROUNDING:
- Every response must describe at least one physical object in detail
- Objects must have tangible properties: texture, weight, temperature, appearance
- Describe how the object relates to the immediate scene and action
- Include sensory details beyond sight: sound, smell, touch, taste when relevant
2. ENVIRONMENTAL AWARENESS:
- Integrate current weather, lighting, and atmospheric conditions naturally
- Objects should reflect environmental effects (wet from rain, warm from sun)
- Characters interact with environment (brushing away fog, shading eyes)
3. STATE PERSISTENCE:
- Objects maintain consistent state unless acted upon
- Describe changes to object states when they occur
- Broken things stay broken, moved things stay moved
EXAMPLE - Poor (Abstract):
"You enter the tavern. It's busy. The bartender looks at you."
EXAMPLE - Good (Anchored):
"You push through the tavern's heavy oak door, its iron handle
cold and slightly damp from the evening fog. The air inside
hits you—warm, thick with the smell of spilled ale and wood
smoke from the stone hearth crackling in the corner. The
bartender, polishing a cloudy glass with a stained rag, glances
up. Condensation beads on the glass's surface, catching the
flickering firelight."
Environmental Clock System
Backend timer architecture:
class EnvironmentalClock:
def __init__(self, event_id: str, trigger_time: int, description: str):
self.event_id = event_id
self.trigger_time = trigger_time # Unix timestamp
self.description = description
self.triggered = False
self.conditions = [] # Optional prerequisite conditions
def check_trigger(self, current_time: int, world_state: dict) -> bool:
"""Returns True if clock should fire"""
if self.triggered:
return False
if current_time < self.trigger_time:
return False
# Check if all conditions met
for condition in self.conditions:
if not condition.evaluate(world_state):
return False
return True
def execute(self, websocket) -> str:
"""Fires the event and returns narrative text"""
self.triggered = True
# Generate system message to inject into narrative
return f"[ENVIRONMENTAL EVENT] {self.description}"
Clock Types
Time-Based Clocks
Trigger after a specific duration:
{
"event_id": "tavern_closing_time",
"type": "time_based",
"trigger_after_seconds": 7200,
"description": "The tavern keeper begins ushering patrons toward the door, announcing last call.",
"repeating": false
}
Condition-Based Clocks
Trigger when world state matches criteria:
{
"event_id": "storm_arrives",
"type": "condition_based",
"conditions": [
{"weather": "cloudy"},
{"player_location": "outdoors"},
{"time_of_day": "afternoon"}
],
"description": "Dark clouds roll in from the west. The first fat raindrops begin to fall, quickly building to a steady downpour.",
"atmospheric_change": {"weather": "heavy_rain", "visibility": "reduced"}
}
Escalation Clocks
Intensify if player ignores a problem:
{
"event_id": "fire_spreads",
"type": "escalation",
"stages": [
{
"delay_seconds": 300,
"description": "Smoke begins seeping under doorways. You hear distant crackling.",
"severity": 1
},
{
"delay_seconds": 600,
"description": "Orange light flickers through windows. The heat is noticeable now, and smoke fills the hallway.",
"severity": 3
},
{
"delay_seconds": 900,
"description": "Flames burst through the western wall. The building groans ominously as structural beams begin to fail.",
"severity": 5
}
],
"interrupt_condition": {"player_action": "extinguish_fire"}
}
Atmospheric Anchor System
Dynamic ambient conditions tracked in world state:
{
"current_atmosphere": {
"weather": {
"condition": "light_rain",
"temperature": 52,
"wind": "gentle",
"visibility": "good"
},
"lighting": {
"time_of_day": "dusk",
"ambient_level": "dim",
"primary_source": "fading_sunlight",
"shadows": "long",
"color_cast": "orange_and_purple"
},
"mood": {
"tone": "melancholic",
"tension": "low",
"energy": "quiet"
},
"soundscape": [
"gentle rainfall on cobblestones",
"distant church bells",
"muffled conversations from nearby tavern"
],
"scents": [
"wet stone",
"woodsmoke from chimneys",
"fresh bread from the bakery"
]
}
}
Atmospheric Transitions
Gradual shifts that feel natural:
class AtmosphericTransition:
def __init__(self, from_state: dict, to_state: dict, duration_seconds: int):
self.from_state = from_state
self.to_state = to_state
self.duration = duration_seconds
self.start_time = None
def get_current_state(self, current_time: int) -> dict:
"""Interpolate between states based on elapsed time"""
if not self.start_time:
self.start_time = current_time
elapsed = current_time - self.start_time
progress = min(1.0, elapsed / self.duration)
# Interpolate atmospheric values
return self._interpolate(self.from_state, self.to_state, progress)
Example Transition:
Dusk → Night (30 minutes)
- Lighting: dim → dark
- Ambient sounds: birds → crickets
- Temperature: 68°F → 58°F
- Visibility: good → limited
- Mood: peaceful → mysterious
Database Schema
Environmental Clocks Table
CREATE TABLE environmental_clocks (
id TEXT PRIMARY KEY,
session_id TEXT NOT NULL,
event_type TEXT NOT NULL, -- 'time_based', 'condition_based', 'escalation'
trigger_time INTEGER,
conditions TEXT, -- JSON
description TEXT NOT NULL,
triggered BOOLEAN DEFAULT 0,
triggered_at INTEGER,
repeating BOOLEAN DEFAULT 0,
repeat_interval INTEGER,
active BOOLEAN DEFAULT 1
);
Atmospheric State Table
CREATE TABLE atmospheric_state (
session_id TEXT PRIMARY KEY,
weather_condition TEXT,
temperature INTEGER,
time_of_day TEXT,
lighting_level TEXT,
mood_tone TEXT,
soundscape TEXT, -- JSON array
scents TEXT, -- JSON array
last_updated INTEGER
);
Object State Table
CREATE TABLE scene_objects (
id TEXT PRIMARY KEY,
session_id TEXT NOT NULL,
name TEXT NOT NULL,
description TEXT,
location TEXT,
state TEXT, -- 'intact', 'damaged', 'broken', etc.
properties TEXT, -- JSON (texture, temperature, weight, etc.)
last_mentioned INTEGER,
persistent BOOLEAN DEFAULT 0
);
Use Cases
Mystery Investigation
Environmental details provide clues:
- Object State: “The candle has burned down halfway—it’s been lit for about three hours.”
- Weather Clue: “The muddy footprints lead inside, but the rain only started an hour ago.”
- Atmospheric Mood: The oppressive silence in the manor amplifies tension
- Sensory Detail: “You catch a faint whiff of almond—cyanide poisoning?”
Survival Scenario
Environment becomes an active challenge:
- Clock: Temperature drops 5°F every 30 minutes
- Consequence: Player must find shelter or face hypothermia
- Object Focus: “The frost forming on your water flask warns that night is coming.”
- Atmospheric Pressure: Wind howling, visibility near zero, disorientation
Combat Dynamics
Environment affects tactical options:
- Lighting: “The flickering torch creates dancing shadows—hard to aim.”
- Weather: “Rain makes the stone stairs treacherously slick.”
- Objects: “You grab the iron candlestick from the table—heavy, cold, improvised weapon.”
- Soundscape: “Thunder masks the sound of your footsteps on the wooden floor.”
Romantic Scene
Atmosphere sets emotional tone:
- Sensory Grounding: “Rose petals float in the marble fountain, releasing subtle fragrance.”
- Lighting: “Candlelight casts warm amber glow, softening sharp edges.”
- Soundscape: “Distant string quartet drifts through the garden, mixing with fountain’s gentle splash.”
- Weather: “Cool evening breeze carries jasmine scent from the hedge maze.”
Technical Architecture
Backend Components
New File: engine/environment.py
AtmosphericState: Class managing current conditions
EnvironmentalClock: Class for timed events
ObjectRegistry: Track scene objects and states
advance_clocks(): Check and trigger pending events
transition_atmosphere(): Smooth environmental changes
inject_atmospheric_context(): Add environment to AI prompt
Modified File: engine/llm.py
- Inject atmospheric context before each AI response
- Enforce object-description validation
- Parse object state changes from AI output
WebSocket Events
New events for environmental system:
{
"event": "clock_triggered",
"payload": {
"event_id": "storm_arrives",
"description": "Dark clouds roll in...",
"atmospheric_changes": {"weather": "rain"}
}
}
{
"event": "atmosphere_changed",
"payload": {
"previous": {"lighting": "dim"},
"current": {"lighting": "dark"},
"description": "The last light fades from the sky..."
}
}
Commands
Player Commands:
/time # Show current in-world time and conditions
/weather # Display current weather and forecast
/atmosphere # View full atmospheric state
/clocks # List active environmental clocks
Admin Commands:
/set_weather [condition]
/set_time [hour]
/add_clock [event] [duration]
/skip_time [minutes]
AI Integration
Context Injection
Before each AI response, inject:
[CURRENT ATMOSPHERE]
Time: Late evening (8:47 PM)
Weather: Light rain, 52°F, gentle wind from the west
Lighting: Dim twilight transitioning to darkness; streetlamps just lit
Ambient Sounds: Rainfall on cobblestones, distant tavern music, church bells
Scents: Wet stone, woodsmoke, fresh bread
Mood: Melancholic, peaceful, slightly mysterious
[ENVIRONMENTAL EVENTS]
- Tavern will close in approximately 2 hours
- Rain will intensify within 30 minutes
[SCENE OBJECTS]
- Cobblestone street: wet, reflecting lamplight, uneven surface
- Iron lamppost: cold to touch, flickering gas flame inside
- Wooden tavern door: heavy oak, brass handle, slightly ajar
Validation
Check AI responses for required elements:
def validate_object_description(response_text: str) -> bool:
"""Ensure response includes physical object description"""
# Check for concrete nouns
# Check for sensory adjectives
# Check for state descriptions
# Reject if too abstract
pass
If validation fails, prepend request:
[SYSTEM CORRECTION] Your previous response lacked physical grounding.
Please revise to include detailed description of at least one tangible
object in the scene, including its texture, temperature, or other
sensory properties.
Configuration
Settings in config.yaml:
sensory_anchoring:
enabled: true
enforce_object_descriptions: true
min_sensory_details: 2 # Minimum senses engaged per response
environmental_clocks: true
clock_check_interval: 60 # Seconds between clock evaluations
atmospheric_transitions: true
transition_duration: 1800 # Default transition time (seconds)
weather_enabled: true
time_progression: true
time_scale: 1.0 # Real seconds per in-game minute
Future Enhancements
Potential expansions beyond Phase 3:
- Seasonal Cycles: Long-term environmental changes
- Object Physics: More sophisticated state interactions
- Scent Memory: NPCs react to familiar smells
- Echo System: Spatial audio descriptions based on room acoustics
- Temperature Effects: Mechanical impact on character performance
- Object Durability: Wear and tear over time
- Weather Forecasting: NPCs predict upcoming conditions
- Ritual Timing: Magic or events tied to specific times/conditions
Design Philosophy
The sensory anchoring system is designed to:
- Ground Abstraction: Transform vague narration into concrete experiences
- Engage Senses: Move beyond visual-only descriptions
- Create Urgency: Clocks add time pressure and consequences
- Enhance Immersion: Rich sensory detail draws players deeper into scenes
- Support Continuity: Persistent objects and states maintain world consistency
By enforcing physical specificity and environmental dynamism, sensory anchoring transforms Flower from a dialogue engine into a fully-realized world that feels alive, reactive, and tangible.