Skip to content

Ymsniper/lain-layers

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

3 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Lain Layers

A local journal app themed after Serial Experiments Lain's recurring Layer:0X title cards.

Each entry is a Layer. Give it a title and body, click it, and the title-card animation plays: glowing liquid-glass letterforms scatter across the screen and strobe-flicker into place while a distorted voice builds up and resolves cleanly into "Layer zero one." The reverb and echo values were derived from autocorrelation analysis of the actual source audio.

screenshot placeholder


Requirements

  • Python 3 — stdlib only, no pip install needed
  • espeak-ng for speech synthesis

Install & Run

Clone the repo and install espeak-ng for your distro, then run the server:

git clone https://github.com/Ymsniper/lain-layers.git
cd lain-layers
python3 server.py

Then open http://localhost:8765 in your browser.

espeak-ng install by distro

Distro Command
Arch / CachyOS / Manjaro sudo pacman -S espeak-ng
Debian / Ubuntu / Mint / Pop!_OS sudo apt install espeak-ng
Fedora / RHEL 8+ / CentOS Stream sudo dnf install espeak-ng
openSUSE Tumbleweed / Leap sudo zypper install espeak-ng
Void Linux sudo xbps-install espeak-ng
Alpine Linux sudo apk add espeak-ng
Gentoo sudo emerge app-accessibility/espeak-ng
NixOS nix-env -iA nixpkgs.espeak-ng
Solus sudo eopkg install espeak-ng
Slackware slackpkg install espeak-ng

Entries are saved to entries.json next to server.py. Nothing leaves your machine.


Voice

The show uses Apple's proprietary PlainTalk "Whisper" voice — unavailable on Linux. This app uses espeak-ng's built-in whisper variant (espeak-ng -v en+whisper), the closest free offline equivalent.

The in-app ⚙ voice panel (bottom-right) lets you tune variant, rate, pitch, reverb, echo, and playback speed. Settings persist via localStorage.


Audio DSP

Speech is processed through a pure-Python DSP chain (wave + array, no dependencies):

Layer Description
Tight reverb 4 early reflections at 29 / 42 / 67 / 95 ms — from raw-waveform autocorrelation of the source audio
Diffuse wash 13 overlapping taps from 120 ms – 850 ms
Discrete echo 323 ms slap-back — located via envelope cross-correlation of the loudest voice burst

Each clip uses a per-sample wet/dry envelope so distortion evolves across the clip's own duration rather than sitting at a fixed level. The server returns the exact rendered clip length in X-Audio-Duration so the frontend animation stays in sync without guessing.


Animation

Each title-word letter is independently animated:

  • Position — scatter → flicker between waypoints via steps(1) → smooth bezier settle
  • Opacity — strobe blink (5× snap-on/off) while position is live, then hold at 1
  • Ghosts — 3 tight afterimage copies per letter strobe at offset positions, vanishing as the real letter arrives
  • Echo — large faint rotated duplicate of the settled word fades in with the caption
  • Layout — even/odd layer numbers alternate which corner the word and caption occupy

Animation duration is derived from buildup.duration / speed, so audio and visual are always in sync by construction.


Project Structure

lain-layers/
├── server.py        # stdlib HTTP server: entries CRUD + /api/speak (espeak-ng + DSP)
├── index.html       # page structure
├── style.css        # theme + title-card keyframe animation
├── app.js           # app logic + letter-assembly animation
├── entries.json     # local journal data (auto-created, git-ignored)
└── assets/
    ├── Lovelt__.ttf         # display font (Love Letter TW)
    └── titlecard-bg.jpg     # title card background

License

MIT © 2026 Ymsniper

About

A local journal app themed after Serial Experiments Lain's recurring Layer:0X title cards.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors