{"id":15059545,"url":"https://github.com/evashort/chords","last_synced_at":"2025-04-10T05:31:12.441Z","repository":{"id":71877149,"uuid":"106947610","full_name":"evashort/chords","owner":"evashort","description":"Text-based chord progression editor","archived":false,"fork":false,"pushed_at":"2019-09-11T00:58:12.000Z","size":745,"stargazers_count":31,"open_issues_count":9,"forks_count":1,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-03-24T06:51:35.086Z","etag":null,"topics":["chord-progression","chord-visualization","chords","elm","elm-lang","elm-language","music-composition","music-theory","web-audio","web-audio-api"],"latest_commit_sha":null,"homepage":"http://evanshort.name/chords/","language":"Elm","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/evashort.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-10-14T17:20:03.000Z","updated_at":"2025-02-23T02:01:37.000Z","dependencies_parsed_at":"2023-09-12T04:16:34.783Z","dependency_job_id":null,"html_url":"https://github.com/evashort/chords","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/evashort%2Fchords","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/evashort%2Fchords/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/evashort%2Fchords/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/evashort%2Fchords/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/evashort","download_url":"https://codeload.github.com/evashort/chords/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248162982,"owners_count":21057850,"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":["chord-progression","chord-visualization","chords","elm","elm-lang","elm-language","music-composition","music-theory","web-audio","web-audio-api"],"created_at":"2024-09-24T22:45:21.135Z","updated_at":"2025-04-10T05:31:12.365Z","avatar_url":"https://github.com/evashort.png","language":"Elm","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Chords\nText-based chord progression editor\n\nType chord names in a textbox to create a sequence of play buttons that you\ncan use like an instrument.\n\nTry it in your browser: https://evashort.com/chords/\n\nchordsite.slack.com is the Slack channel for discussing this project.\n\n[Follow this link](https://join.slack.com/t/chordsite/shared_invite/enQtNDIwMTU5ODM5MzM0LWVkZDhhODllOGNjMDdmMzZjMDRmYTExNTY5YTg0NWRhZWViMTUyYWIxYmRiNmY3YTkyNjA3NjczZTFlMTZkMTI) for a Slack invite.\n\n## Current features\n- Chords in textbox are colored to match play buttons\n- Play chords as arpeggio, strum pattern, continuous pad, or basic strum\n- Queue up a second chord to play in the next measure\n- Select a key signature to transpose the entire chord progression\n- Save progressions in URL to bookmark or share them\n- Adjustable tempo\n- Playable circle of 5ths\n- 7th, 9th, and 13th chords\n- Table showing all chords in key\n- View recently played chords to capture improvised sequences\n- On-screen piano keyboard that displays the most recently played chord\n- Search for chords by selecting piano keys\n- Option to save settings in local storage\n- Tutorial for new users\n\n## Planned features\n- Option to input chords as roman numerals\n- Tap to set tempo\n- Syntax to set duration of each chord\n- Chord duration saved in \"Recently played\" view\n- Export progression as MIDI\n- Buttons to play entire line of progression\n- View different Greek modes in chord table\n- 7th chords displayed within circle of 5ths, with show/hide option\n\n## Possible features\n- More arpeggio styles\n- Mobile-friendly interface\n- Suggestions for next chord in progression\n- Support for selected slash chords\n- Show chords on musical staff\n- Show guitar chord diagrams\n- Somehow indicate which notes are in the current key signature\n- Allow focusing chords using arrow keys\n\n## Color choices\nThe chord colors basically follow this scheme:\n- blue = tonic\n- red = dominant\n- green = subdominant\n\nChords are arranged in rainbow order as you move up the major scale by thirds.\nThis way, triads that share two notes have neighboring colors.\n\nMajor and minor 7ths are colored by mixing the two triads they contain.\n\nDiminished triads and chords that contain them are dark with white text.\n\nThe colors were chosen to be maximally saturated and close to the same\nlightness in the\n[CIELAB color space](https://en.wikipedia.org/wiki/CIELAB_color_space).\nSince the red-green axis is missing for most people with color vision\ndeficiency, the lightness varies to compensate, with green being lightest and\nred being darkest. The lightness was chosen to contrast with the text as\nrequired by\n[WCAG 2.0](https://www.w3.org/TR/UNDERSTANDING-WCAG20/visual-audio-contrast-contrast.html).\n\n## Build steps\n1. Install Elm from http://elm-lang.org/\n\n1. From the project folder, run `elm make src/Main.elm --output main.js`\n\n1. Run `elm reactor`\n\n1. Go to the URL shown, most likely http://localhost:8000\n\n1. Click on `index.html`\n\n## IDE setup\n1. Install VS Code from https://code.visualstudio.com/\n\n1. Within VS Code, install the `elm` extension\n\n1. Install Node.js from https://nodejs.org/en/\n\n1. Use npm to install elm-live: https://github.com/architectcodes/elm-live\n\nIn [`.vscode/tasks.json`](https://github.com/evashort/chords/blob/master/.vscode/tasks.json), there is a build command to start elm-live from within VS Code. To run it, click *Run Build Task...* in the *Tasks* menu. You only have to do this once at the beginning of each session. Once elm-live is running, it will recompile the project every time you save a file. Your changes should appear immediately at the URL displayed by elm-live, most likely http://localhost:8000/\n\n## Implementation details\nOn the JS side, I use the Web Audio API to create a new chain of audio nodes\nfor each note that is played. A sawtooth oscillator feeds into a gain node\nand then a lowpass filter, both of which decrease over time. There is a loop\nthat disconnects the filter nodes so they can be garbage-collected.\n\nOn the Elm side, when you click a chord play button, I generate a list of\n`AudioChange` objects, such as `AddNote { t : Float, f : Float }` or\n`MuteAllNotes`. Then I encode them with a custom JSON encoder and send them\nthrough a port to JS. So the entire arpeggio is sent to the Web Audio API as\nsoon as you click the button. In order for Elm to schedule the notes at the\nright time, I had to write a native module called\n[`AudioTime`](https://github.com/evashort/chords/blob/master/src/AudioTime.elm)\nto access the audio clock.\n\nElm also sets \"alarms\", which are times when the UI needs to be updated. The\nloop in [`audio.js`](https://github.com/evashort/chords/blob/master/audio.js)\nnotifies Elm when an alarm has passed.\n\nIn order to programmatically replace text in the textarea without erasing the\nundo history, we secretly create a new textarea for every replacement. If the\ntextarea receives a ctrl+z key press and the text doesn't change within 5ms,\nwe assume that the user is trying to undo the programmatic change and we\nrestore the old textarea. Same goes for redo. In Chrome and Edge, pressing\nctrl+z can also affect the hidden textareas so we have to detect and\ncounteract that. This is all handled on the JS side in\n[`theater.js`](https://github.com/evashort/chords/blob/master/theater.js),\nwith an Elm interface in\n[`Theater.elm`](https://github.com/evashort/chords/blob/master/src/Theater.elm).\n\n## Design principles\n\n### Keep it flat flat flat\nRefactor code when it becomes hard to manage, not when it becomes repetitive.\nRefactoring for the sake of code re-use tends to introduce extra abstraction\nlayers which can hide opportunities for more meaningful refactors. The same\nis true of refactors that attempt to increase encapsulation.\n\n### Simple is better than pure\nIt's usually a bad idea to write extra code that smooths over an ugly hack for\nthe sake of \"correctness\". Expect new features to introduce rough edges to the\ncode base. If you let ugly code sit for a while, you'll often find a better\nsolution while working on a different feature.\n\n### Always be open to removing features\nThe most important refactors start by questioning the utility of a feature you\nalways thought was essential. This project was essentially dead for two months\nbecause the code got too complicated to maintain. I was able to bring it back\nby removing the ability to invert chords, a feature that had been baked into\nthe design from the beginning.\n\n### Accept no glitches\nAs users, we don't always notice how much mental energy we spend compensating\nfor interfaces that are slightly unpredictable. We develop a lot of\nunconscious behaviors like double-checking that a certain element has focus.\nThis program is meant to act like an extension of the user's brain as they\nreason about chords, so it's very important to fix any glitches that might sap\ntheir mental energy.\n\n### The UI should be normal\nUse standard controls like checkboxes, buttons, and drop-down menus. Don't\nstyle them with custom CSS. Yes, I know broke this rule by making fancy\nMac-inspired radio buttons. Each UI element should have an explicit text\ndescription that's always visible so users know what it does at a glance.\nDon't hide things to save space unless absolutely necessary.\n\n### Strong opinions loosely held\nKeep the UI simple by not overwhelming the user with choices. This means\nhaving an opinion about everything. If a user wants something to be\ncustomizable, see if you can accommodate their use case by changing your\nopinion instead.\n\n### Variable names\nPrefer single-word variable names that refer to concrete objects in the real\nworld. Use a thesaurus to spark your creativity but forego the long words.\nTake advantage of metaphors to name multiple related variables. For example, a\n1D list containing multiple rows of data separated with a delimiter is called\na `train`, and each row of data is called a `car`.\n\n### Fail loudly\nIt's not always practical to rely on Elm's guarantee of no runtime errors. For\nexample, you might let `y = modBy 12 x` and then assume that `y \u003c 12`. Or you\nadd an item to a list and then assume that the list is not empty. Make these\nassumptions explicit by calling `Debug.todo` if they fail and printing some\nuseful information.\n\n### Code style\nI don't have strong opinions about code style and formatting; I'm just doing\nwhat works for me. I'd like to start using elm-format but I'm afraid it will\nruin the commit history.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fevashort%2Fchords","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fevashort%2Fchords","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fevashort%2Fchords/lists"}