{"id":16684546,"url":"https://github.com/saoudrizwan/piano","last_synced_at":"2025-09-01T20:09:47.030Z","repository":{"id":45693308,"uuid":"103162618","full_name":"saoudrizwan/Piano","owner":"saoudrizwan","description":"Easily play combinations of sound effects and Taptic Engine vibrations on iOS.","archived":false,"fork":false,"pushed_at":"2024-08-20T21:19:31.000Z","size":957,"stargazers_count":242,"open_issues_count":1,"forks_count":21,"subscribers_count":7,"default_branch":"master","last_synced_at":"2025-06-20T23:03:12.762Z","etag":null,"topics":["apple","ios","iphone","swift","taptic-engine","vibration"],"latest_commit_sha":null,"homepage":"","language":"Swift","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/saoudrizwan.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-09-11T16:50:58.000Z","updated_at":"2025-06-05T06:42:24.000Z","dependencies_parsed_at":"2024-10-25T18:33:46.051Z","dependency_job_id":"95ed11fd-d863-4da5-989f-1605fc000830","html_url":"https://github.com/saoudrizwan/Piano","commit_stats":{"total_commits":70,"total_committers":4,"mean_commits":17.5,"dds":"0.19999999999999996","last_synced_commit":"c508659fea5ad6ff29d55aa267600af0222e45f3"},"previous_names":[],"tags_count":11,"template":false,"template_full_name":null,"purl":"pkg:github/saoudrizwan/Piano","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/saoudrizwan%2FPiano","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/saoudrizwan%2FPiano/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/saoudrizwan%2FPiano/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/saoudrizwan%2FPiano/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/saoudrizwan","download_url":"https://codeload.github.com/saoudrizwan/Piano/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/saoudrizwan%2FPiano/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":273183228,"owners_count":25059812,"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-09-01T02:00:09.058Z","response_time":120,"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":["apple","ios","iphone","swift","taptic-engine","vibration"],"created_at":"2024-10-12T14:44:13.962Z","updated_at":"2025-09-01T20:09:46.992Z","avatar_url":"https://github.com/saoudrizwan.png","language":"Swift","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n    \u003cimg src=\"https://user-images.githubusercontent.com/7799382/30356431-dbba9920-97ed-11e7-8f2b-a5b5ba0e7682.png\" alt=\"Piano\" /\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n    \u003cimg src=\"https://user-images.githubusercontent.com/7799382/30309920-bcdb85ec-9742-11e7-96fc-af8155f4712d.png\" alt=\"Platform: iOS 10.0+\" /\u003e\n    \u003ca href=\"https://developer.apple.com/swift\" target=\"_blank\"\u003e\u003cimg src=\"https://user-images.githubusercontent.com/7799382/30309908-ace5d886-9742-11e7-85ea-8d4e5f2af2ac.png\" alt=\"Language: Swift 4\" /\u003e\u003c/a\u003e\n    \u003ca href=\"https://cocoapods.org/pods/Piano\" target=\"_blank\"\u003e\u003cimg src=\"https://user-images.githubusercontent.com/7799382/33073452-cd78293e-ce77-11e7-8b39-8a1565616814.png\" alt=\"CocoaPods compatible\" /\u003e\u003c/a\u003e\n    \u003ca href=\"https://github.com/Carthage/Carthage\" target=\"_blank\"\u003e\u003cimg src=\"https://user-images.githubusercontent.com/7799382/30309900-9fc15d2e-9742-11e7-91fd-31bb1226db90.png\" alt=\"Carthage compatible\" /\u003e\u003c/a\u003e\n    \u003cimg src=\"https://user-images.githubusercontent.com/7799382/30309910-adef2b38-9742-11e7-8140-d05534dd92a5.png\" alt=\"License: MIT\" /\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n    \u003ca href=\"#installation\"\u003eInstallation\u003c/a\u003e\n  • \u003ca href=\"#usage\"\u003eUsage\u003c/a\u003e\n  • \u003ca href=\"#documentation\"\u003eDocumentation\u003c/a\u003e\n  • \u003ca href=\"#why-i-built-piano\"\u003eWhy I Built Piano\u003c/a\u003e\n  • \u003ca href=\"#license\"\u003eLicense\u003c/a\u003e\n  • \u003ca href=\"#contribute\"\u003eContribute\u003c/a\u003e\n  • \u003ca href=\"#questions\"\u003eQuestions?\u003c/a\u003e\n  • \u003ca href=\"#credits\"\u003eCredits\u003c/a\u003e\n\u003c/p\u003e\n\nPiano is a **convenient** and **easy-to-use** wrapper around the `AVFoundation` and `UIHapticFeedback` frameworks, leveraging the full capabilities of the **Taptic Engine**, while following strict Apple guidelines to **preserve battery life**. Ultimately, Piano allows you, the composer, to conduct masterful symphonies of sounds and vibrations, and create a more immersive, usable and meaningful user experience in your app or game.\n\n\n## Compatibility\n\nPiano requires **iOS 10+** and is compatible with **Swift 4.2** projects.\n\n## Installation\n\n* Installation for \u003ca href=\"https://guides.cocoapods.org/using/using-cocoapods.html\" target=\"_blank\"\u003eCocoaPods\u003c/a\u003e:\n\n```ruby\nplatform :ios, '10.0'\ntarget 'ProjectName' do\nuse_frameworks!\n\n    pod 'Piano', '~\u003e 1.8'\n\nend\n```\n*(if you run into problems, `pod repo update` and try again)*\n\n* Installation for \u003ca href=\"https://github.com/Carthage/Carthage\" target=\"_blank\"\u003eCarthage\u003c/a\u003e:\n\n ```ruby\n github \"saoudrizwan/Piano\"\n ```\n *(make sure Xcode 10 is [set as your system's default Xcode](https://stackoverflow.com/a/28901378/3502608) before using CocoaPods or Carthage with Swift 4 frameworks)*\n\n* Or embed the Piano framework into your project\n\nAnd `import Piano` in the files you'd like to use it.\n\n## Usage\n\nUsing Piano is simple.\n```swift\nlet symphony: [Piano.Note] = [\n    .sound(.asset(name: \"acapella\")),\n    .hapticFeedback(.impact(.light)),\n    .waitUntilFinished,\n    .hapticFeedback(.impact(.heavy)),\n    .wait(0.2),\n    .sound(.system(.chooChoo))\n]\n\nPiano.play(symphony)\n```\n... or better yet:\n```swift\n🎹.play([\n    .sound(.asset(name: \"acapella\"))\n    ])\n```\nOptionally add a completion block to be called when all the notes are finished playing:\n```swift\n🎹.play([\n    .sound(.asset(name: \"acapella\"))\n]) {\n    // ...\n}\n```\nOr cancel the currently playing symphony:\n```swift\n🎹.cancel()\n```\n\nIn the background, each note has an internal completion block, so you can add a `.waitUntilFinished` note that tells Piano to not play the next note until the previous note is done playing. This is useful for creating patterns of custom haptic feedback, besides the ones Apple predefined. This is also great for creating complex combinations of sound effects and vibrations.\n\n### Notes\n\n#### `.sound(Audio)`\nPlays an audio file.\n\n|Audio | |\n|------------ | ------------- |\n|`.asset(name: String)` | Name of asset in any .xcassets catalogs. It's recommended to add your sound files to Asset Catalogs instead of as standalone files to your main bundle.|\n|`.file(name: String, extension: String)` | Retrieves a file from the main bundle. For example a file named `Beep.wav` would be accessed with `.file(name: \"Beep\", extension: \"wav\")`.|\n|`.url(URL)` | This only works for file URLs, not network URLs.|\n|`.system(SystemSound)` | Predefined system sounds in every iPhone. [See all available options here](https://github.com/saoudrizwan/Piano/blob/master/Sources/SystemSound.swift). |\n\n#### `.vibration(Vibration)`\nPlays standard vibrations available on all models of the iPhone.\n\n|Vibration | |\n|------------ | -------------|\n|`.default`  | Basic 1-second vibration |\n|`.alert`  | Two short consecutive vibrations |\n\n#### `.tapticEngine(TapticEngine)`\nPlays Taptic Engine vibrations available on the iPhone 6S and above.\n\n|TapticEngine | |\n| ------------ | ------------- |\n|`.peek` | One weak boom |\n|`.pop` | One strong boom |\n|`.cancelled` | Three sequential weak booms |\n|`.tryAgain` | One weak boom then one strong boom |\n|`.failed` | Three sequential strong booms |\n\n#### `.hapticFeedback(HapticFeedback)`\nPlays Taptic Engine Haptic Feedback available on the iPhone 7 and above.\n\n|HapticFeedback | | |\n|------------ | ------------- |------------- |\n|`.notification(Notification)` | **Notification** | Communicate that a task or action has succeeded, failed, or produced a warning of some kind. |\n| | `.success` | Indicates that a task or action has completed successfully. |\n| | `.warning` | Indicates that a task or action has produced a warning. |\n| | `.failure` | Indicates that a task or action has failed. |\n|`.impact(Impact)`  | **Impact** | Indicates that an impact has occurred. For example, you might trigger impact feedback when a user interface object collides with something or snaps into place. |\n| | `.light` | Provides a physical metaphor representing a collision between small, light user interface elements.|\n| | `.medium` | Provides a physical metaphor representing a collision between moderately sized user interface elements.|\n| | `.heavy` | Provides a physical metaphor representing a collision between large, heavy user interface elements.|\n|`.selection` | | Indicates that the selection is actively changing. For example, the user feels light taps while scrolling a picker wheel.|\n\n\u003csub\u003eSee: [Apple's Guidelines for using Haptic Feedback](https://developer.apple.com/ios/human-interface-guidelines/user-interaction/feedback/)\u003c/sub\u003e\n\n#### `.waitUntilFinished`\nTells Piano to wait until the previous note is done playing before playing the next note.\n\n#### `.wait(TimeInterval)`\nTells Piano to wait a given duration before playing the next note.\n\n### Device Capabilities\n\n* The iPhone 6S and 6S Plus carry the first generation of Taptic Engine which has a few \"haptic\" vibration patterns, which you can play with Piano using the `.tapticEngine()` notes.\n\n* The iPhone 7 and above carry the latest version of the Taptic Engine which supports the iOS 10 Haptic Feedback frameworks, allowing you to select from many more vibration types. You can play these vibrations using the `.hapticFeedback()` notes.\n\n* All versions of the iPhone can play the `.vibration()` notes.\n\nPiano also includes a useful extension for `UIDevice` to check if the user's device has a Taptic Engine and if it supports Haptic Feedback. This extension is especially useful for creating symphonies for all devices:\n```swift\nif UIDevice.current.hasHapticFeedback {\n    // use .hapticFeedback(HapticFeedback) notes\n} else if UIDevice.current.hasTapticEngine {\n    // use .tapticEngine(TapticEngine) notes\n} else {\n    // use .vibration(Vibration) notes\n}\n```\n**Note:** This extension does not work on simulators, it will always return false.\n\n### Taptic Engine Guide\n\nApple's [guide over the Haptic Feedback framework](https://developer.apple.com/documentation/uikit/uifeedbackgenerator) is very clear about using the Taptic Engine appropriately in order to prevent draining the user's device's battery life. Piano was built with this in mind, and handles most cases as efficiently as possible. But you can help preserve battery life and reduce latency further by calling these helper methods based on your specific needs.\n\n#### 1. Wake up the Taptic Engine\n```swift\nPiano.wakeTapticEngine()\n```\nThis initializes and allocates the Haptic Feedback framework and essentially \"wakes up\" the Taptic Engine, as it is normally in an idle state. A good place to put this is at the begin state of a gesture or action, in anticipation of playing a `.hapticFeedback()` note.\n\n#### 2. Prepare the Taptic Engine\n\n```swift\nPiano.prepareTapticEngine()\n```\nThis tells the Taptic Engine to prepare itself before creating any feedback to reduce latency when triggering feedback.\n\nFrom Apple's [documentation](https://developer.apple.com/documentation/uikit/uifeedbackgenerator):\n\u003e This is particularly important when trying to match feedback to sound or visual cues. To preserve power, the Taptic Engine stays in this prepared state for only a short period of time (on the order of seconds), or until you next trigger feedback. Think about when and where you can best prepare your generators. If you call prepare and then immediately trigger feedback, the system won’t have enough time to get the Taptic Engine into the prepared state, and you may not see a reduction in latency. On the other hand, if you call prepare too early, the Taptic Engine may become idle again before you trigger feedback.\n\ntl;dr A good place to put this is right after calling `.wakeTapticEngine()`, usually at the beginning of a gesture or action, in anticipation of playing a `.hapticFeedback()` note.\n\n#### 3. Put the Taptic Engine back to Sleep\n```swift\nPiano.putTapticEngineToSleep()\n```\nOnce we know we're done using the Taptic Engine, we can deallocate the Haptic Feedback framework, returning the Taptic Engine to its idle state. A good place to put this is at the end of a finished, cancelled, or failed gesture or action.\n\n#### But you don't have to.\nPiano automatically wakes and prepares the Taptic Engine when you call `.play([ ... ])` if it includes a `.hapticFeedback()` note, and returns the Taptic Engine back to sleep when the notes are done playing.\n\n### The Example App\n\nThe [example app](https://github.com/saoudrizwan/Piano/tree/master/Example) is a great place to get started. It's designed as a playground for you to compose and test out your own symphonies of sounds and vibrations.\n\n\u003cp align=\"center\"\u003e\n\u003cimg src=\"https://user-images.githubusercontent.com/7799382/30370416-613f985a-982c-11e7-8646-33f1efb55d90.png\" alt=\"Piano\" width=\"300\" height=\"500\" /\u003e\n\u003c/p\u003e\n\nYou can even drag and drop your own sound files into the project and tweak the code a bit to see how your own sounds can work alongside the Taptic Engine. To add your own sound file, simply drag it into `Sounds.xcassets`, name it accordingly, then edit the `cellData` property in `ViewController.swift` (Scroll down to `case 7` in `cellData`, or look for \"Add your own sound assets here...\" in the Jump Bar using `Ctrl + 6`).\n\n## Documentation\nOption + click on any of Piano's methods or notes for detailed documentation.\n\u003cimg src=\"https://user-images.githubusercontent.com/7799382/30358465-97784ee0-97f9-11e7-9f12-75fa041cf556.png\" alt=\"documentation\"\u003e\n\n\n## Why I Built Piano\n\nWith the new iPhone 8 and iPhone X, we are going to see many new Augmented Reality apps, and one of the keypoints in the [Human Interface Guidelines for AR](https://developer.apple.com/ios/human-interface-guidelines/technologies/augmented-reality/) is to not clutter the AR view, allowing as much content from the augmented reality to be displayed as possible. Besides AR, Apple has spent tremendous time and manpower giving the iPhone an interface beyond our vision with the Taptic Engine and Siri. Apple even had a [session during WWDC 2017](https://developer.apple.com/videos/play/wwdc2017/803/) talking about the importance of sound design and the impact it can have on a user experience. It's obvious that the future of technology is not visual interfaces, but augmenting our connection with the real world. By using our physical, auditory, and most importantly visual senses, we can see the world in a whole new light. That's why I built Piano and [ARLogger](https://github.com/saoudrizwan/ARLogger), frameworks I hope will help developers create immersive and uncluttered interfaces, while keeping the user aware of the technology's state and purpose. If you'd like my help on an AR project, or just want to chat about the future of technology, don't hesitate to reach out to me on Twitter [@sdrzn](http://twitter.com/sdrzn).\n\n## License\n\nPiano uses the MIT license. Please file an issue if you have any questions or if you'd like to share how you're using Piano.\n\n## Contribute\n\nPlease feel free to create issues for feature requests or send pull requests of any additions you think would complement Piano and its philosophy.\n\n## Questions?\n\nContact me by email \u003ca href=\"mailto:hello@saoudmr.com\"\u003ehello@saoudmr.com\u003c/a\u003e, or by twitter \u003ca href=\"https://twitter.com/sdrzn\" target=\"_blank\"\u003e@sdrzn\u003c/a\u003e. Please create an \u003ca href=\"https://github.com/saoudrizwan/Piano/issues\"\u003eissue\u003c/a\u003e if you come across a bug or would like a feature to be added.\n\n## Credits\n\n* Example app sound files from [Icons 8 UI Sounds](https://icons8.com/sounds)\n* Music notes in README header image from [LSE Design on the Noun Project](https://thenounproject.com/LSEdesigns/collection/music-notes/)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsaoudrizwan%2Fpiano","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsaoudrizwan%2Fpiano","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsaoudrizwan%2Fpiano/lists"}