{"id":16525889,"url":"https://github.com/thetarnav/glitched-writer","last_synced_at":"2025-05-12T03:32:06.022Z","repository":{"id":45823859,"uuid":"216355063","full_name":"thetarnav/glitched-writer","owner":"thetarnav","description":"Glitched, text writing js module. Highly customizable settings. Decoding, decrypting, scrambling, or simply spelling out text.","archived":false,"fork":false,"pushed_at":"2021-08-15T15:43:48.000Z","size":5749,"stargazers_count":125,"open_issues_count":2,"forks_count":11,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-05-12T01:59:17.809Z","etag":null,"topics":["animation","cyberpunk","front-end","glitch","html","javascript","nier","termial","text","text-animation","typescript","web","writing"],"latest_commit_sha":null,"homepage":"https://glitched-writer.site","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/thetarnav.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-10-20T12:02:47.000Z","updated_at":"2025-04-23T01:08:00.000Z","dependencies_parsed_at":"2022-08-12T12:21:06.755Z","dependency_job_id":null,"html_url":"https://github.com/thetarnav/glitched-writer","commit_stats":null,"previous_names":[],"tags_count":41,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thetarnav%2Fglitched-writer","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thetarnav%2Fglitched-writer/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thetarnav%2Fglitched-writer/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thetarnav%2Fglitched-writer/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/thetarnav","download_url":"https://codeload.github.com/thetarnav/glitched-writer/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253668091,"owners_count":21944979,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["animation","cyberpunk","front-end","glitch","html","javascript","nier","termial","text","text-animation","typescript","web","writing"],"created_at":"2024-10-11T17:07:48.355Z","updated_at":"2025-05-12T03:32:05.649Z","avatar_url":"https://github.com/thetarnav.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Glitched Writer\n\n[![npm](https://img.shields.io/npm/v/glitched-writer)](https://www.npmjs.com/package/glitched-writer)\n[![npm type definitions](https://img.shields.io/npm/types/glitched-writer)](https://www.npmjs.com/package/glitched-writer)\n[![](https://data.jsdelivr.com/v1/package/npm/glitched-writer/badge?style=rounded)](https://www.jsdelivr.com/package/npm/glitched-writer)\n[![bundle size](https://img.shields.io/bundlephobia/minzip/glitched-writer)](https://bundlephobia.com/result?p=glitched-writer)\n[![npm](https://img.shields.io/npm/dt/glitched-writer)](https://www.npmjs.com/package/glitched-writer)\n\n[![glitched-writer-preview](https://user-images.githubusercontent.com/24491503/67164275-06ab6900-f379-11e9-81ac-cab76dbc8dcd.gif)](https://glitched-writer.site)\n\n### What is Glitched Writer:\n\n\u003e **A lightweight npm module for writing text to HTML elements. Highly customizable settings. Decoding, decrypting, scrambling, and simply spelling out text.**\n\n### Features:\n\n-  Manages text animation of **HTML Element**. Write, pause, play, add, remove and write some more!\n\n-  **Highly customizable** behavior. Set of options let you animate the text the way is suits your design.\n\n-  Adding Callback functions to fire on writer events (start, step, finish).\n\n-  Custom Event **gw-finished** will be dispatched on the HTML Element.\n\n-  For styling purposes writer attatches **gw-writing** class to the HTML Element and **data-gw-string attribute** with current string.\n\n-  Handles **html tags** \u0026 **html entities** (e.g. \\\u003cbr/\\\u003e, \\\u003ca href=\"#\"\\\u003elink\\\u003c/a\\\u003e, \\\u0026#59;, \\\u0026amp;).\n\n-  Can letterize string it into many **span** elements inside the parent element.\n\n-  Written in **Typescript**.\n\n---\n\n### \u003e\u003e\u003e [PLAYGROUND](https://glitched-writer.site) \u003c\u003c\u003c | \u003e\u003e\u003e [DEMOS](https://codepen.io/collection/XWVEEa) \u003c\u003c\u003c | \u003e\u003e\u003e [NPM](https://www.npmjs.com/package/glitched-writer) \u003c\u003c\u003c | \u003e\u003e\u003e [Vue component](https://www.npmjs.com/package/vue-glitched-writer) \u003c\u003c\u003c\n\n---\n\n## Table Of Contents\n\n1. **[Installation](#installation)**\n2. **[Usage](#usage)**\n   -  [Creating Class Instance](#creating-class-instance)\n   -  [Writing](#writing)\n   -  [Queue Writing](#queue-writing)\n   -  [Pausing \u0026 Playing](#pausing--playing)\n   -  [One-Time-Use](#one-time-use)\n   -  [On Text Input](#on-text-input)\n   -  [Callbacks | Events](#callbacks-|-events)\n   -  [Add \u0026 Remove](#add--remove)\n   -  [Writing HTML](#writing-html)\n   -  [Letterize](#letterize)\n   -  [Endless option](#endless-animation)\n   -  [Changing options after creation](#changing-options-post-initialization)\n   -  [CSS Tricks](#optimizing-css---preventing-layour-shifts)\n   -  [Available imports](#available-imports)\n3. **[Presets](#presets)**\n4. **[Options](#customizing-behavior)**\n   -  [Effect style](#stylistic-options)\n   -  [Control](#control-options)\n   -  [Generators](#generator-options)\n\n---\n\n## Installation\n\nDownload and install with npm.\n\n```bash\nnpm i glitched-writer\n```\n\n```js\nimport GlitchedWriter from 'glitched-writer'\n```\n\nOr use [Skypack](https://www.skypack.dev/view/glitched-writer) to import without need to install the package.\n\n```js\nimport GlitchedWriter from 'https://cdn.skypack.dev/glitched-writer'\n```\n\n## CDN\n\nYou can also attach script tag with src pointing to CDN, like [JsDelivr](https://www.jsdelivr.com/package/npm/glitched-writer).\n\n```html\n\u003cscript src=\"https://cdn.jsdelivr.net/npm/glitched-writer/lib/index.min.js\"\u003e\u003c/script\u003e\n```\n\nIn result, the **GlitchedWriter** object will be available in your code, this object contains all named exports, [listed here](#available-imports).\n\n```js\n// use create method to create new instance.\nconst writer = GlitchedWriter.create(Element, options, finishCB)\n```\n\n## Usage:\n\n### Creating Class Instance\n\nCreating writer class instance:\n\n```js\n// Calling GlitchedWriter constructor:\nconst writer = new GlitchedWriter(\n\thtmlElement, // Element / Selector string / undefined\n\toptions, // {...} / Preset name / undefined\n\tonFinishCallback, // (string, data) =\u003e {} / undefined\n)\n\n// Custom options:\nconst writer = new GlitchedWriter(htmlElement, {\n\tinterval: [10, 70],\n\toneAtATime: true,\n\tletterize: true,\n})\n\n// On-finish-callback added:\nconst writer = new GlitchedWriter(htmlElement, {}, (string, writerData) =\u003e {\n\tconsole.log(`Current string: ${string}`)\n\tconsole.log('All the class data:', writerData)\n})\n```\n\n### Writing\n\nWriting stuff and waiting with async / await.\n\n```js\nimport { wait } from 'glitched-writer'\n\n// Wrap this in some async function:\n// Or use .then() instead.\nconst res = await writer.write('Welcome')\n\nconsole.log(`Finished writing: ${res.string}`)\nconsole.log('All the writer data:', res)\n\nawait wait(1200) // additional simple promise to wait some time\n\nawait writer.write('...to Glitch City!')\n```\n\n### Queue Writing\n\nIf you have prepared array of texts to write - in a loop, or one time - you can pass them all to the .write() method and initiate a Queue.\n\n```js\nconst phrases = ['First, write this.', 'Then this.', 'And finally this!']\n\nwriter.queueWrite(phrases, 1000, true)\n\n/**\n * 1. @param texts\n * - string[] - Array of strings to write\n * - HTMLElement - the parent element with the paragraphs\n * - string - query selector pointing to that element\n * 2. @param queueInterval - Time to wait between writing each texts [ms]\n * 3. @param loop - boolean | Callback | number - What to do when the queue has ended.\n * - false -\u003e stop;\n * - true -\u003e continue looping;\n * - Callback -\u003e stop and fire the callback.\n * - number -\u003e wait number ms and than continue\n */\n```\n\n#### Texts from HTML (SEO Friendly)\n\nInstead of using the `string` array, you can place an `div` with your queue as `paragraphs` on the page. Then pass it to the queueWrite function as first param.\nThis allows bots and search engines, as well as users with JavaScript disabled, to see your text on the page.\n\n```html\n\u003cdiv id=\"phrases\" style=\"display: none;\"\u003e\n\t\u003cp\u003eWelcome!\u003c/p\u003e\n\t\u003cp\u003eto my \u003cb\u003eawesome\u003c/b\u003e website.\u003c/p\u003e\n\u003c/div\u003e\n\u003c!-- will read as: ['Welcome!', 'to my \u003cb\u003eawesome\u003c/b\u003e website.'] --\u003e\n```\n\n```js\nwriter.queueWrite('#phrases', queueInterval, loop)\n```\n\n### Pausing \u0026 Playing\n\nYou can pause and resume playing at any time.\n\n```js\nwriter.write('Some very cool header.').then(({ status, message }) =\u003e {\n\t// this will run when the writing stops.\n\tconsole.log(`${status}: ${message}`)\n})\n\nsetTimeout(\n\t() =\u003e writer.pause(), // will stop writing\n\t1000,\n)\n\nsetTimeout(async () =\u003e {\n\tconst { string } = await writer.play() // continue writing\n\tconsole.log('Completed:', string) // will log after finished writing\n}, 2000)\n```\n\n### One-Time-Use\n\nFor quick one-time writing.\n\n```js\nimport { write, queueWrite } from 'glitched-writer'\n\nwrite('Write this and BEGONE!', htmlElement, options, stepCB, finishCB)\n\nqueueWrite(texts, htmlElement, options, interval, loop, stepCB, finishCB)\n```\n\n### On Text Input\n\nDon't be afraid to call write method on top of each oder.\nNew will stop the ongoing. But, it's good to `debounce` the event handler.\n\n```js\nimport debounce from 'lodash.debounce'\n\nconst onInput = debounce(() =\u003e writer.write(inputEl.value), 500)\n\ninputEl.addEventListener('input', onInput)\n```\n\n### Callbacks | Events\n\n```js\n// your html element:\ntextHtmlElement.addEventListener('gw-finished', e =\u003e\n\tconsole.log('finished writing:', e.detail.string),\n)\n\n/**\n * Adding callbacks: writer.addCallback(type, callback)\n *\n * @param type \"start\" | \"step\" | \"finish\"\n * @param callback your callback function: (string, writerData) =\u003e {}\n */\n\nconst startCB = string =\u003e console.log('Started writing:', string)\n// add\nwriter.addCallback('start', startCB)\n// remove\nwriter.removeCallback('start', startCB)\n```\n\n### Add \u0026 Remove\n\n`.add(string)` \u0026 `.remove(number)` are methods usefull for quick changes to the previous text.\n\n```js\n// Let's say current text content is: \"Hello World\"\n\nwriter.add('!!!')\n// -\u003e Hello World!!!\n\nwriter.remove(9)\n// -\u003e Hello\n```\n\n### Writing HTML\n\n(**! Potentially dangerous !**) Let's you write text with html tags in it. Don't use on user-generated content.\n\n```js\n// You need to enable html option.\nconst writer = new GlitchedWriter(htmlElement, { html: true })\n\nwriter.write('\u003cb\u003eBe sure to click \u003ca href=\"...\"\u003ethis!\u003c/a\u003e\u003c/b\u003e')\n```\n\n### Letterize\n\nSplits written text into series of `\u003cspan\u003e` elements. Then writing letters seperately into these child-elements.\n\n```js\n// You need to enable html option.\nconst writer = new GlitchedWriter(htmlElement, { letterize: true })\n\nwriter.write('Hello there!')\n/**\n * The shape of one Char:\n * span.gw-char (+ .gw-finished when compleated | .gw-changed with each change)\n * \tspan.gw-ghosts\n * \tspan.gw-letter (+ .gw-glitched when is a glitched letter)\n * \tspan.gw-ghosts\n */\n```\n\n### Endless animation\n\nOption `endless` let's you run the text animation until you disable that function.\n\nThis opens the door for some additional effects, like: **Show on hover** (e.g. on secret fields) or **refreshing text** to give it user attention.\n\nHere is a [live example](https://codepen.io/thetarnav/pen/oNBLpxb).\n\n```js\n// SHOW ON HOVER\n// First make the password scramble forever\nwriter.endless(true)\nwriter.write('PASSWORD')\n\n// And disable endless option on hover\npassEl.addEventListener('mouseover', () =\u003e writer.endless(false))\n```\n\n### Changing options post initialization\n\nOptions can be changed in 2 ways after instance creation.\n\n```js\n// Extending current options\nwriter.options.extend({\n\thtml: true,\n\tmaxGhosts: 10,\n\t// the rest will stay the same\n})\n\n// Reseting options\nwriter.options.set({\n\tletterize: true,\n\toneAtATime: 4,\n\t// the rest will be set to default\n})\n```\n\n### Optimizing CSS - Preventing Layour Shifts\n\nChanging text rapidly can cause a lot of layout shift. These are few css tricks worth considering when using this package:\n\n```css\n/* 1. Warn the browser that the text content will be changing */\n.gw {\n   will-change: contents;\n}\n\n/* 2. It's good to make the element position absolute or fixed\n      so it wont influence the rest of the layout */\n.gw {\n   position: fixed;\n}\n\n/* 3. Make the width and height constant,\n      so it doesn't shift while writing */\n.gw {\n   width: 80ch;\n   height: 6rem;\n}\n\n/* 4. If you cannot limit the element size,\n      then you should warn browser that it will change */\n.gw {\n   will-change: contents, height, width;\n}\n```\n\n### Available imports\n\nList of all things that can be imported from `glitched-writer` module.\n\n```ts\nimport GlitchedWriter, { // \u003c-- GlitchedWriter class\n\tCustomOptions, // \u003c-- Options type\n\tCallback, // \u003c-- Callbacks type\n\tWriterDataResponse, // \u003c-- Type of a data response in callbacks\n\twrite, // \u003c-- One time, standalone write function\n\tqueueWrite, // \u003c-- Standalone queue write function\n\tpresets, // \u003c-- Object with all prepared presets of options\n\tglyphs, // \u003c-- Some glyph sets\n\twait, // \u003c-- Ulitity promise function, that can be used to wait some time\n\tcreate, // \u003c-- Function serving as an alternative way to create GlitchedWriter instance.\n} from 'glitched-writer'\n```\n\n---\n\n## Presets\n\nTo use one of the available presets, You can simply write it's name when creating writer, in the place of options.\nAvailable presets, as for now:\n\n-  **[default](https://codepen.io/thetarnav/pen/MWWyPzY)** - Loaded automatically, featured on the GIF up top.\n\n-  **[nier](https://codepen.io/thetarnav/pen/OJWNRor)** - Imitating the way text was appearing in the _NieR: Automata's UI_.\n\n-  **[typewriter](https://codepen.io/thetarnav/pen/qBRpQpQ)** - Simple but feels like being written by a human: one letter at a time, with erasing enabled by default.\n\n-  **[terminal](https://codepen.io/thetarnav/pen/mdRyqga)** - Similar to the typewriter preset but more \"robotic\". Characters flow smoothly (with stable interval), but with little \"stutering\" here and there.\n\n-  **zalgo** - Inspired by the _\"zalgo\"_ or _\"cursed text\"_, Ghost characters mostly includes the unicode combining characters, which makes the text glitch vertically. Requires high \"maxGhosts\" to look good.\n\n-  **[neo](https://codepen.io/thetarnav/pen/vYgYWbb)** - Recreated: _Justin Windle's [\"Text Scramble Effect\"](https://codepen.io/soulwire/pen/mErPAK)_\n\n-  **[encrypted](https://codepen.io/thetarnav/pen/oNBLpxb)** - Simple Text Scramble effect, suits well displaying secret data, like passwords or card numbers. And generally looks good for more \"casual\" usecases - where you don't want too much \"layout shifting\", caused by rapid characters number changing.\n\n-  **bitbybit** - Writes text word by word, in rhythmic but stattering manner.\n\n-  **[cosmic](https://codepen.io/thetarnav/pen/ExWgYer)** - Text slowly appears from and vanishes to the hollowness of space. Use with preserved sequences of white space _(white-space: pre-wrap;)_\n\n```js\nnew GlitchedWriter(htmlElement, 'terminal')\n```\n\n### Importing preset objects\n\nYou can import the option object of mentioned presets and tweak them, as well as some glyph sets.\n\n```js\nimport { presets, glyphs } from 'glitched-writer'\n\n// Extend preset for your needs:\nnew GlitchedWriter(htmlElement, {\n\t...presets.typewriter,\n\tletterize: true,\n})\n```\n\n---\n\n## Customizing behavior\n\nThere are many options you can tweak to customize the writting effect. Check out the [playground website](https://glitched-writer.site/) I've made, where you can test both presets and options.\n\n**Range** values will result in random values for each step for every letter.\n\n**Ghost** are \"glitched letters\" that gets rendered randomly in the time of writing, but aren't part of final string.\n\n### Stylistic Options:\n\n\u003e \\- options that set the visual effect.\n\n```ts\nsteps?: [number, number] | number, // [1, 8]\ninterval?: [number, number] | number, // [60, 170]\ndelay?: [number, number] | number, // [0, 2000]\nchangeChance?: number, // 0.6\nghostChance?: number, // 0.2\nmaxGhosts?: number, // '0.2'\noneAtATime?: boolean | number | 'word', // 0 | false\nglyphs?: string | string[] | Set\u003cstring\u003e, // glyphs.full + glyphs.zalgo\nglyphsFromText?: boolean, // false\nfillSpace?: boolean, // true\nmode?: 'normal' | 'matching' | 'erase' | 'clear', // 'matching'\n```\n\n-  **steps** - Number of **minimum** steps it takes one letter to reach it's goal one. Set to 0 if you want them to change to right letter in one step. (int)\n\n-  **interval** - Interval between each step, for every letter. (int: ms)\n\n-  **delay** - first delay each letter must wait before it starts working (int: ms)\n\n-  **changeChance** - Chance of letter being replaced by a glitched character (p: 0-1)\n\n-  **ghostChance** - Chance for ghost letter to appear (p: 0-1)\n\n-  **maxGhosts** - Maximal number of ghosts to occur\n\n   -  **int** - _(eg. 15) -\u003e this will be the limit._\n   -  **float** - _(eg. 0.25) -\u003e Limit = maxGhosts \\* goalString.length_\n\n-  **oneAtATime** - Without this option enabled, letters in your string will animate all at once. Enabling this option, by setting it to **true** or any **intiger larger than 0**, will cause the string to be written from letter by letter, left to right. Number value, signifies how many letters will be typed at once.\n\n   -  **\"word\"** - _now you can also set is to \"word\". Instead of writing letter by letter, or couple of letters, writer will divide goal text by words._\n\n-  **glyphs** - A set of characters that can appear as ghosts or letters can change into them\n\n-  **glyphsFromText** - If you want to add letters from written text to the glyph charset\n\n-  **fillSpace** - With this **enabled** if letter gets erased ny replacing with space - to keep the same \"width\" of previous string, and to make letters _\"disappear in space\"_. If **disabled**, every letter will \"stick\" to the rest. To make it more clear (hopefully), here is an example \"frame\" of writing: **\"Something farely long\"** -\u003e **\"Short String\"**.\n\n   -  false - \"XOSh8rt S3rinFv\"\n   -  true - \" X OSh8rt S3rinF v \"\n\n-  **mode** - Writing mode - decides on how to prepare the Char Table.\n\n   -  'matching' - _Will scan starting and goal string for matching characters and will try to build character map from that. Requires ghosts enabled (\u003e0) to take effect_\n   -  'normal' - _Wont do any matching, just converts starting string into character map._\n   -  'erase' - _First Erases entire string and then writes your text._\n   -  'erase_smart' - _Same as erase, but saves the matching begginging letters_\n   -  'clear' - _Instantly deletes entire textContent and then writes your text._\n\n### Control Options:\n\n\u003e \\- options that control writer behavior.\n\n```ts\nhtml?: boolean, // false\nletterize?: boolean, // false\nendless?: boolean // false\nfps?: number, // 60\n```\n\n-  **html** - _Potentially dangerous option._ If true, written string will be injected as html, not text content. It provides advanced text formating with html tags and more. But be sure to NOT enable it on user-provided content.\n\n-  **letterize** - Instead of injecting written text to \"textContent\" or \"innerHTML\", it appends every letter of that text as a child span element. Then changing textContent of that span to current letter. It gives a lot of styling possibilities, as you can style ghosts, letters, and whole chars seperately, depending on current writer and char state.\n\n-  **endless** - It will make the animation endless. _But why?_ Well, you can disable this option while the animation is running _( writer.endless(false) )_ and finish the animation when you want.\n\n-  **fps** - Animation loop is done using requestAnimationFrame, with fps you can controll the maximum framerate of writing animation. Only actually matters for high refresh monitors. _(! wont have an effect with letterize enabled !_)\n\n### Generator Options:\n\n\u003e \\- custom functions used to generate variables contextually, for use in writing.\n\n```ts\n/**\n * @param char - Char for which to generate value\n * @param base - default function generating that value\n */\n\n// generaing ghost/glitched char\ngenGlyph?: (char: Char, base: Function) =\u003e string\n\ngenDelay?: (char: Char, base: Function) =\u003e number // [ms]\n\ngenInterval?: (char: Char, base: Function) =\u003e number // [ms]\n```\n\n## The End - couple of final words\n\nThanks for checking out the Glitched Writer. Let me know, if you are using it somewhere - would love to see it working out there.\n\nIf you have any questions, just create new [discusion](https://github.com/thetarnav/glitched-writer/discussions) or [issue](https://github.com/thetarnav/glitched-writer/issues). Or just send me an email at gthetarnav@gmail.com, if you want.\n\nPresets or feature ideas are also welcome :)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthetarnav%2Fglitched-writer","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fthetarnav%2Fglitched-writer","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthetarnav%2Fglitched-writer/lists"}