{"id":15630590,"url":"https://github.com/afeld/magickly","last_synced_at":"2025-08-21T09:33:06.216Z","repository":{"id":64881406,"uuid":"1518764","full_name":"afeld/magickly","owner":"afeld","description":"image manipulation as a (plugin-able) service","archived":false,"fork":false,"pushed_at":"2016-02-01T07:24:25.000Z","size":584,"stargazers_count":179,"open_issues_count":14,"forks_count":35,"subscribers_count":5,"default_branch":"master","last_synced_at":"2025-06-03T18:52:00.972Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"http://magickly.afeld.me","language":"Ruby","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/afeld.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2011-03-23T23:34:06.000Z","updated_at":"2024-09-21T18:04:17.000Z","dependencies_parsed_at":"2022-12-18T17:30:50.297Z","dependency_job_id":null,"html_url":"https://github.com/afeld/magickly","commit_stats":null,"previous_names":[],"tags_count":11,"template":false,"template_full_name":null,"purl":"pkg:github/afeld/magickly","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/afeld%2Fmagickly","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/afeld%2Fmagickly/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/afeld%2Fmagickly/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/afeld%2Fmagickly/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/afeld","download_url":"https://codeload.github.com/afeld/magickly/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/afeld%2Fmagickly/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":271455685,"owners_count":24762774,"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-08-21T02:00:08.990Z","response_time":74,"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":[],"created_at":"2024-10-03T10:33:30.665Z","updated_at":"2025-08-21T09:33:05.922Z","avatar_url":"https://github.com/afeld.png","language":"Ruby","funding_links":[],"categories":["Imagery"],"sub_categories":[],"readme":"# ![Magickly - image manipulation as a (plugin-able) service](http://magickly.afeld.me/images/logo.jpg)\n\nBuilt as a practical wrapper of Imagemagick which handles caching, c/o the [Dragonfly](http://markevans.github.com/dragonfly/) gem.\n\nSay the base URL is the hosted version of this app, [magickly.afeld.me](http://magickly.afeld.me).  The image URL is appended to the query string as a `src=`, followed by any of the supported operations below.  Multiple operations can be combined, and will be applied in order.\n\nIf no query params are provided, a simple sandbox page is displayed.  Try it here:\n\n[magickly.afeld.me](http://magickly.afeld.me)\n\nMore insights:\n\n* [Blog post](https://aidan.jux.com/nerdery/310516) about how it's used at \u003ca href=\"https://jux.com\"\u003eJux\u003c/a\u003e\n* Talk: *Magickly, Mustachio, \u0026 Stateless APIs* ([slides](https://docs.google.com/presentation/d/1vuXqmMOssslx3nRxqWh1e3SgGN7_euYdVMIWm94DDPA/edit?usp=sharing))\n    * [12-minute overview version](https://www.youtube.com/watch?v=X3VdlBDCJSA) at [Code Genius](http://code.genius.com/)\n    * [49-minute in-depth version](http://www.hakkalabs.co/articles/anatomy-ruby-gem-magickly) at [Anatomy of a Ruby Gem](http://www.meetup.com/Anatomy-of-a-Ruby-Gem/events/163904202/)\n\n## Installation\n\n[Compatible](http://travis-ci.org/#!/afeld/magickly) with Ruby 1.9.3, 2.0 and 2.1, and jRuby (in 1.9 mode).  Requires Imagemagick \u003e= v6.2.4.\n\n    $ gem install magickly\n\n## Running the App\n\nA few options:\n\n### A. Run the app directly\n\n    # in the app directory:\n    $ gem install thin\n    $ thin start\n\nThe app can be accessed at [http://localhost:3000](http://localhost:3000).\n\nWhen running as an app, Rack::Cache is used to cache generated versions of images. These cached files and metadata are stored in the tmp/ directory and will get regenerated as necessary.\n\n### B. Use as an endpoint in another Rack app\n\nAs an example, to have magickly accessible at `/magickly` in a Rails app:\n\n    # Gemfile\n    gem 'magickly', '~\u003e 1.1'\n    \n    # config/routes.rb\n    match '/magickly', :to =\u003e Magickly::App, :anchor =\u003e false\n\nFor more info, see [Rails Routing from the Outside In](http://guides.rubyonrails.org/routing.html#routing-to-rack-applications) or Michael Raidel's [Mount Rails apps in Rails 3](http://inductor.induktiv.at/blog/2010/05/23/mount-rack-apps-in-rails-3/).\n\n## Processing Parameters\n\n*See the [Dragonfly documentation](http://markevans.github.com/dragonfly/file.ImageMagick.html) for more details about the permitted* `geometry` *values.*\n\n### src=*url* (required)\n\nThe URL of the original image.\n\n### brightness_contrast=*br.* x *con.*\n\n*brightness* and *contrast* are percentage change, between -100 and 100.  For example, to increase contrast by 20% but leave brightness unchanged, use `brightness_contrast=0x20`.\n\n![tanned imagemagick logo](http://magickly.afeld.me/?src=http://upload.wikimedia.org/wikipedia/commons/thumb/0/0d/Imagemagick-logo.png/200px-Imagemagick-logo.png\u0026brightness_contrast=-10x50)\n\n[http://magickly.afeld.me/?src=http://upload.wikimedia.org/wikipedia/commons/thumb/0/0d/Imagemagick-logo.png/200px-Imagemagick-logo.png\u0026brightness_contrast=-10x50](http://magickly.afeld.me/?src=http://upload.wikimedia.org/wikipedia/commons/thumb/0/0d/Imagemagick-logo.png/200px-Imagemagick-logo.png\u0026brightness_contrast=-10x50)\n\n### flip=true\n\n![flipped imagemagick logo](http://magickly.afeld.me/?src=http://upload.wikimedia.org/wikipedia/commons/thumb/0/0d/Imagemagick-logo.png/200px-Imagemagick-logo.png\u0026flip=true)\n\n[http://magickly.afeld.me/?src=http://upload.wikimedia.org/wikipedia/commons/thumb/0/0d/Imagemagick-logo.png/200px-Imagemagick-logo.png\u0026flip=true](http://magickly.afeld.me/?src=http://upload.wikimedia.org/wikipedia/commons/thumb/0/0d/Imagemagick-logo.png/200px-Imagemagick-logo.png\u0026flip=true)\n\n### flop=true\n\n![flopped imagemagick logo](http://magickly.afeld.me/?src=http://upload.wikimedia.org/wikipedia/commons/thumb/0/0d/Imagemagick-logo.png/200px-Imagemagick-logo.png\u0026flop=true)\n\n[http://magickly.afeld.me/?src=http://upload.wikimedia.org/wikipedia/commons/thumb/0/0d/Imagemagick-logo.png/200px-Imagemagick-logo.png\u0026flop=true](http://magickly.afeld.me/?src=http://upload.wikimedia.org/wikipedia/commons/thumb/0/0d/Imagemagick-logo.png/200px-Imagemagick-logo.png\u0026flop=true)\n\n### glow=*amount*,*softness*\n\nwhere `amount` is a float \u003e= 1.0, and `softness` is an int \u003e= 0.\n\n![glowing imagemagick logo](http://magickly.afeld.me/?src=http://upload.wikimedia.org/wikipedia/commons/thumb/0/0d/Imagemagick-logo.png/200px-Imagemagick-logo.png\u0026glow=1.2,20)\n\n[http://magickly.afeld.me/?src=http://upload.wikimedia.org/wikipedia/commons/thumb/0/0d/Imagemagick-logo.png/200px-Imagemagick-logo.png\u0026glow=1.2,20](http://magickly.afeld.me/?src=http://upload.wikimedia.org/wikipedia/commons/thumb/0/0d/Imagemagick-logo.png/200px-Imagemagick-logo.png\u0026glow=1.2,20)\n\n### greyscale=true\n\n![flopped imagemagick logo](http://magickly.afeld.me/?src=http://upload.wikimedia.org/wikipedia/commons/thumb/0/0d/Imagemagick-logo.png/200px-Imagemagick-logo.png\u0026greyscale=true)\n\n[http://magickly.afeld.me/?src=http://upload.wikimedia.org/wikipedia/commons/thumb/0/0d/Imagemagick-logo.png/200px-Imagemagick-logo.png\u0026greyscale=true](http://magickly.afeld.me/?src=http://upload.wikimedia.org/wikipedia/commons/thumb/0/0d/Imagemagick-logo.png/200px-Imagemagick-logo.png\u0026greyscale=true)\n\n### halftone=*threshold*\n\nwhere *threshold* is a value between 0 and 100.\n\n![halftone imagemagick logo](http://magickly.afeld.me/?src=http://upload.wikimedia.org/wikipedia/commons/thumb/0/0d/Imagemagick-logo.png/200px-Imagemagick-logo.png\u0026halftone=60)\n\n[http://magickly.afeld.me/?src=http://upload.wikimedia.org/wikipedia/commons/thumb/0/0d/Imagemagick-logo.png/200px-Imagemagick-logo.png\u0026halftone=60](http://magickly.afeld.me/?src=http://upload.wikimedia.org/wikipedia/commons/thumb/0/0d/Imagemagick-logo.png/200px-Imagemagick-logo.png\u0026halftone=60)\n\n### jcn=true\n\n![JCN imagemagick logo](http://magickly.afeld.me/?src=http://upload.wikimedia.org/wikipedia/commons/thumb/0/0d/Imagemagick-logo.png/200px-Imagemagick-logo.png\u0026jcn=true)\n\n[http://magickly.afeld.me/?src=http://upload.wikimedia.org/wikipedia/commons/thumb/0/0d/Imagemagick-logo.png/200px-Imagemagick-logo.png\u0026jcn=true](http://magickly.afeld.me/?src=http://upload.wikimedia.org/wikipedia/commons/thumb/0/0d/Imagemagick-logo.png/200px-Imagemagick-logo.png\u0026jcn=true)\n\n### resize=*geometry*\n\n![resized imagemagick logo](http://magickly.afeld.me/?src=http://upload.wikimedia.org/wikipedia/commons/thumb/0/0d/Imagemagick-logo.png/200px-Imagemagick-logo.png\u0026resize=100x100)\n\n[http://magickly.afeld.me/?src=http://upload.wikimedia.org/wikipedia/commons/thumb/0/0d/Imagemagick-logo.png/200px-Imagemagick-logo.png\u0026resize=100x100](http://magickly.afeld.me/?src=http://upload.wikimedia.org/wikipedia/commons/thumb/0/0d/Imagemagick-logo.png/200px-Imagemagick-logo.png\u0026resize=100x100)\n\n### rotate=*degrees*\n\n![rotated imagemagick logo](http://magickly.afeld.me/?src=http://upload.wikimedia.org/wikipedia/commons/thumb/0/0d/Imagemagick-logo.png/200px-Imagemagick-logo.png\u0026rotate=45)\n\n[http://magickly.afeld.me/?src=http://upload.wikimedia.org/wikipedia/commons/thumb/0/0d/Imagemagick-logo.png/200px-Imagemagick-logo.png\u0026rotate=45](http://magickly.afeld.me/?src=http://upload.wikimedia.org/wikipedia/commons/thumb/0/0d/Imagemagick-logo.png/200px-Imagemagick-logo.png\u0026rotate=45)\n\n### saturation=*percentage*\n\n*percentage* is the percentage of variation: a positive integer.  100 means no change.  For example, to increase saturation by 50%, use `saturation=150`.\n\n![saturated imagemagick logo](http://magickly.afeld.me/?src=http://upload.wikimedia.org/wikipedia/commons/thumb/0/0d/Imagemagick-logo.png/200px-Imagemagick-logo.png\u0026saturation=150)\n\n[http://magickly.afeld.me/?src=http://upload.wikimedia.org/wikipedia/commons/thumb/0/0d/Imagemagick-logo.png/200px-Imagemagick-logo.png\u0026saturation=150](http://magickly.afeld.me/?src=http://upload.wikimedia.org/wikipedia/commons/thumb/0/0d/Imagemagick-logo.png/200px-Imagemagick-logo.png\u0026saturation=150)\n\n### tilt_shift=true\n\n![tilt-shifted imagemagick logo](http://magickly.afeld.me/?src=http://upload.wikimedia.org/wikipedia/commons/thumb/0/0d/Imagemagick-logo.png/200px-Imagemagick-logo.png\u0026tilt_shift=true)\n\n[http://magickly.afeld.me/?src=http://upload.wikimedia.org/wikipedia/commons/thumb/0/0d/Imagemagick-logo.png/200px-Imagemagick-logo.png\u0026tilt_shift=true](http://magickly.afeld.me/?src=http://upload.wikimedia.org/wikipedia/commons/thumb/0/0d/Imagemagick-logo.png/200px-Imagemagick-logo.png\u0026tilt_shift=true)\n\n### thumb=*geometry*\n\n![thumbnail of imagemagick logo](http://magickly.afeld.me/?src=http://upload.wikimedia.org/wikipedia/commons/thumb/0/0d/Imagemagick-logo.png/200px-Imagemagick-logo.png\u0026thumb=200x100%23)\n\n[http://magickly.afeld.me/?src=http://upload.wikimedia.org/wikipedia/commons/thumb/0/0d/Imagemagick-logo.png/200px-Imagemagick-logo.png\u0026thumb=200x100%23](http://magickly.afeld.me/?src=http://upload.wikimedia.org/wikipedia/commons/thumb/0/0d/Imagemagick-logo.png/200px-Imagemagick-logo.png\u0026thumb=200x100%23)\n\n(note: the `%23` in the geometry string above is an encoded '`#`', which tells Dragonfly to fill the dimensions and crop)\n\n### two_color=true\n\n![two color imagemagick logo](http://magickly.afeld.me/?src=http://upload.wikimedia.org/wikipedia/commons/thumb/0/0d/Imagemagick-logo.png/200px-Imagemagick-logo.png\u0026two_color=true)\n\n[http://magickly.afeld.me/?src=http://upload.wikimedia.org/wikipedia/commons/thumb/0/0d/Imagemagick-logo.png/200px-Imagemagick-logo.png\u0026two_color=true](http://magickly.afeld.me/?src=http://upload.wikimedia.org/wikipedia/commons/thumb/0/0d/Imagemagick-logo.png/200px-Imagemagick-logo.png\u0026two_color=true)\n\n## Alternate Syntax\n\nSome CDNs are jerks and don't respect query params on resources (_ahem_ CLOUDFRONT _ahem_) when caching.  To use this syntax:\n\n* replace the question mark that starts the query string (`?`) with `q/`\n* replace the ampersands (`\u0026`) and equals signs (`=`) with forward slashes (`/`)\n* make sure the `src` is encoded - this can be done in Javascript with `encodeURIComponent()`\n\nTherefore, instead of\n\n\thttp://magickly.afeld.me/?src=http://upload.wikimedia.org/wikipedia/commons/thumb/0/0d/Imagemagick-logo.png/200px-Imagemagick-logo.png\u0026thumb=200x100\n\t\nthe new URL would be\n\n\thttp://magickly.afeld.me/q/src/http%3A%2F%2Fupload.wikimedia.org%2Fwikipedia%2Fcommons%2Fthumb%2F0%2F0d%2FImagemagick-logo.png%2F200px-Imagemagick-logo.png/thumb/200x100\n\n## Encoded Syntax\n\nSometimes, you just can't handle escaped entities in your URLs (Facebook's handling of OpenGraph tags, for example). In those cases, we provide a Base64 encoded syntax which is an extension of the path-based Alternate Syntax above. Once your path-based url is constructed (see \"Alternate Syntax\" above), simply pass everything after the `q/` through Base64.urlsafe_encode or the javascript btoa() function and send it to the `qe/` endpoint instead.\n\nTherefore, instead of\n\n\thttp://magickly.afeld.me/q/src/http%3A%2F%2Fupload.wikimedia.org%2Fwikipedia%2Fcommons%2Fthumb%2F0%2F0d%2FImagemagick-logo.png%2F200px-Imagemagick-logo.png/thumb/200x100\n\nthe new URL would be\n\n\thttp://magickly.afeld.me/qe/c3JjL2h0dHAlM0ElMkYlMkZ1cGxvYWQud2lraW1lZGlhLm9yZyUyRndpa2lwZWRpYSUyRmNvbW1vbnMlMkZ0aHVtYiUyRjAlMkYwZCUyRkltYWdlbWFnaWNrLWxvZ28ucG5nJTJGMjAwcHgtSW1hZ2VtYWdpY2stbG9nby5wbmcvdGh1bWIvMjAweDEwMA==\n\n## Analyzers\n\nMagickly v1.2.0 introduces the ability to retrieve image properties via a REST API.  For example, to retrieve the number of colors in the photo, visit:\n\n[magickly.afeld.me/analyze/number_of_colors?src=...](http://magickly.afeld.me/analyze/number_of_colors?src=http://upload.wikimedia.org/wikipedia/commons/0/0d/Imagemagick-logo.png)\n\nTo get the list of available analyzers, visit [magickly.afeld.me/analyze](http://magickly.afeld.me/analyze)\n\n## Customization\n\nIn addition to the available parameters listed above, custom \"shortcuts\" can be created to perform arbitrary imagemagick operations.  For example, to create a shortcut called `resize_with_blur`:\n\n    # somewhere in your app configuration, i.e. config/initializers/magickly.rb for a Rails 3 app\n    Magickly.dragonfly.configure do |c|\n      c.job :resize_with_blur do |size|\n        process :convert, \"-filter Gaussian -resize #{size}\"\n      end\n    end\n\nwhich can then be used with the query string `?src=...\u0026resize_with_blur=200x`.  Note that magickly will pass the value of the query param to the block as a single string.\n\nSee the [Dragonfly documentation](http://markevans.github.com/dragonfly/file.GeneralUsage.html#Shortcuts) for more info on \"shortcuts\", and the [shortcuts.rb](https://github.com/afeld/magickly/blob/master/lib/shortcuts.rb) file for examples.\n\n## Disclaimer\n\nThe hosted version of the app ([magickly.afeld.me](http://magickly.afeld.me)) is a single app instance intended for demonstration purposes - if you are going to be making a large number of API calls to it or would like to use it in production, please let us know :-)\n\n## See Also\n\n* [Thumbor](http://thumbor.org/)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fafeld%2Fmagickly","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fafeld%2Fmagickly","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fafeld%2Fmagickly/lists"}