{"id":13433502,"url":"https://github.com/softboydev/tram","last_synced_at":"2025-03-17T12:31:56.964Z","repository":{"id":135389162,"uuid":"230322333","full_name":"softboydev/tram","owner":"softboydev","description":"\"tram\" is short for \"typographic rythm automation machine\" and is a typographic midi sequencer","archived":false,"fork":false,"pushed_at":"2024-05-02T10:30:43.000Z","size":16300,"stargazers_count":21,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"master","last_synced_at":"2024-10-28T16:12:39.025Z","etag":null,"topics":["electron","electronic-music","midi","midi-sequencer","music","sequencer","techno","typography"],"latest_commit_sha":null,"homepage":"https://softboydev.itch.io/tram","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"cc0-1.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/softboydev.png","metadata":{"files":{"readme":"readme.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","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":"2019-12-26T20:09:34.000Z","updated_at":"2024-08-10T12:56:58.000Z","dependencies_parsed_at":"2024-01-21T23:03:00.111Z","dependency_job_id":"fdec5d31-095e-4cba-88a3-500b5a60b0b0","html_url":"https://github.com/softboydev/tram","commit_stats":null,"previous_names":["softboydev/tram"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/softboydev%2Ftram","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/softboydev%2Ftram/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/softboydev%2Ftram/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/softboydev%2Ftram/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/softboydev","download_url":"https://codeload.github.com/softboydev/tram/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":244033862,"owners_count":20387014,"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":["electron","electronic-music","midi","midi-sequencer","music","sequencer","techno","typography"],"created_at":"2024-07-31T02:01:27.406Z","updated_at":"2025-03-17T12:31:52.946Z","avatar_url":"https://github.com/softboydev.png","language":"JavaScript","funding_links":[],"categories":["JavaScript"],"sub_categories":[],"readme":"![banner](./img/banner.gif)\n\n# TRAM\n\nTRAM is short for Typographic Rythm Automation Machine and is a experimental MIDI sequencer. Map a midi message to a symbol, map a set of symbols to another symbol, combine symbols into bars, combine bars into beats. TRAM features a minimalistic UI, autosave and immediate code evaluation so it's perfect for livecoding performances.\n\n![New TRAM 1](./img/screenshot-1.jpg)\n\n*Wide UI*\n\n![New TRAM 2](./img/screenshot-2.jpg)\n\n*Narrow UI*\n\nTRAM got it's name from a typographic sample based drumcomputer I wrote in 2019. I had the idea while taking the  tram. This how the original looked like:\n\n![Old Tram 1](./img/preview-1.jpg)\n\n![Old Tram 2](./img/preview-2.jpg)\n\n## Installation\n\nThere are three ways to use TRAM. Either install a build for your platform from itch.io: [softboydev.itch.io/tram](https://softboydev.itch.io/tram)\n\nOr run it within electron.js\n\n```\ngit clone https://github.com/softboydev/tram.git\ncd tram/app\nnpm install\nnpm start\n```\n\nOr build a build of your choice with electron forge\n\n```\ngit clone https://github.com/softboydev/tram.git\ncd tram/app\nnpm install\nnpm run build_osx\n```\n\nYou can run any of these build commands\n\n```\nbuild_osx -\u003e Mac OS\nbuild_win -\u003e Windows\nbuild_all -\u003e Builds for both platforms\n```\n\n## Usage\n\nEvery line you enter into the main interface will either be interpreted as a mapping or as a bar.\n\nYou can open and save files from the menu or using shortcuts. The current state of the editor is automatically saved whenever you change anything so there is no need to backup anything.\n\nYou can use *Cmd/Strg+C*, *Cmd/Strg+V*, *Cmd/Strg+A*and *Cmd/Strg+Z* in the editor.\n\nYou can change the filename by clicking between the two square brackets in the bottom center of the UI. The new filename will be used when you save the next time.\n\nYou can change the fontsize from the menu or using shortcuts.\n\nUsing *Window/Help* or *Cmd/Strg+?* will display a super short help popup. You need to close it the same way.\n\n### Mapping\n\nMapping is one of the two key features. With mapping I am referring to the action of connecting a character or word to either a set of other characters or a MIDI message.\n\nMapping can be done either in the main window or in the dedicated mapping window. The terminal window allows no mapping.\n\nThe way a character or word is mapped is very simple. Every line that contains at least two words (a single character is treated as a word as well) is eligible for mapping. The first word (seperated by the space) is what the command is mapped to and the second word is the command. All other words in the line are treated normally and the two first words are ignored for anything but the mapping.\n\nYou can map letters, numbers and special characters to MIDI\n\n```\nK 128:36:100\n3 129:39:127\n+ 130:45:60\n```\n\nYou can also map whole words to MIDI\n\n```\nKICK 128:36:100\nSNARE 129:39:127\nCLAP 130:45:60\n```\n\nYou can map a word or character to a set of characters (not a set of words though!)\n\n```\n/ KKKK\nBEAT KSKS\n```\n\nYou can map characters to words and then a word to a set words\n\n```\nA KICK\nB SNARE\nC CLAP\nBEAT ABCBCABC\n```\n\nThe mapping needs to follow a certain convention. The second word can be split by any of these separators:\n\n- empty space\n- commata\n- dot\n- minus\n- vertical line\n- double point\n\nThe first part will be the midi message, the second and third part become the data bytes (see this table for reference: https://www.midi.org/specifications-old/item/table-2-expanded-messages-list-status-bytes).\n\nYou may use shorthands. By typing a number between 1 and 16 as the midi message you will send a note on command on the correspoding channel. You can also type notes and octaves instead of a number in the first data byte. These two midi messages are equivalent:\n\n```\n128:40:127\n1:d4:127\n```\n\nNote off commands are sent automatically whenever you send a note on command. The note off command is sent right before the note on command. You can send additional note off commands manually, for example when you want a note to be held short then until the next note.\n\nYou can find some default mappings in the `/defaults` folder of the git repo.\n\n#### Words and Characters\n\nOne of the interesting aspects of TRAM is how any combination of character can be mapped to any command. But this can also lead to a lot of confusion.\n\nLet's say your drumcomputer has the kick on the first MIDI channel. In that case the following string would send kick with a pitch of C3 and velocity of 100\n\n```\n1:36:100\n```\n\nNow if you would want a kick to sound whenever you type the letter K you would write\n\n```\nK 1:36:100\n```\n\nThe space after the letter maps the MIDI command to the letter. When you now type out KKK you would get a four to the floor pattern. Now lets say you would want to save that pattern to a new operator called TECHNO, then you would type\n\n```\nK 1:36:100\nTECHNO KKKK\n```\n\nIf you now type TECHNO you would hear a four the floor pattern. But what if you would also map the character T to a snare like this?\n\n```\nK 1:36:100\nTECHNO KKKK\nT 2:36:100\n```\n\nIn that case the previous mapping of TECHNO would overwrite the mapping of T whenever the full word is spelled out, but as soon as you either type only T or any word containing the letter T that is not mapped you would instead hear the snare.\n\nThis creates a kick snare requireUpdateAfterMapping\n\n```\nK 1:36:100\nTECHNO KKKK\nT 2:36:100\nKTKT\n```\n\nWhile this would create a polyrythmic snare pattern layered over the four to the floor pattern\n\n```\nK 1:36:100\nTECHNO KKKK\nT 2:36:100\nTECHNO\nTECHN\n```\n\nHowever once you type a word containing the subword TECHNO you would instead get a shifted kickpattern\n\n```\nK 1:36:100\nTECHNO KKKK\nT 2:36:100\nTECHNOLOGY\n```\n\nNow what happens when we start typing full sentences like TECHNO IS COOL?\n\n```\nK 1:36:100\nTECHNO KKKK\nT 2:36:100\nTECHNO IS COOL\n```\n\nTECHNO is being interpreted as four kicks while IS COOL has no mapping so its just left out. However we could map COOL to a snare pattern like this\n\n```\nK 1:36:100\nTECHNO KKKK\nT 2:36:100\nTECHNO IS COOL\nCOOL TTT\n```\n\nNotice how nothing changes? That is because new mappings are added line by line. We have to map COOL before we use it, like this\n\n```\nK 1:36:100\nTECHNO KKKK\nT 2:36:100\nCOOL TTT\nTECHNO IS COOL\n```\n\nNow what happens if we replace TECHNO with HOUSE? Remember: when a line contains a space the first two words are treated as a mapping, regardless of how many words are in the line. TECHNO IS COOL did work because TECHNO was already mapped, so the first two words in the line were not interpreted as a mapping. HOUSE is not mapped yet. So what will HOUSE be mapped to?\n\n```\nK 1:36:100\nTECHNO KKKK\nT 2:36:100\nCOOL TTT\nHOUSE IS COOL\n```\n\nRight now you can only hear the snare pattern from COOL. Because HOUSE is now mapped to IS. When we give IS a mapping, HOUSE will be mapped to the mapping of IS. So lets make IS five kicks like so\n\n```\nK 1:36:100\nTECHNO KKKK\nT 2:36:100\nCOOL TTT\nIS KKK\nHOUSE IS COOL\n```\n\nNow why do we still hear the same? That is because we have only mapped HOUSE to IS. We havent used HOUSE yet. But when we type an extra line using HOUSE we will get a new pattern (because HOUSE is now mapped therefore the new line containing it is not interpreted as a mapping anymore)\n\n```\nK 1:36:100\nTECHNO KKKK\nT 2:36:100\nCOOL TTT\nIS KKK\nHOUSE IS COOL\nHOUSE IS LIFE\n```\n\nNow you can hear the kicks from HOUSE and from IS. When we replace IS by WAS we only hear the 5 five kicks from HOUSE\n\n```\nK 1:36:100\nTECHNO KKKK\nT 2:36:100\nCOOL TTT\nIS KKK\nHOUSE IS COOL\nHOUSE WAS LIFE\n```\n\nNow what happens when a word is not mapped to anything but parts of it are? Lets give F and E a sound like so (assuming clave and clap are on channel 3 and 4)\n\n```\nK 1:36:100\nF 3:36:100\nE 4:36:100\nTECHNO KKKK\nT 2:36:100\nCOOL TTT\nIS KKK\nHOUSE IS COOL\nHOUSE WAS LIFE\n```\n\nNotice how the pattern changed as previously ignored characters now have a mapping? Lets clear all other lines that are not mapping and just write HOUSE IS LIFE (we just remove the COOL)\n\n```\nK 1:36:100\nF 3:36:100\nE 4:36:100\nTECHNO KKKK\nT 2:36:100\nCOOL TTT\nIS KKKKK\nHOUSE IS\nHOUSE WAS LIFE\n```\n\nRight now you can hear one clave and one clap. If we now map LI to something as well the pattern changes\n\n```\nK 1:36:100\nF 3:36:100\nE 4:36:100\nTECHNO KKKK\nT 2:36:100\nCOOL TTT\nIS KKKKK\nLI FFE\nHOUSE IS\nHOUSE WAS LIFE\n```\n\nNow LIFE becomes a complex clap and clave pattern due to LI being mapped to a pattern and F and E being mapped to a specific MIDI note. But what happens if we now also map the whole word as well like this?\n\n```\nK 1:36:100\nF 3:36:100\nE 4:36:100\nTECHNO KKKK\nT 2:36:100\nCOOL TTT\nIS KKKKK\nLI FFE\nHOUSE IS\nLIFE IS\nLIFE\n```\n\nWe now hear five kicks even though LI and F and E are mapped before the whole word was mapped. That is because mappings happen in order of the lines, so they are only available in the lines after. But once they have happened mappings are sorted by their length. Which means: we check if LIFE has been mapped before we check if any of its parts have been mapped.\n\n#### Recursion\n\nOne final thing. You can map words to themselves, lets call it recursive mapping. To not crash your computer I have decided to limit recursion to a depth of 10. What does recursion do exactly? Image this:\n\n```\nA 1:36:100\nB 2:36:100\nC 4:36:100\nAB\n```\n\nA is a kick and B is a snare. AB will result in a kick and snare pattern with the kick on the 1 and snare on the 3. If we now map AB to something else instead (like C) and type it out in another line we only hear the clap, because the mapping of the word AB overwrites the individual mappings of the characters A and B\n\n```\nA 1:36:100\nB 2:36:100\nC 4:36:100\nAB C\nAB\n```\n\nBut see what happens if instead map the word AB a word that contains the word AB again?\n\n```\nA 1:36:100\nB 2:36:100\nC 4:36:100\nAB ABC\nAB\n```\n\nWe hear a lot of claps suddenly. That is because internally every occurence of AB is replaced with ABC. But we dont do only one round. After a round of replacements has happened we do another one and again replace every occurence of AB with ABC. This place an additional C in the line with every round. You hear 10 extra claps because recursion is limited to 10. But you can go wild with this concept by creating complex mapping networks\n\n```\nA 1:36:100\nB 2:36:100\nC 3:36:100\nD 4:36:100\nAB ABC\nBC ADB\nADB ABCD\nAB\n```\n\nNow only typing out AB will result in an insanely complex pattern.\n\n### Programming\n\nTRAM will try to interprete every line that is not a mapping as a bar. The amount of symbols in the line determine the length of the bar. Bars are resolved to 16th notes so when a line contains 16 symbols (or words) it will result in a bar that is actually a bar long (4 quarter notes). Bars are looped regardless of their length, so it's very easy to build polyrythms.\n\nIf a line contains more then 16 commands the line is interpreted as more then one bar. One additional bar is added for each full set of 16 commands.\n\nUnmapped characters are treated as a pause, so can use unmapped characters to shift steps around by extending the pattern length without adding additional commands.\n\nThis will result in a clap on the 1\n\n```\nC\n```\n\nThis will result in a snare on the 1 and the 3\n\n```\nC\nSS\n```\n\nThis will result in a four to the floor pattern with a snare on every second kick\n\n```\nC\nSS\nKKKK\n```\n\nThis will add 16th notes hihats\n\n```\nC\nSS\nKKKK\nHHHHHHHHHHHHHHHH\n```\n\nNow if we want the clap to be on the last kick in the pattern instead of the first we simply add 3 unmapped characters before it\n\n```\n---C\nSS\nKKKK\nHHHHHHHHHHHHHHHH\n```\n\nLets add an open hat on the upbeat\n\n```\n---C\nSS\nKKKK\nHHHHHHHHHHHHHHHH\n-O-O-O-O\n```\n\nNow lets break out from the rigid pattern by adding a lo-tom, a hi-tom and a rimshot pattern\n\n```\n---C\nSS\nKKKK\nHHHHHHHHHHHHHHHH\n-O-O-O-O\nLLL\nTTTTT\nR-RR-RR\n```\n\nYou can comment out lines using the `#` symbol right at the start of the line. So lets prepare the pattern for the drop\n\n```\n---C\nSS\n#KKKK\nHHHHHHHHHHHHHHHH\n-O-O-O-O\nLLL\nTTTTT\nR-RR-RR\n```\n\n### Midi\n\nTRAM sends Midi data to a single device and will also only recieve Midi data from this device. You can look for Midi devices with *Midi/Refresh* from the menu or using *Cmd/Strg+M* as shortcut. The list of all midi devices will be displayed in the bottom left corner of the UI. You can use the functions from the *Midi* menu or the shortcuts *Cmd/Strg+Shift+M* and *Cmd/Strg+Shift+N* to switch between devices.\n\nTRAM can send and recieve Midi clock and Midi transport messages. The clock can be switch between 24 and 48ppq and between and internal and an external clock. You can toggle these from the *Midi* menu. Please note: clock recieval is *very* wonky right now, I am sorry.\n\nYou can change the tempo and control the transport from the *Transport* menu or using shortcuts.\n\n### Windows\n\nThe main window allows you too map, program and set the settings from a UI. However sometimes, especially when you have a lot of complex mappings that you dont need to interact with ofter, you might want to unclutter your main window. For that I have added the *Mapping Window*. Open it from the menu `Window \u003e Open Mapping Window` or use the shortcut `Cmd+M`. The content of the mapping window is only interpreted as mappings, never as commands, so you can type here without disturbing a running pattern.\n\nFor when you want to type freely without having to worry about accidentally overwriting a mapping or when you just want people to type in whatever I have added a *Terminal Mode*. It is displayed (on the big screen) in the top center, otherwise its available from the menu `Window \u003e Set Terminl Mode To`. These are the three modes available\n\n- `Terminal:All` - Allows mapping of MIDI and words from the editor\n- `Terminal:Words` - Only allows mapping of words but not of MIDI (so `BEAT ABAB` is valid while `A 1:C3:100` is not)\n- `Terminal:None` - Allows no mappings at all\n\nThe mappings done in the Mapping window are not affected by this setting. Lines that would have been mapped in another are treated as regular inputs when they are not mapped (in the mode `Terminal:Words` the line `A 1:C3:100` would map the `A` character to the word or character combination consisting of `1`,`:`,`C`,`3` and `0`)\n\n## Releases\n\nBelow you can find release notes on all major releases that had a dedicated build available. Look into the devlog over at [softboydev.itch.io/tram](https://softboydev.itch.io/tram) for more details.\n\n- **0.1.0** Release of the initial alpha version\n- **0.1.1** Fixed a bug that would make switching between MIDI devices impossible\n- **0.2.0** Switched to the original quanitsation again\n- **0.3.0** Improved pattern generation, new mapping window, new terminal mode and word interpretation\n  - You can now set the editor to different modes that only allows editing of the pattern, no new mappings are possible\n  - You can now open a mapping window where you can create mappings that are not visible in the main window and also dont interfere with your playback pattern\n  - TRAM is now capable of mapping whole words and sentences instead of only single characters. TRAM will even find words contained within other words, so you can map TECHNO to a four to the floor pattern and then write out TECHNOLOGY\n\n## Known Issues\n\n- Currently there is no Linux package available\n- Many keyboard shortcuts do not work on Windows\n\n## Future Plans\n\n- Fixing known issues\n- Adding a settings window to set colors and defaults\n- Adding OSC capabilites\n- Improving MIDI implementation, especially clock recieval\n- Adding an optional graphic interface for the mapping window\n- Adding performance features such as block comments and timed comments that become uncommented after a certain number of bars\n\n## Participation\n\nIf you find any bugs feel free to send me an email or open an issue. Also feel free to send more default mapping to me or add them as a pull request.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsoftboydev%2Ftram","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsoftboydev%2Ftram","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsoftboydev%2Ftram/lists"}