Thank you for your interest in improving the Hypnosis Script Generator. This document covers how to contribute content, code, and bug reports.
The entire project is a single HTML file designed to run in Perchance.org's HTML panel. There is no build step, no bundler, no framework. The file contains:
<style>— all CSS (~700 lines)- HTML body — wizard UI, session screen, post-session screen, modals, live panel (~400 lines)
<script>— all JavaScript (~2800 lines)
| Section | Purpose |
|---|---|
| IDB Storage | IndexedDB key-value wrapper (idbGet, idbSet) |
| Toast System | Non-blocking notification toasts |
| iOS Safari Fix | Viewport meta for touch devices |
| Data: Methods | 37 induction method definitions |
| Data: Pacing Rules | Per-method pacing instructions for the AI |
| Data: Intention Types | 20 intention type definitions with AI guidance |
| Data: Personas | 25 guide persona definitions |
| State | Global state object and phase durations |
| Step Management | Wizard navigation, validation, UI updates |
| Rendering | Grid renderers, detail panels, config summary |
| Configure Panel | Dynamic Step 4 context rendering |
| Voices | TTS voice loading and quality scoring |
| Soundscape Engine | SoundscapeEngine class (10 ambient types) |
| Visual Renderers | 16 Canvas 2D render functions + VIZ_RENDERERS map |
| Visual Setup | setupVisual() — mounts renderers with ResizeObserver |
| Persona Editor | Modal CRUD for custom personas |
| Crafter | craftSuggestion() — AI suggestion generation |
| Script Generator | generateFullScript() — AI full script generation |
| Library | Save/load/delete scripts, session history, backup/restore |
| Export & Share | Script download, URL sharing, backup export/import |
| Transcript | Populate and toggle script transcript |
| Session Player | startSession(), speakScript(), speak(), _doSpeak() |
| Post-Session | finishSession() — summary, mood, grounding, cooldown |
| Emergency | Persona re-alert scripts, emergencyEnd() |
| Handlers | All event listeners (~65) |
| Init | Async startup: IDB migration, rendering, AI preload |
Add an entry to BUILTIN_PERSONAS:
{ id: 'unique-id', name: 'The Name', tagline: 'Short description.',
category: 'contemplative|showman|somatic|authoritarian',
systemPrompt: `Voice instructions for the AI. Describe tone, vocabulary, pacing, signature phrases.` },Then add a matching re-alert script to PERSONA_REALERTS:
'unique-id': "Re-alert script with [PAUSE N] markers. Match the persona's voice.",Add an entry to METHODS:
{ id: 'unique-id', name: 'Method Name', tagline: 'One-line description.',
tags: ['tag1', 'tag2'], category: 'therapeutic|somatic|demonstration|rapid|advanced',
detail: 'Full description of the method and how it works.',
visual: 'breathing|tunnel|vortex|candle|moire|starfield|...', // any VIZ_RENDERERS key
drone: true|false, // whether soundscape is recommended
pacing: 'monotony|structured|narrative|focused|...', // PACING_RULES key
recommended: true // optional — shows a badge
},If the method needs a new pacing style, add it to PACING_RULES:
'pacing-key': `Pacing instructions for the AI. Describe phrase length, pause timing, tone.`,Write a function with the signature (ctx, W, H, t, color):
function renderMyEffect(ctx, W, H, t, color) {
const rgb = hexToRgb(color);
// ... Canvas 2D drawing using t (time in seconds) as the animation driver
}Register it in VIZ_RENDERERS:
const VIZ_RENDERERS = {
// ...existing...
'my-effect': renderMyEffect,
};Add an <option> for it in both the Configure tab vizSelect and the live panel liveVizSelect.
Add a new else if branch in SoundscapeEngine.start():
} else if (type === 'my-sound') {
// Create oscillators, noise buffers, filters, etc.
// Connect everything to `m` (master gain)
// For periodic events, use setTimeout with `if (!this.running) return;` guard
}Add <option> entries in both soundscapeSelect (Configure tab) and liveSoundscapeSelect (live panel).
- No build tools, no transpilation, no modules — everything is vanilla ES2020+ in a single
<script>. - Use
constandlet, nevervar. - Use
?.optional chaining and??nullish coalescing freely. - Template literals for HTML generation — always escape user content via
escapeHtml(). - Prefer
notify.warn/error/success/infooveralert(). - Always
linearRampToValueAtTimefor Web Audio gain changes — never set.valuedirectly. - Guard all
Numberinputs withNumber.isFinite()before passing to Web Audio. - All canvas renderers must respect
state.abortFlag(checkif (state.abortFlag) return;in rAF loops). - Cap visual flicker at ≤2 Hz per WCAG 2.3.1.
Include:
- Browser and version
- Console errors (right-click → Inspect → Console)
- Steps to reproduce
- Which step of the wizard you were on
Since this runs on Perchance, testing is manual:
- Extract the
<script>content and runnode --checkto verify syntax. - Open the generator on Perchance and verify no console errors.
- Walk through all 5 wizard steps.
- Start a session and verify TTS, visuals, and soundscape all work.
- Test emergency end → grounding exercise.
- Test backup export → import on a fresh browser profile.