{"id":29829909,"url":"https://github.com/chrynan/chords","last_synced_at":"2025-07-29T09:42:02.536Z","repository":{"id":45239938,"uuid":"51400387","full_name":"chRyNaN/chords","owner":"chRyNaN","description":"A Kotlin multi-platform view library for displaying stringed instrument chord diagrams","archived":false,"fork":false,"pushed_at":"2021-12-30T03:13:24.000Z","size":2198,"stargazers_count":26,"open_issues_count":0,"forks_count":5,"subscribers_count":3,"default_branch":"develop","last_synced_at":"2023-02-27T06:41:52.146Z","etag":null,"topics":["android","android-library","chord-diagram","chords","guitar","guitar-chart","guitar-chords","guitar-tabs","kotlin","kotlin-library","kotlin-multi-platform","kotlin-multiplatform","kotlin-multiplatform-library","music","music-composition","tabs","view"],"latest_commit_sha":null,"homepage":"","language":"Kotlin","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/chRyNaN.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}},"created_at":"2016-02-09T21:14:06.000Z","updated_at":"2023-02-20T13:34:40.000Z","dependencies_parsed_at":"2022-09-10T00:12:20.386Z","dependency_job_id":null,"html_url":"https://github.com/chRyNaN/chords","commit_stats":null,"previous_names":[],"tags_count":null,"template":null,"template_full_name":null,"purl":"pkg:github/chRyNaN/chords","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chRyNaN%2Fchords","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chRyNaN%2Fchords/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chRyNaN%2Fchords/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chRyNaN%2Fchords/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/chRyNaN","download_url":"https://codeload.github.com/chRyNaN/chords/tar.gz/refs/heads/develop","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chRyNaN%2Fchords/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":267665545,"owners_count":24124556,"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","status":"online","status_checked_at":"2025-07-29T02:00:12.549Z","response_time":2574,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":["android","android-library","chord-diagram","chords","guitar","guitar-chart","guitar-chords","guitar-tabs","kotlin","kotlin-library","kotlin-multi-platform","kotlin-multiplatform","kotlin-multiplatform-library","music","music-composition","tabs","view"],"created_at":"2025-07-29T09:41:35.133Z","updated_at":"2025-07-29T09:42:02.527Z","avatar_url":"https://github.com/chRyNaN.png","language":"Kotlin","funding_links":[],"categories":[],"sub_categories":[],"readme":"# chords\n\nAn easily customizable Kotlin multi-platform View to display guitar (and other stringed instrument) chords. Simple to\nuse and beautifully designed.\n\n**Current Version:** ![GitHub tag (latest by date)](https://img.shields.io/github/v/tag/chrynan/chords)\n\n\u003cimg alt=\"Sample Screenshot\" src=\"https://github.com/chRyNaN/chords/blob/master/sample-android/screenshots/device-2020-01-18-180759.png\" width=\"300\"\u003e\u003c/img\u003e\n\u003cimg alt=\"Sample Screenshot\" src=\"https://github.com/chRyNaN/chords/blob/master/sample-android/screenshots/device-2020-01-18-183742.png\" width=\"300\"\u003e\u003c/img\u003e\n\nThis library has been updated significantly from its original version and the process is detailed in\nthis [blog post](https://chrynan.codes/converting-an-old-java-library-to-kotlin/) which was featured in Android Weekly\nissue #398.\n\u003ca href=\"https://androidweekly.net/issues/issue-398\" title=\"Android Weekly Issue 398\"\u003e\n\u003cimg alt=\"Badge\" src=\"https://androidweekly.net/issues/issue-398/badge\" height=\"20px\"\u003e\u003c/img\u003e\n\u003c/a\u003e\n\n## Building the library\n\nThe library is provided through [Repsy.io](https://repsy.io). Checkout\nthe [releases page](https://github.com/chRyNaN/chords/releases) to get the latest version. \u003cbr /\u003e\n\u003cimg alt=\"GitHub tag (latest by date)\" src=\"https://img.shields.io/github/v/tag/chRyNaN/chords\"\u003e\n\n### Repository\n\n```groovy\nrepositories {\n    maven { url = uri(\"https://repo.repsy.io/mvn/chrynan/public\") }\n}\n```\n\n### Dependencies\n\n**core:**\n\n```kotlin\nimplementation(\"com.chrynan.chords:chords-core:VERSION\")\n```\n\n**compose:**\n\n```kotlin\nimplementation(\"com.chrynan.chords:chords-compose:VERSION\")\n```\n\n## Usage\n\nThere are a few main components to using the library:\n\n* `ChordWidget` is the `ChordView` implementation that displays the chord.\n* `ChordChart` is a class that represents information about the chord chart that will be displayed.\n* `Chord` is a class that represents the markers on a chord that will be displayed.\n\n### Creating an instance of `ChordWidget`\n\n**Android:**\n\n```xml\n\u003c!-- Specify an exact size (MATCH_PARENT, MATCH_CONSTRAINTS, DP value). --\u003e\n\u003ccom.chrynan.chords.widget.ChordWidget\n        android:id=\"@+id/chordWidget\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"/\u003e\n```\n\n**Kotlin JS:**\n\n```kotlin\nval widget = ChordWidget(htmlCanvas)\n```\n\n**Jetpack Compose:**\n\n```kotlin\nChordWidget(\n    modifier = Modifier.size(width = 200.dp, height = 200.dp),\n    chord = chord,\n    chart = chart\n)\n```\n\n### Assigning a `ChordChart` to a `ChordWidget`\n\n```kotlin\nchordWidget?.chart = ChordChart(\n    fretStart = FretNumber(1),\n    fretEnd = FretNumber(2),\n    stringCount = 6,\n    stringLabels = setOf(\n        StringLabel(string = StringNumber(1), label = \"e\"),\n        StringLabel(string = StringNumber(2), label = \"B\"),\n        StringLabel(string = StringNumber(3), label = \"G\"),\n        StringLabel(string = StringNumber(4), label = \"D\"),\n        StringLabel(string = StringNumber(5), label = \"A\"),\n        StringLabel(string = StringNumber(6), label = \"E\")\n    )\n)\n```\n\n### Creating a Chord using the DSL\n\n```kotlin\nval chord = chord(\"G\") {\n    +ChordMarker.Note(\n        fret = FretNumber(3),\n        finger = Finger.MIDDLE,\n        string = StringNumber(6)\n    )\n    +ChordMarker.Note(\n        fret = FretNumber(2),\n        finger = Finger.INDEX,\n        string = StringNumber(5)\n    )\n    +ChordMarker.Open(string = StringNumber(4))\n    +ChordMarker.Open(string = StringNumber(3))\n    +ChordMarker.Note(\n        fret = FretNumber(3),\n        finger = Finger.RING,\n        string = StringNumber(2)\n    )\n    +ChordMarker.Note(\n        fret = FretNumber(3),\n        finger = Finger.PINKY,\n        string = StringNumber(1)\n    )\n}\n```\n\n### Assigning a `Chord` to a `ChordWidget`\n\n```kotlin\nchordWidget?.chord = chord\n```\n\n**Note:** This library doesn't try to coerce values to fit into a chart or exclude values that exceed the chart bounds.\nIf the `ChordChart` and `Chord` have inconsistent values, the `ChordWidget` may look peculiar. It's important for the\nuser of the library to properly handle coordinating the different components.\n\n### Parsing Chords from other formats\n\nThe `ChordParser` interface takes in an input type and outputs a `ChordParseResult`. This interface can be implemented\nfor different format input types. There are a couple provided `ChordParser` implementations.\n\n**AsciiChordParser:**\n\n`AsciiChordParser` parses a `String` input of an ASCII Chord Diagram and outputs a `ChordParseResult` containing\na `Chord`.\n\n```kotlin\nval chordDiagram = \"\"\"\n            C\n    e |-----0------|\n    B |-----1------|\n    G |-----0------|\n    D |-----2------|\n    A |-----3------|\n    E |------------|\n\"\"\".trimIndent()\n\nval parser = AsciiChordParser()\n\nlaunch {\n    // parse() is a suspending function and needs to be called from another suspending\n    // function or a coroutine\n    val result = parser.parse(chordDiagram)\n\n    val chord: Chord = result.chord\n    val stringLabels: Set\u003cStringLabel\u003e = result.stringLabels\n    val baseFret: FretNumber? = result.baseFret\n}\n```\n\n**ChordProParser:**\n\n`ChordProParser` parses a `String` input of a Chord Pro (Chord or Define) Directive and outputs a `ChordParseResult`\ncontaining a `Chord`.\n\n```kotlin\nval chordDiagram = \"{define: Bes base-fret 1 frets 1 1 3 3 3 1 fingers 1 1 2 3 4 1}\"\n\nval parser = ChordProParser()\n\nlaunch {\n    // parse() is a suspending function and needs to be called from another suspending\n    // function or a coroutine\n    val result = parser.parse(chordDiagram)\n\n    val chord: Chord = result.chord\n    val stringLabels: Set\u003cStringLabel\u003e = result.stringLabels\n    val baseFret: FretNumber? = result.baseFret\n}\n```\n\n### Customizing the appearance\n\n`ChordWidget` implements the `ChordView` interface which contains properties to alter the appearance of the view.\n\n**ChordView:**\n\n```kotlin\ninterface ChordView {\n\n    ...\n\n    var fitToHeight: Boolean\n\n    var showFretNumbers: Boolean\n\n    var showFingerNumbers: Boolean\n\n    var stringLabelState: StringLabelState\n\n    var mutedStringText: String\n\n    var openStringText: String\n\n    var fretColor: ColorInt\n\n    var fretLabelTextColor: ColorInt\n\n    var stringColor: ColorInt\n\n    var stringLabelTextColor: ColorInt\n\n    var noteColor: ColorInt\n\n    var noteLabelTextColor: ColorInt\n\n    ...\n}\n```\n\n**Updating properties directly on ChordWidget:**\n\n```kotlin\nchordWidget?.noteColor = Color.BLUE\nchordWidget?.openStringText = \"o\"\n```\n\n**Note:** That in Kotlin JS, you have to explicitly call the `render()` function after updating properties on\nthe `ChordWidget`.\n\n**Updating properties using a ViewModel and Binder:**\n\n```kotlin\nval binder = ChordViewBinder(chordWidget)\n\nval viewModel = ChordViewModel(\n    fretColor = Color.BLACK,\n    fretLabelTextColor = Color.WHITE,\n    stringLabelTextColor = Color.BLACK,\n    stringColor = Color.BLACK,\n    noteColor = Color.BLACK,\n    noteLabelTextColor = Color.WHITE\n)\n\nbinder.bind(viewModel)\n```\n\n**Note:** That in Kotlin JS, there is a convenience function `ChordViewBinder.bindAndRender()` which will bind the\nproperties from the `ChordViewModel` to the `ChordWidget` and call `render()`.\n\n**Updating properties in Android XML:**\n\n```xml\n\n\u003ccom.chrynan.chords.widget.ChordWidget\n        android:id=\"@+id/chordWidget\"\n        android:layout_width=\"200dp\"\n        android:layout_height=\"300dp\"\n        app:stringLabelState=\"label\"\n        app:showFretNumbers=\"false\"/\u003e\n```\n\n**Available Android XML Attributes:**\n\n```xml\n\n\u003cattr name=\"fretColor\" format=\"color\"/\u003e\n\u003cattr name=\"fretLabelTextColor\" format=\"color\"/\u003e\n\u003cattr name=\"stringColor\" format=\"color\"/\u003e\n\u003cattr name=\"stringLabelTextColor\" format=\"color\"/\u003e\n\u003cattr name=\"noteColor\" format=\"color\"/\u003e\n\u003cattr name=\"noteLabelTextColor\" format=\"color\"/\u003e\n\u003cattr name=\"mutedStringText\" format=\"string\"/\u003e\n\u003cattr name=\"openStringText\" format=\"string\"/\u003e\n\u003cattr name=\"showFingerNumbers\" format=\"boolean\"/\u003e\n\u003cattr name=\"showFretNumbers\" format=\"boolean\"/\u003e\n\u003cattr name=\"stringLabelState\" format=\"enum\"\u003e\n\u003cenum name=\"number\" value=\"0\"/\u003e\n\u003cenum name=\"label\" value=\"1\"/\u003e\n\u003cenum name=\"hide\" value=\"2\"/\u003e\n\u003c/attr\u003e\n\u003cattr name=\"fitToHeight\" format=\"boolean\"/\u003e\n\u003cattr name=\"fretLabelTypeface\" format=\"reference\"/\u003e\n\u003cattr name=\"noteLabelTypeface\" format=\"reference\"/\u003e\n\u003cattr name=\"stringLabelTypeface\" format=\"reference\"/\u003e\n```\n\n### Selectable Chord names in Text using Android Spans\n\nThe library comes with a `ChordSpan` which allows the pairing of text with a `Chord`. And when the `ChordSpan` is\nselected, a listener is alerted with the `Chord`.\n\n**Adding a `ChordSpan` to a `TextView`:**\n\n```kotlin\nval text = SpannableString(\"G\")\nval span = ChordSpan(chord, this) // \"this\" refers to the listener\n\ntext.setSpan(span, 0, 1, Spannable.SPAN_INCLUSIVE_EXCLUSIVE)\n\ntextView?.text = text\n// Need to specify LinkTouchMovementMethod as the movement method for clicks to work\ntextView?.movementMethod = LinkTouchMovementMethod()\n```\n\n**Using DSL functions to add a `ChordSpan` to a `TextView`:**\n\n```kotlin\nval textBuilder = buildSpannableString {\n    +chordSpan(chord, listener)\n\n    +chordSpan(\"G\", chord) {\n        // Handle click event\n    }\n\n    +styledChordSpan(chord, listener) {\n        this.backgroundColor = Color.BLUE\n    }\n}\n\ntextView?.text = textBuilder\n// Need to specify LinkTouchMovementMethod as the movement method for clicks to work\ntextView?.movementMethod = LinkTouchMovementMethod()\n```\n\n**Listening to `Chord` selected events:**\n\n```kotlin\nclass MainActivity : AppCompatActivity(),\n    ChordSpan.ChordSelectedListener {\n\n    override fun onChordSpanSelected(chord: Chord) {\n        // Perform action with chord\n    }\n}\n```\n\n**Customizing the `ChordSpan` appearance:**\n\n`ChordSpan` extends from `TouchableSpan` which inherits from `TouchableSpanView` and has the following customizable\nproperties:\n\n```kotlin\nvar backgroundColor = Color.TRANSPARENT\nvar selectedBackgroundColor = Color.TRANSPARENT\nvar textColor = Color.BLUE\nvar selectedTextColor = Color.BLUE\nvar isUnderlined = false\nvar isUnderlinedWhenSelected = false\nvar textTypeface = Typeface.DEFAULT\nvar selectedTextTypeface = Typeface.DEFAULT\n```\n\nThese properties can be changed on the span:\n\n```kotlin\nspan.textColor = Color.RED\n```\n\n### Passing Chords between Android components\n\nThe model classes are not `Parcelable` because they are in a Kotlin multi-platform module and don't have access to\nAndroid Framework classes. But the Android library module does have wrapper classes that handle the serialization and\nde-serialization of the `Chord` and `ChordChart` models.\n\nThese classes are `ParcelableChordWrapper` and `ParcelableChartWrapper`. To pass `Chord` and `ChordChart` between\ncomponents, such as, in a Bundle, just wrap them with their respective wrapper models.\n\n```kotlin\narguments =\n    Bundle().apply {\n        putParcelable(KEY_CHORD, ParcelableChordWrapper(chord))\n        putParcelable(KEY_CHART, ParcelableChartWrapper(chart))\n    }\n```\n\nThen retrieve the wrappers just as you would with any other Parcelable object.\n\n```kotlin\narguments?.getParcelable\u003cParcelableChordWrapper\u003e(KEY_CHORD)\narguments?.getParcelable\u003cParcelableChartWrapper\u003e(KEY_CHART)\n```\n\nFor convenience, there are extension functions on the `Bundle` and `Intent` objects which handle the wrapping and\nunwrapping of the `Chord` and `ChordChart` objects.\n\n**Bundle:**\n\n```kotlin\narguments =\n    Bundle().apply {\n        putChord(KEY_CHORD, chord)\n        putChordChart(KEY_CHART, chart)\n    }\n\nval chord = arguments?.getChord(KEY_CHORD)\nval chart = arguments?.getChordChart(KEY_CHART)\n```\n\n**Intent:**\n\n```kotlin\nintent.putChord(KEY_CHORD, chord)\nintent.putChordChart(KEY_CHART, chart)\n\nval chord = intent.getChordExtra(KEY_CHORD)\nval chart = intent.getChordChartExtra(KEY_CHART)\n```\n\nThere is also a convenience `ChordAndChart` class (with similar Parcelable extension functions) for when both a `Chord`\nand a `ChordChart` need to be passed between Android components together.\n\n```kotlin\nval chordAndChart = ChordAndChart(chord = chord, chart = chordChart)\n\n// Bundle\narguments = bundle.putChordAndChart(KEY_CHORD_AND_CHART, chordAndChart)\narguments?.getChordAndChart(KEY_CHORD_AND_CHART)\n\n// Intent\nintent.putChordAndChartExtra(KEY_CHORD_AND_CHART, chordAndChart)\nintent.getChordAndChartExtra(KEY_CHORD_AND_CHART)\n```\n\n### Samples\n\nCheckout the sample modules for examples on using the library.\n\n## License\n\n```\nCopyright 2020 chRyNaN\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n   http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchrynan%2Fchords","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fchrynan%2Fchords","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchrynan%2Fchords/lists"}