If most LLM apps are search engines, DriftScript is improv theater.
It takes one prompt, routes it through multiple AI personalities, and surfaces how language drifts over time. The objective is not perfect fidelity. The objective is controlled chaos that people want to share.
This post breaks down how the app was built, the architecture decisions behind it, and where to take it next.
Product Idea in One Line
Input prompt -> 5 to 10 personality rewrites -> compare start and end -> score the drift.
Why This Pattern Works for Viral UX
- It is instantly understandable
- It is inherently replayable
- It produces surprising outputs quickly
- It generates shareable artifacts without extra user work
Stack
- Streamlit for fast product iteration and deployment simplicity
- Python for orchestration and deterministic chain logic
- OpenAI SDK as the single API layer
- OpenAI, Featherless, and Ollama via provider abstraction
- Pillow for PNG share card export
- python-dotenv for environment configuration
High-Level System Design
Provider Abstraction: One Interface, Many Backends
Using the OpenAI-compatible SDK surface lets us swap endpoints with minimal changes.
def build_client(provider: str) -> OpenAI:
if provider == "openai":
return OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
if provider == "featherless":
return OpenAI(
api_key=os.getenv("FEATHERLESS_API_KEY"),
base_url=os.getenv("FEATHERLESS_BASE_URL", "https://api.featherless.ai/v1"),
)
if provider == "ollama":
return OpenAI(
api_key=os.getenv("OLLAMA_API_KEY", "ollama"),
base_url=os.getenv("OLLAMA_BASE_URL", "http://localhost:11434/v1"),
)
This pattern gives three immediate benefits:
- Lower vendor lock-in
- Cheap experimentation with model mixes
- Same core app logic for cloud and local modes
Prompt Contract Per Step
Every agent step uses the same contract, with personality injected into the system prompt.
SYSTEM:
You are a rewriting agent with the following personality:
[PERSONALITY DESCRIPTION]
...
Rules:
- Do NOT explain
- Do NOT mention you are an AI
- Keep it concise (max 3–5 sentences)
- Amplify tone and style significantly
USER:
Rewrite this text:
[INPUT TEXT]
Keeping the contract fixed is important. It makes output format stable while still allowing major stylistic divergence.
Chain Orchestration
The orchestrator carries forward output from one step to the next.
def run_chain(input_text, steps, provider, default_model, model_mode, random_model_pool, chaos_mode, seed):
results = []
current_text = input_text
rng = random.Random(seed)
for i in range(steps):
personality_name, personality_desc = choose_personality(i, rng)
model = resolve_step_model(provider, default_model, model_mode, random_model_pool, i, rng)
step = rewrite_step(current_text, personality_name, personality_desc, provider, model, chaos_mode, seed, i)
results.append(step)
current_text = step.output_text
return results, current_text
Chaos Mode Design
Chaos mode is not random noise. It is bounded unpredictability.
What changes when enabled:
- Base temperature increases
- Temperature gets jitter per step
- Extra prompt directives are sampled from a chaos instruction pool
- Remix uses a fresh seed while preserving baseline config
This keeps outputs unstable enough to be fun, but still coherent enough to read/share.
Reliability: Retry Once
Each step retries one time on transient errors.
for attempt in range(2):
try:
return llm_call(...)
except Exception:
if attempt == 0:
time.sleep(0.35)
else:
raise
This reduces failure rate without introducing heavy queueing or backoff complexity in MVP stage.
Drift Metric
A semantic score would require embedding calls and extra cost. For MVP speed, DriftScript uses:
- token cosine similarity
- length ratio
- weighted blend
preservation = (0.7 * cosine) + (0.3 * len_ratio)
drift = (1.0 - preservation) * 100
Why this is good enough:
- cheap and fast
- interpretable
- responsive in the UI
UI Decisions
The interface is structured around three moments:
- Run
- Compare
- Share
Key sections:
- Sidebar configuration (provider, model routing, chaos, steps, seed)
- Before vs After visual with lightweight word diff
- Chain timeline in expanders for readability
- Share card (text and PNG export)
- Remix for quick iteration loops
Local State Features
MVP includes session-only state for:
- run history
- leaderboard
This is deliberate. It validates engagement loops before adding database/auth complexity.
Security and Config
Environment config is loaded from .env via python-dotenv.
Example:
OPENAI_API_KEY=
FEATHERLESS_API_KEY=
FEATHERLESS_BASE_URL=https://api.featherless.ai/v1
OLLAMA_BASE_URL=http://localhost:11434/v1
OLLAMA_API_KEY=ollama
.env is ignored in Git.
Performance Notes
Targeting <5s for full chain depends on provider latency and model size.
To improve perceived speed next:
- stream tokens for each step
- parallel speculative branches then choose best output
- cache repeated runs by seed+prompt+config
Tradeoffs Taken
- No persistent DB yet
- No authentication yet
- No model-level cost analytics yet
- No distributed queueing yet
These are intentional omissions to optimize for fast iteration and product learning.
Extension Roadmap
- Multiplayer lobbies and real-time chain playback
- Global public gallery and ranking signals
- Embedding-based drift analytics
- Team mode: alternate human + AI turns
- Scheduled challenges and daily prompt themes
- Fine-grained moderation layers per provider
Developer Takeaway
DriftScript demonstrates a useful architecture pattern:
- strict prompt contract
- pluggable model providers
- deterministic orchestration with optional chaos
- thin yet meaningful scoring layer
- sharing-first UX
For small AI product teams, this is a practical blueprint for shipping social AI apps quickly without over-engineering.
Github Repo: https://github.com/harishkotra/DriftScript


Top comments (0)