staticfx 2.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (102) hide show
  1. staticfx-2.0.0/CHANGELOG.md +73 -0
  2. staticfx-2.0.0/CONTRIBUTING.md +71 -0
  3. staticfx-2.0.0/LICENSE +21 -0
  4. staticfx-2.0.0/MANIFEST.in +9 -0
  5. staticfx-2.0.0/PKG-INFO +164 -0
  6. staticfx-2.0.0/README.md +130 -0
  7. staticfx-2.0.0/docs/ARCHITECTURE.md +150 -0
  8. staticfx-2.0.0/docs/EFFECT_FORMAT.md +392 -0
  9. staticfx-2.0.0/docs/RELEASING.md +58 -0
  10. staticfx-2.0.0/docs/STUDIO.md +98 -0
  11. staticfx-2.0.0/docs/USAGE.md +208 -0
  12. staticfx-2.0.0/docs/WRITING_EFFECTS.md +145 -0
  13. staticfx-2.0.0/examples/cell_division.sfx +38 -0
  14. staticfx-2.0.0/examples/ion_storm.sfx +45 -0
  15. staticfx-2.0.0/examples/neon_mandala.sfx +53 -0
  16. staticfx-2.0.0/pyproject.toml +89 -0
  17. staticfx-2.0.0/setup.cfg +4 -0
  18. staticfx-2.0.0/staticfx/__init__.py +3 -0
  19. staticfx-2.0.0/staticfx/__main__.py +5 -0
  20. staticfx-2.0.0/staticfx/app/__init__.py +1 -0
  21. staticfx-2.0.0/staticfx/app/app.py +335 -0
  22. staticfx-2.0.0/staticfx/app/keymap.py +163 -0
  23. staticfx-2.0.0/staticfx/app/player.py +143 -0
  24. staticfx-2.0.0/staticfx/app/session.py +97 -0
  25. staticfx-2.0.0/staticfx/cli/__init__.py +20 -0
  26. staticfx-2.0.0/staticfx/cli/commands.py +293 -0
  27. staticfx-2.0.0/staticfx/cli/parser.py +185 -0
  28. staticfx-2.0.0/staticfx/core/__init__.py +1 -0
  29. staticfx-2.0.0/staticfx/core/clock.py +30 -0
  30. staticfx-2.0.0/staticfx/core/color.py +89 -0
  31. staticfx-2.0.0/staticfx/core/frame.py +85 -0
  32. staticfx-2.0.0/staticfx/core/glyphs.py +15 -0
  33. staticfx-2.0.0/staticfx/core/keys.py +116 -0
  34. staticfx-2.0.0/staticfx/core/renderer.py +84 -0
  35. staticfx-2.0.0/staticfx/core/terminal.py +148 -0
  36. staticfx-2.0.0/staticfx/effects/__init__.py +1 -0
  37. staticfx-2.0.0/staticfx/effects/base.py +88 -0
  38. staticfx-2.0.0/staticfx/effects/builtin/__init__.py +203 -0
  39. staticfx-2.0.0/staticfx/effects/builtin/fields.py +404 -0
  40. staticfx-2.0.0/staticfx/effects/builtin/geometry.py +77 -0
  41. staticfx-2.0.0/staticfx/effects/builtin/particles.py +623 -0
  42. staticfx-2.0.0/staticfx/effects/builtin/shapes.py +255 -0
  43. staticfx-2.0.0/staticfx/effects/builtin/signals.py +298 -0
  44. staticfx-2.0.0/staticfx/effects/builtin/simulations.py +439 -0
  45. staticfx-2.0.0/staticfx/effects/builtin/terminal.py +362 -0
  46. staticfx-2.0.0/staticfx/effects/curated.py +16 -0
  47. staticfx-2.0.0/staticfx/effects/engine/__init__.py +11 -0
  48. staticfx-2.0.0/staticfx/effects/engine/effect.py +267 -0
  49. staticfx-2.0.0/staticfx/effects/engine/filters.py +115 -0
  50. staticfx-2.0.0/staticfx/effects/engine/generate.py +203 -0
  51. staticfx-2.0.0/staticfx/effects/engine/layers.py +396 -0
  52. staticfx-2.0.0/staticfx/effects/engine/recipe.py +215 -0
  53. staticfx-2.0.0/staticfx/effects/engine/schema.py +87 -0
  54. staticfx-2.0.0/staticfx/effects/engine/warps.py +203 -0
  55. staticfx-2.0.0/staticfx/effects/registry.py +103 -0
  56. staticfx-2.0.0/staticfx/effects/spec.py +51 -0
  57. staticfx-2.0.0/staticfx/integrations.py +48 -0
  58. staticfx-2.0.0/staticfx/library/__init__.py +1 -0
  59. staticfx-2.0.0/staticfx/library/config.py +122 -0
  60. staticfx-2.0.0/staticfx/library/effects.py +159 -0
  61. staticfx-2.0.0/staticfx/library/files.py +44 -0
  62. staticfx-2.0.0/staticfx/library/legacy.py +180 -0
  63. staticfx-2.0.0/staticfx/library/looks.py +134 -0
  64. staticfx-2.0.0/staticfx/postfx.py +78 -0
  65. staticfx-2.0.0/staticfx/py.typed +0 -0
  66. staticfx-2.0.0/staticfx/themes.py +162 -0
  67. staticfx-2.0.0/staticfx/ui/__init__.py +1 -0
  68. staticfx-2.0.0/staticfx/ui/browser.py +269 -0
  69. staticfx-2.0.0/staticfx/ui/dialogs.py +95 -0
  70. staticfx-2.0.0/staticfx/ui/help.py +99 -0
  71. staticfx-2.0.0/staticfx/ui/pickers.py +221 -0
  72. staticfx-2.0.0/staticfx/ui/play.py +213 -0
  73. staticfx-2.0.0/staticfx/ui/screen.py +39 -0
  74. staticfx-2.0.0/staticfx/ui/settings.py +262 -0
  75. staticfx-2.0.0/staticfx/ui/studio.py +433 -0
  76. staticfx-2.0.0/staticfx/ui/widgets.py +196 -0
  77. staticfx-2.0.0/staticfx.egg-info/PKG-INFO +164 -0
  78. staticfx-2.0.0/staticfx.egg-info/SOURCES.txt +100 -0
  79. staticfx-2.0.0/staticfx.egg-info/dependency_links.txt +1 -0
  80. staticfx-2.0.0/staticfx.egg-info/entry_points.txt +2 -0
  81. staticfx-2.0.0/staticfx.egg-info/requires.txt +5 -0
  82. staticfx-2.0.0/staticfx.egg-info/top_level.txt +1 -0
  83. staticfx-2.0.0/tests/conftest.py +49 -0
  84. staticfx-2.0.0/tests/fixtures/v1_config.json +24 -0
  85. staticfx-2.0.0/tests/fixtures/v1_looks.json +17 -0
  86. staticfx-2.0.0/tests/fixtures/v1_plugin.sfx +17 -0
  87. staticfx-2.0.0/tests/fixtures/v1_studio_effect.sfx +115 -0
  88. staticfx-2.0.0/tests/pty_session.py +174 -0
  89. staticfx-2.0.0/tests/test_app.py +226 -0
  90. staticfx-2.0.0/tests/test_cli.py +131 -0
  91. staticfx-2.0.0/tests/test_core.py +148 -0
  92. staticfx-2.0.0/tests/test_details.py +203 -0
  93. staticfx-2.0.0/tests/test_docs.py +78 -0
  94. staticfx-2.0.0/tests/test_effects.py +83 -0
  95. staticfx-2.0.0/tests/test_engine.py +110 -0
  96. staticfx-2.0.0/tests/test_library.py +191 -0
  97. staticfx-2.0.0/tests/test_terminal.py +120 -0
  98. staticfx-2.0.0/tests/test_ui.py +279 -0
  99. staticfx-2.0.0/tools/curate.py +144 -0
  100. staticfx-2.0.0/tools/demo.py +96 -0
  101. staticfx-2.0.0/tools/gen_docs.py +82 -0
  102. staticfx-2.0.0/tools/screenshot.py +120 -0
@@ -0,0 +1,73 @@
1
+ # Changelog
2
+
3
+ ## 2.0.0 (2026-11-07)
4
+
5
+ A rewrite. Your settings, favourites, looks and Studio effects carry over
6
+ automatically.
7
+
8
+ ### New
9
+
10
+ - An effect browser with groups (Favourites, Recent, My Effects, Gallery,
11
+ seven built-in categories, Generated), search that ranks name matches first,
12
+ and live preview beside the list.
13
+ - A Gallery of 47 of the best generated effects, picked by hand.
14
+ - A settings screen (`o`): frame rate, character set, colour mode,
15
+ auto-cycle interval, and rebinding any key, with conflict warnings.
16
+ - A welcome card on first run, an optional status bar (`i`), and a key hint
17
+ line on start-up.
18
+ - In the Studio, every control has a real name and range, with a one-line
19
+ explanation of the selected setting; save as a copy (`a`); unlimited undo.
20
+ - Subcommands: `list`, `themes`, `looks`, `new`, `import`, `export`,
21
+ `render`, `fetch`, `doctor`. The 1.x flags still work.
22
+ - `staticfx doctor` checks your terminal and saved files and renders
23
+ every effect once.
24
+ - 26 themes, up from 13, including takes on Dracula, Nord, Gruvbox,
25
+ Solarized, Catppuccin and Tokyo Night. The theme menu shows each palette.
26
+ - Plain keys everywhere: menus no longer need Ctrl combinations: in the
27
+ browser `/` searches and `f`, `e`, `n`, `d` act on the highlighted effect.
28
+ Letters work with or without Shift, and every menu closes with `q`.
29
+ - Built-in looks reworked into twelve, each with a description.
30
+ - Meteor Shower has a twinkling sky; Fireworks fill large terminals.
31
+ - Docs: usage guide, Studio guide, file format, writing effects, architecture.
32
+
33
+ ### Changed
34
+
35
+ - One procedural engine: 1.x had two (`proc_*` and `gen_*`). The
36
+ `gen_*` effects are gone; favourites pointing at them are dropped
37
+ quietly. `proc_*` effects look the same as before.
38
+ - One `.sfx` format (version 3) with readable keys; 1.x Studio files
39
+ (version 2) and 1.x plugins are converted when read; old plugins are copied
40
+ into My Effects once.
41
+ - An automated quality gate removes 8 generated effects that were blank,
42
+ frozen, or a solid wall.
43
+ - Strobe is hidden until you allow flashing in Settings, and the theme
44
+ menu never previews it unless you have.
45
+ - The 1.x key that cycled rainbow and strobe is gone: Rainbow is a normal theme now. The old
46
+ "rainbow" overlay is called **recolour** (`w`) and keeps the effect's
47
+ shading.
48
+ - Presets and hacker scenes are merged into **looks**.
49
+ - The theme `monochrome` is now `mono` (the old name still works).
50
+ - Only rows that changed are redrawn each frame.
51
+
52
+ ### Fixed
53
+
54
+ - A deleted effect or an unknown theme in `config.json` no longer stops
55
+ StaticFX from starting; any bad setting is ignored with a message.
56
+ - Ctrl-C in the Studio asks before discarding your work instead of quitting.
57
+ - Ctrl-Z suspends cleanly and restores the screen on `fg`.
58
+ - A user effect named like a built-in (`plasma`) no longer hides the
59
+ built-in on the command line.
60
+ - Plugin effects deleted in the menu came back on restart.
61
+ - Hand-edited config values of the wrong type were accepted silently.
62
+ - Below 40×12, a "terminal too small" message replaces menus that used to
63
+ draw over each other.
64
+ - Glyphs outside the terminal's character set could appear in ASCII mode.
65
+
66
+ ### Removed
67
+
68
+ - The `gen_*` engine and the hidden `--plugin-dir` option.
69
+
70
+ ## 1.0.0
71
+
72
+ First release: 50 built-in effects, 2,000 procedural effects in two
73
+ families, themes, looks, the Effect Studio and `.sfx` sharing.
@@ -0,0 +1,71 @@
1
+ # Contributing
2
+
3
+ Thanks for wanting to help. Bug reports, new effects and fixes are all
4
+ welcome.
5
+
6
+ ## Setting up
7
+
8
+ ```bash
9
+ git clone https://github.com/ethanlabs101/StaticFX && cd StaticFX
10
+ python3 -m venv .venv && . .venv/bin/activate
11
+ pip install -e '.[dev]'
12
+ ```
13
+
14
+ StaticFX itself has no dependencies; those three are only for development.
15
+ The scripts in `tools/` also need Pillow (and `tools/demo.py` needs ffmpeg).
16
+
17
+ ## Before you open a pull request
18
+
19
+ ```bash
20
+ pytest # everything, including real-terminal tests
21
+ ruff check . && ruff format --check .
22
+ mypy
23
+ ```
24
+
25
+ CI runs the same on Linux and macOS with Python 3.10 to 3.13.
26
+
27
+ ## Where things go
28
+
29
+ [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) maps the code. In short:
30
+
31
+ - A new hand-written effect: [docs/WRITING_EFFECTS.md](docs/WRITING_EFFECTS.md).
32
+ - A new layer, warp or filter for the engine: add a function and a `Kind`
33
+ entry in `staticfx/effects/engine/layers.py` (or `warps.py`,
34
+ `filters.py`), then run `python tools/gen_docs.py` to update the format
35
+ reference. The Studio picks it up by itself.
36
+ - A new screen: subclass `Screen` in `staticfx/ui/`, give it a `help` tuple,
37
+ and add a test in `tests/test_ui.py`. It gets drawn at the minimum terminal
38
+ size and in ASCII mode automatically if you add it to `OPENERS` there.
39
+ - A new main-view key: add the action to `ACTIONS` and a default to
40
+ `DEFAULT_KEYS` in `staticfx/app/keymap.py`, and its handler in
41
+ `PlayScreen.actions`. A test checks the three agree, and another checks the
42
+ usage guide mentions it.
43
+
44
+ ## Things that need care
45
+
46
+ - **Generated effects must not change by accident.** `test_engine.py`
47
+ fingerprints all 1,000 recipes. If you change `generate.py` or the
48
+ maths behind an existing layer, warp or filter, the fingerprint changes and
49
+ everyone's `proc_NNN` favourites point at different pictures. Do it only on
50
+ purpose, update the fingerprint, rerun `python tools/curate.py`, and note it
51
+ in the changelog.
52
+ - **Effect keys are forever.** They're stored in favourites, looks and
53
+ scripts. Rename the label, never the key.
54
+ - **Saved files must stay readable.** If you change the `.sfx` format or
55
+ `config.json`, bump the version and keep reading the old one (see
56
+ `library/legacy.py` and `config._migrate`).
57
+ - **Nothing may stop the app from starting.** Bad files and values are
58
+ reported and skipped, never raised.
59
+
60
+ ## Style
61
+
62
+ `ruff format` decides formatting (100 columns). Beyond that: write code for
63
+ the next person to read, keep functions short, name things for what they
64
+ mean, and comment on *why*, not *what*. Docstrings on modules, classes and
65
+ anything non-obvious; not on every getter.
66
+
67
+ ## Reporting a bug
68
+
69
+ Please include the output of `staticfx doctor`, your terminal and OS, and
70
+ what you pressed. If an effect looks wrong, `staticfx render EFFECT --ansi
71
+ --seed 1 --out frame.txt` captures it exactly.
staticfx-2.0.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 ethangrames04
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,9 @@
1
+ # The source distribution carries the tests, docs and examples so it can be
2
+ # checked and repackaged on its own. Screenshots stay on GitHub.
3
+ include CHANGELOG.md CONTRIBUTING.md
4
+ graft docs
5
+ graft examples
6
+ graft tests
7
+ graft tools
8
+ prune assets
9
+ global-exclude __pycache__ *.py[cod]
@@ -0,0 +1,164 @@
1
+ Metadata-Version: 2.4
2
+ Name: staticfx
3
+ Version: 2.0.0
4
+ Summary: Animated ASCII effects for your terminal, with a studio for making your own
5
+ Author: ethanlabs101
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/ethanlabs101/StaticFX
8
+ Project-URL: Documentation, https://github.com/ethanlabs101/StaticFX/blob/main/docs/USAGE.md
9
+ Project-URL: Changelog, https://github.com/ethanlabs101/StaticFX/blob/main/CHANGELOG.md
10
+ Project-URL: Issues, https://github.com/ethanlabs101/StaticFX/issues
11
+ Keywords: terminal,ascii,animation,cmatrix,screensaver,tui,ascii-art
12
+ Classifier: Development Status :: 5 - Production/Stable
13
+ Classifier: Environment :: Console
14
+ Classifier: Intended Audience :: End Users/Desktop
15
+ Classifier: Operating System :: MacOS
16
+ Classifier: Operating System :: POSIX :: Linux
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3 :: Only
19
+ Classifier: Programming Language :: Python :: 3.10
20
+ Classifier: Programming Language :: Python :: 3.11
21
+ Classifier: Programming Language :: Python :: 3.12
22
+ Classifier: Programming Language :: Python :: 3.13
23
+ Classifier: Topic :: Artistic Software
24
+ Classifier: Topic :: Terminals
25
+ Classifier: Typing :: Typed
26
+ Requires-Python: >=3.10
27
+ Description-Content-Type: text/markdown
28
+ License-File: LICENSE
29
+ Provides-Extra: dev
30
+ Requires-Dist: pytest>=8; extra == "dev"
31
+ Requires-Dist: ruff>=0.6; extra == "dev"
32
+ Requires-Dist: mypy>=1.10; extra == "dev"
33
+ Dynamic: license-file
34
+
35
+ # StaticFX
36
+
37
+ Animated ASCII effects for your terminal. Think `cmatrix`, but with a
38
+ thousand more effects, live theme switching, and a studio for making your own.
39
+
40
+ ![StaticFX demo: Matrix rain, the effect browser, a galaxy, the Studio](https://raw.githubusercontent.com/ethanlabs101/StaticFX/main/assets/demo.gif)
41
+
42
+ Pure Python, no dependencies, runs in any modern terminal on Linux, macOS
43
+ and WSL.
44
+
45
+ ## Install
46
+
47
+ You need Python 3.10 or newer.
48
+
49
+ ```bash
50
+ pipx install staticfx
51
+ staticfx
52
+ ```
53
+
54
+ (`pip install staticfx` works too; [pipx](https://pipx.pypa.io) just keeps it in
55
+ its own environment.) To try it without installing anything:
56
+
57
+ ```bash
58
+ git clone https://github.com/ethanlabs101/StaticFX
59
+ cd StaticFX
60
+ python3 -m staticfx
61
+ ```
62
+
63
+ ## The 30-second tour
64
+
65
+ Key | Does
66
+ --- | ---
67
+ `←` `→` | Previous / next effect
68
+ `m` | Browse and search every effect, with live preview
69
+ `t` | Change the colour theme
70
+ `l` | Looks: effect + theme + filters, saved under a name
71
+ `n` | Make your own effect in the Studio
72
+ `f` | Favourite
73
+ `a` | Auto-cycle (screensaver mode)
74
+ `?` | Every key
75
+ `q` | Quit
76
+
77
+ Everything is remappable in Settings (`o`). The full guide is in
78
+ [docs/USAGE.md](https://github.com/ethanlabs101/StaticFX/blob/main/docs/USAGE.md).
79
+
80
+ ## What's inside
81
+
82
+ There are 50 hand-written effects in seven groups. Rain: the classic, plus
83
+ binary, hex and glitch versions. Particles: snow, embers, fireworks, orbits.
84
+ Space: a starfield, a galaxy, a black hole, a wormhole. Waves and fields:
85
+ plasma, aurora, a lava lamp, a synthwave grid. Geometry: a tumbling cube, a 4D
86
+ tesseract, spirographs. Simulations: Game of Life, Langton's ant, fire, a maze
87
+ that carves itself. Retro tech: boot screens, decryption, circuits, TV static.
88
+
89
+ On top of those are 992 generated effects. Each one is a recipe of layered
90
+ patterns (spirals, Julia and Mandelbrot fractals, Voronoi cells, metaballs,
91
+ noise), warps (kaleidoscope, swirl, tile, fold) and filters. They're seeded,
92
+ so `proc_042` is the same effect for everyone, forever. An automated quality
93
+ check weeds out the duds, and 47 hand-picked ones live in the Gallery.
94
+
95
+ Then there are your own. The Studio remixes any generated effect or starts
96
+ from scratch, with a live preview and undo, and saves small JSON files you
97
+ can share.
98
+
99
+ You also get:
100
+
101
+ - **26 colour themes**: originals like Matrix, Inferno and Vaporwave, takes on
102
+ popular editor schemes (Dracula, Nord, Gruvbox, Catppuccin, Tokyo Night), an
103
+ animated rainbow, and Strobe if you opt in (it's off by default for
104
+ photosensitivity reasons)
105
+ - filters that work on anything: CRT scanlines, glitch, rainbow recolour
106
+ - looks, which save an effect with its theme, speed and filters so you can
107
+ recall it with one key (a dozen come built in)
108
+
109
+ ![The theme menu, with a colour swatch for each theme](https://raw.githubusercontent.com/ethanlabs101/StaticFX/main/assets/themes.png)
110
+
111
+ ![The effect browser](https://raw.githubusercontent.com/ethanlabs101/StaticFX/main/assets/browser.png)
112
+
113
+ ## Make your own
114
+
115
+ Press `n` for a new effect, or highlight a generated one in the browser and
116
+ press `e` to remix it. Arrow keys change things; `s` saves it to
117
+ **My Effects**.
118
+
119
+ ![The Effect Studio](https://raw.githubusercontent.com/ethanlabs101/StaticFX/main/assets/studio.png)
120
+
121
+ See [docs/STUDIO.md](https://github.com/ethanlabs101/StaticFX/blob/main/docs/STUDIO.md) for what every control does, and
122
+ [docs/EFFECT_FORMAT.md](https://github.com/ethanlabs101/StaticFX/blob/main/docs/EFFECT_FORMAT.md) for the file format if you'd
123
+ rather write recipes by hand.
124
+
125
+ ## Command line
126
+
127
+ ```bash
128
+ staticfx -e plasma -t ocean # start on an effect and theme
129
+ staticfx --look "Synthwave" # start from a look
130
+ staticfx --auto 30 --duration 600 # screensaver: new effect every 30s, quit after 10 min
131
+ staticfx list fire # search effects
132
+ staticfx render galaxy --size 80x24 # print one frame (add --ansi for colour)
133
+ staticfx fetch # fastfetch/neofetch output over an effect
134
+ staticfx new "Night Drive" # create a random effect in My Effects
135
+ staticfx export my_night_drive nd.sfx # share it
136
+ staticfx doctor # check your setup
137
+ ```
138
+
139
+ `staticfx --help` and `staticfx COMMAND --help` list everything.
140
+
141
+ ## Docs
142
+
143
+ - [Usage](https://github.com/ethanlabs101/StaticFX/blob/main/docs/USAGE.md): every key, the browser, themes, looks, settings,
144
+ command line, troubleshooting
145
+ - [The Studio](https://github.com/ethanlabs101/StaticFX/blob/main/docs/STUDIO.md)
146
+ - [Effect file format](https://github.com/ethanlabs101/StaticFX/blob/main/docs/EFFECT_FORMAT.md)
147
+ - [Writing a built-in effect](https://github.com/ethanlabs101/StaticFX/blob/main/docs/WRITING_EFFECTS.md) in Python
148
+ - [Architecture](https://github.com/ethanlabs101/StaticFX/blob/main/docs/ARCHITECTURE.md): how it fits together, and why
149
+ - [Releasing](https://github.com/ethanlabs101/StaticFX/blob/main/docs/RELEASING.md): publishing a new version to PyPI
150
+
151
+ ## Development
152
+
153
+ ```bash
154
+ git clone https://github.com/ethanlabs101/StaticFX && cd StaticFX
155
+ python3 -m pip install -e '.[dev]'
156
+ python3 -m pytest # ~650 tests, about a minute
157
+ ruff check . && ruff format --check . && mypy
158
+ ```
159
+
160
+ See [CONTRIBUTING.md](https://github.com/ethanlabs101/StaticFX/blob/main/CONTRIBUTING.md) for how changes are made and checked.
161
+
162
+ ## License
163
+
164
+ [MIT](https://github.com/ethanlabs101/StaticFX/blob/main/LICENSE)
@@ -0,0 +1,130 @@
1
+ # StaticFX
2
+
3
+ Animated ASCII effects for your terminal. Think `cmatrix`, but with a
4
+ thousand more effects, live theme switching, and a studio for making your own.
5
+
6
+ ![StaticFX demo: Matrix rain, the effect browser, a galaxy, the Studio](https://raw.githubusercontent.com/ethanlabs101/StaticFX/main/assets/demo.gif)
7
+
8
+ Pure Python, no dependencies, runs in any modern terminal on Linux, macOS
9
+ and WSL.
10
+
11
+ ## Install
12
+
13
+ You need Python 3.10 or newer.
14
+
15
+ ```bash
16
+ pipx install staticfx
17
+ staticfx
18
+ ```
19
+
20
+ (`pip install staticfx` works too; [pipx](https://pipx.pypa.io) just keeps it in
21
+ its own environment.) To try it without installing anything:
22
+
23
+ ```bash
24
+ git clone https://github.com/ethanlabs101/StaticFX
25
+ cd StaticFX
26
+ python3 -m staticfx
27
+ ```
28
+
29
+ ## The 30-second tour
30
+
31
+ Key | Does
32
+ --- | ---
33
+ `←` `→` | Previous / next effect
34
+ `m` | Browse and search every effect, with live preview
35
+ `t` | Change the colour theme
36
+ `l` | Looks: effect + theme + filters, saved under a name
37
+ `n` | Make your own effect in the Studio
38
+ `f` | Favourite
39
+ `a` | Auto-cycle (screensaver mode)
40
+ `?` | Every key
41
+ `q` | Quit
42
+
43
+ Everything is remappable in Settings (`o`). The full guide is in
44
+ [docs/USAGE.md](https://github.com/ethanlabs101/StaticFX/blob/main/docs/USAGE.md).
45
+
46
+ ## What's inside
47
+
48
+ There are 50 hand-written effects in seven groups. Rain: the classic, plus
49
+ binary, hex and glitch versions. Particles: snow, embers, fireworks, orbits.
50
+ Space: a starfield, a galaxy, a black hole, a wormhole. Waves and fields:
51
+ plasma, aurora, a lava lamp, a synthwave grid. Geometry: a tumbling cube, a 4D
52
+ tesseract, spirographs. Simulations: Game of Life, Langton's ant, fire, a maze
53
+ that carves itself. Retro tech: boot screens, decryption, circuits, TV static.
54
+
55
+ On top of those are 992 generated effects. Each one is a recipe of layered
56
+ patterns (spirals, Julia and Mandelbrot fractals, Voronoi cells, metaballs,
57
+ noise), warps (kaleidoscope, swirl, tile, fold) and filters. They're seeded,
58
+ so `proc_042` is the same effect for everyone, forever. An automated quality
59
+ check weeds out the duds, and 47 hand-picked ones live in the Gallery.
60
+
61
+ Then there are your own. The Studio remixes any generated effect or starts
62
+ from scratch, with a live preview and undo, and saves small JSON files you
63
+ can share.
64
+
65
+ You also get:
66
+
67
+ - **26 colour themes**: originals like Matrix, Inferno and Vaporwave, takes on
68
+ popular editor schemes (Dracula, Nord, Gruvbox, Catppuccin, Tokyo Night), an
69
+ animated rainbow, and Strobe if you opt in (it's off by default for
70
+ photosensitivity reasons)
71
+ - filters that work on anything: CRT scanlines, glitch, rainbow recolour
72
+ - looks, which save an effect with its theme, speed and filters so you can
73
+ recall it with one key (a dozen come built in)
74
+
75
+ ![The theme menu, with a colour swatch for each theme](https://raw.githubusercontent.com/ethanlabs101/StaticFX/main/assets/themes.png)
76
+
77
+ ![The effect browser](https://raw.githubusercontent.com/ethanlabs101/StaticFX/main/assets/browser.png)
78
+
79
+ ## Make your own
80
+
81
+ Press `n` for a new effect, or highlight a generated one in the browser and
82
+ press `e` to remix it. Arrow keys change things; `s` saves it to
83
+ **My Effects**.
84
+
85
+ ![The Effect Studio](https://raw.githubusercontent.com/ethanlabs101/StaticFX/main/assets/studio.png)
86
+
87
+ See [docs/STUDIO.md](https://github.com/ethanlabs101/StaticFX/blob/main/docs/STUDIO.md) for what every control does, and
88
+ [docs/EFFECT_FORMAT.md](https://github.com/ethanlabs101/StaticFX/blob/main/docs/EFFECT_FORMAT.md) for the file format if you'd
89
+ rather write recipes by hand.
90
+
91
+ ## Command line
92
+
93
+ ```bash
94
+ staticfx -e plasma -t ocean # start on an effect and theme
95
+ staticfx --look "Synthwave" # start from a look
96
+ staticfx --auto 30 --duration 600 # screensaver: new effect every 30s, quit after 10 min
97
+ staticfx list fire # search effects
98
+ staticfx render galaxy --size 80x24 # print one frame (add --ansi for colour)
99
+ staticfx fetch # fastfetch/neofetch output over an effect
100
+ staticfx new "Night Drive" # create a random effect in My Effects
101
+ staticfx export my_night_drive nd.sfx # share it
102
+ staticfx doctor # check your setup
103
+ ```
104
+
105
+ `staticfx --help` and `staticfx COMMAND --help` list everything.
106
+
107
+ ## Docs
108
+
109
+ - [Usage](https://github.com/ethanlabs101/StaticFX/blob/main/docs/USAGE.md): every key, the browser, themes, looks, settings,
110
+ command line, troubleshooting
111
+ - [The Studio](https://github.com/ethanlabs101/StaticFX/blob/main/docs/STUDIO.md)
112
+ - [Effect file format](https://github.com/ethanlabs101/StaticFX/blob/main/docs/EFFECT_FORMAT.md)
113
+ - [Writing a built-in effect](https://github.com/ethanlabs101/StaticFX/blob/main/docs/WRITING_EFFECTS.md) in Python
114
+ - [Architecture](https://github.com/ethanlabs101/StaticFX/blob/main/docs/ARCHITECTURE.md): how it fits together, and why
115
+ - [Releasing](https://github.com/ethanlabs101/StaticFX/blob/main/docs/RELEASING.md): publishing a new version to PyPI
116
+
117
+ ## Development
118
+
119
+ ```bash
120
+ git clone https://github.com/ethanlabs101/StaticFX && cd StaticFX
121
+ python3 -m pip install -e '.[dev]'
122
+ python3 -m pytest # ~650 tests, about a minute
123
+ ruff check . && ruff format --check . && mypy
124
+ ```
125
+
126
+ See [CONTRIBUTING.md](https://github.com/ethanlabs101/StaticFX/blob/main/CONTRIBUTING.md) for how changes are made and checked.
127
+
128
+ ## License
129
+
130
+ [MIT](https://github.com/ethanlabs101/StaticFX/blob/main/LICENSE)
@@ -0,0 +1,150 @@
1
+ # Architecture
2
+
3
+ StaticFX is about 9,000 lines of Python (nearly half of it the effects themselves) with no
4
+ dependencies. This page is
5
+ the map: what lives where, what happens every frame, and why it's built the
6
+ way it is.
7
+
8
+ ## Layers
9
+
10
+ ```
11
+ cli/ parse the command line, run a subcommand
12
+ │
13
+ app/ the interactive app: settings, player, screen stack, run loop
14
+ │ ui/ screens (browser, Studio, settings, ...) and drawing helpers
15
+ │
16
+ effects/ what can be drawn: built-ins, the procedural engine, the registry
17
+ library/ what the user saves: settings, .sfx effects, looks
18
+ │
19
+ core/ terminal plumbing: colour, frames, rendering, keys, terminal modes
20
+ ```
21
+
22
+ Imports only point down this list. `core` knows nothing about effects;
23
+ `effects` knows nothing about the app; screens reach the app only through
24
+ the `App` object they're given. The one upward reference, `App` creating the
25
+ first `PlayScreen`, is deliberate: the app owns its screen stack.
26
+
27
+ ## Modules
28
+
29
+ Path | What it does
30
+ --- | ---
31
+ `core/color.py` | `RGB` and the few colour operations effects need.
32
+ `core/frame.py` | `Frame`: a grid of `(char, colour)` cells. Spaces are transparent.
33
+ `core/renderer.py` | Encodes a frame as ANSI, writing only rows that changed since the last frame.
34
+ `core/keys.py` | Turns raw terminal bytes into key names (`up`, `ctrl-e`, `a`).
35
+ `core/terminal.py` | Takes over the terminal (alternate screen, raw-ish input) and always gives it back: on exit, exceptions, `SIGTERM`, and around `Ctrl-Z`.
36
+ `core/clock.py` | Frame pacing.
37
+ `themes.py` | Colour themes as 256-entry ramps, plus the animated Rainbow and Strobe.
38
+ `postfx.py` | The CRT, glitch and recolour filters applied to finished frames.
39
+ `effects/base.py` | The `Effect` base class and its contract.
40
+ `effects/builtin/` | The 50 hand-written effects, and `CATALOGUE`, which names and groups them.
41
+ `effects/engine/` | The procedural engine: `schema.py` (parameter descriptions), `layers.py`, `warps.py`, `filters.py`, `recipe.py` (the data model and its JSON form), `generate.py` (seed → recipe), `effect.py` (drawing a recipe).
42
+ `effects/registry.py` | Every playable effect in a stable order, and name lookup.
43
+ `effects/curated.py` | Which generated effects are excluded, and the Gallery.
44
+ `library/config.py` | `Settings` and a loader that never fails.
45
+ `library/effects.py` | Reading and writing `.sfx` files.
46
+ `library/legacy.py` | Converting StaticFX 1.x files.
47
+ `library/looks.py` | Saved looks and the built-in ones.
48
+ `app/app.py` | `App`: settings, library, player, screen stack, toasts, saving.
49
+ `app/player.py` | What's on screen: the effect, theme, speed, filters, and previews.
50
+ `app/keymap.py` | Main-view actions and their keys.
51
+ `app/session.py` | The real-terminal loop, and headless rendering.
52
+ `ui/screen.py` | The `Screen` base class.
53
+ `ui/widgets.py` | Box characters, colours, panels, scrolling lists.
54
+ `ui/play.py`, `browser.py`, `pickers.py`, `studio.py`, `settings.py`, `help.py`, `dialogs.py` | One file per screen.
55
+ `cli/parser.py`, `cli/commands.py` | Arguments, and one function per subcommand.
56
+
57
+ ## One frame
58
+
59
+ `session.run` loops:
60
+
61
+ 1. **Input.** `KeyReader` collects pending keys. `App.handle_key` gives each
62
+ one to the screen on top of the stack. `Ctrl-Z` is the exception: the app
63
+ asks the session to suspend.
64
+ 2. **Resize.** If the terminal changed size, the frame, the player and every
65
+ effect are resized and the screen is redrawn from scratch.
66
+ 3. **Update.** `App.update(dt)` advances the player (which advances the
67
+ effect, scaled by speed), every screen, the toast timer and auto-cycle.
68
+ 4. **Draw.** `App.draw(frame)` asks the player to draw the effect and apply
69
+ filters, then draws each screen from the bottom up.
70
+ 5. **Output.** `Renderer.diff` encodes the rows that changed; one `write`.
71
+ 6. **Save.** If settings changed, they're written, at most once a second.
72
+ 7. **Wait** for the next frame.
73
+
74
+ Nothing in `App` or the screens touches the terminal, which is why the test
75
+ suite can drive the whole app with key presses and inspect the frames.
76
+
77
+ ## Decisions
78
+
79
+ **No curses.** `curses` would handle terminal modes, but it's not available
80
+ on every Python build, its colour support stops at 256 colours without
81
+ work, and it makes the app much harder to test. Writing ANSI directly is a
82
+ few hundred lines in `core/` and gives 24-bit colour everywhere.
83
+
84
+ **Signals off in the terminal.** The terminal is put in a mode where Ctrl-C
85
+ and Ctrl-Z arrive as keys rather than signals. Each screen decides what they
86
+ mean, so Ctrl-C in the Studio can ask before throwing away your work instead
87
+ of killing the program. Suspend is done by hand: restore the terminal, stop,
88
+ re-enter when resumed.
89
+
90
+ **Row-level diffing.** Only rows that changed since the last frame are
91
+ written. For busy effects that saves little (about 7% of bytes across the
92
+ built-ins, measured at 120×36), but a paused effect or a slow one costs
93
+ almost nothing to redraw. Cell-level diffing would save more on busy
94
+ effects, at the price of a cursor move per changed run and noticeably more
95
+ code in the hottest loop; it's the obvious next step if output volume ever
96
+ becomes the bottleneck.
97
+
98
+ **`RGB` is a `NamedTuple`.** Thousands of colours are created per frame;
99
+ tuples are the cheapest immutable, hashable value Python has.
100
+
101
+ **A screen stack.** Each mode (browser, Studio, a confirmation dialog) is a
102
+ `Screen` with `handle`, `update` and `draw`. The top one gets the keys.
103
+ Opening a dialog over the Studio is just a push; nothing else needs to know.
104
+ This replaced a single class with a long `if menu: ... elif studio: ...`
105
+ chain.
106
+
107
+ **One parameter schema.** Each layer, warp and filter kind declares its
108
+ parameters once (`Param` in `engine/schema.py`). That one description is used
109
+ to validate `.sfx` files, fill in defaults, build the Studio's controls and
110
+ generate the reference in `EFFECT_FORMAT.md`, so they can't drift apart.
111
+
112
+ **Generated effects never change.** `generate(seed)` draws its random values
113
+ in a fixed order, and a test fingerprints all 1,000 recipes. Favourites,
114
+ saved looks and scripts that say `proc_042` keep meaning the same picture.
115
+ Changing the generator on purpose means updating the fingerprint and saying
116
+ so in the changelog.
117
+
118
+ **A quality gate, not a hand filter.** `tools/curate.py` renders every
119
+ generated recipe and excludes the ones that are blank, frozen or a solid
120
+ wall. It's repeatable: rerun it after changing the engine. The Gallery, by
121
+ contrast, is picked by eye; taste doesn't automate well.
122
+
123
+ **Settings can't stop the app from starting.** Every value in `config.json`
124
+ is checked on its own; a bad one is replaced by its default with a message.
125
+ The same goes for `.sfx` files and looks: a broken file is skipped and
126
+ reported, never fatal. The last effect you played may have been deleted;
127
+ StaticFX says so and starts on Matrix Rain.
128
+
129
+ **Adaptive detail.** The engine samples the pattern on a grid of blocks
130
+ rather than every cell when the terminal is large, and in the interactive
131
+ app it also coarsens or refines that grid based on how long frames take.
132
+ Headless rendering (`staticfx render`) keeps full detail so output is
133
+ deterministic.
134
+
135
+ ## Testing
136
+
137
+ `tests/` mirrors the layers:
138
+
139
+ File | Covers
140
+ --- | ---
141
+ `test_core.py` | Colours, frame clipping, renderer diffing, key decoding.
142
+ `test_engine.py` | The schema, recipe validation (including a fuzz test that corrupts real recipes), and the generated-effects fingerprint.
143
+ `test_effects.py` | Every built-in, and a sample of generated effects, at sizes from 1×1 to 250×70, with odd timesteps, in both character modes.
144
+ `test_library.py` | Settings, `.sfx` files, looks, and real files from StaticFX 1.x.
145
+ `test_app.py` | Startup, playing, favourites, looks, key bindings, saving.
146
+ `test_ui.py` | Every screen driven by key presses, and drawn at the minimum size and in ASCII mode.
147
+ `test_cli.py` | Every subcommand, and the 1.x flags.
148
+ `test_terminal.py` | The real program in a pseudo-terminal: quitting, Ctrl-C, Ctrl-Z, resizing, `SIGTERM`, a corrupt config.
149
+ `test_docs.py` | Generated docs are current and the README and guides match the code.
150
+ `test_details.py` | Filters, the fetch overlay, adaptive detail, name lookup, dialogs.