<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/"><channel><title>Development – IMG.LY Blog</title><description>Posts tagged Development on the IMG.LY blog.</description><link>https://img.ly/blog/tag/development/</link><language>en-us</language><image><url>https://img.ly/apple-touch-icon.png</url><title>Development – IMG.LY Blog</title><link>https://img.ly/blog/tag/development/</link></image><atom:link href="https://img.ly/blog/tag/development/rss.xml" rel="self" type="application/rss+xml"/><generator>Astro</generator><lastBuildDate>Fri, 19 Jun 2026 11:26:06 GMT</lastBuildDate><ttl>60</ttl><item><title>Cutting Through The Jungle: An In-depth Review of Cloud GPU Providers to Train Your AI Models in 2024</title><link>https://img.ly/blog/reviewing-cloud-gpu-providers-for-training-ai-models/</link><guid isPermaLink="true">https://img.ly/blog/reviewing-cloud-gpu-providers-for-training-ai-models/</guid><description>Embark with us on a journey to finding the best place to host AI models.</description><pubDate>Mon, 22 Apr 2024 06:35:54 GMT</pubDate><content:encoded>&lt;h2 id=&quot;navigating-the-world-of-ai-models-hosting&quot;&gt;Navigating the World of AI Models Hosting&lt;/h2&gt;
&lt;p&gt;Here at IMG.LY, we recently dug into finding the best place to host AI models to support apps we’re dreaming up. We wanted to figure out if using cloud GPUs or going serverless would work better for us. As we were looking specifically for service providers to run Image Generation Workloads on, we focused on those that could be the best fit for that. Along the way, we picked up some cool insights and ran into a few hiccups. We think sharing our journey and the things we figured out could help you when you’re looking to deploy your own AI models.&lt;/p&gt;
&lt;p&gt;First off, we’ll explain what cloud GPU and serverless hosting really mean. Then, we’ll chat about their good and not-so-good sides when it comes to hosting AI models. It’s super important to make sure whatever hosting you choose fits your model like a glove. We’ll talk about some tools we stumbled upon that could help with that. Next up, we’ll give you a peek at some of the providers we checked out and our thoughts on how they might fit with what we’re working on. We decided to skip over the big names like IBM, Google, and Amazon this time. We were curious about what the newer, smaller companies have to offer.&lt;/p&gt;
&lt;p&gt;To wrap things up, we’ll share some final thoughts on all our research. Plus, we’ll throw in some tips and ideas you might want to think about when you’re doing your own digging. Whether you’re developing AI models or planning to host some of the well-known ones, we hope our adventure helps you nail down the perfect hosting solution for what you need. Ready to jump in?&lt;/p&gt;
&lt;h2 id=&quot;kinds-of-cloud-hosting-for-ai-models&quot;&gt;Kinds of Cloud Hosting for AI Models&lt;/h2&gt;
&lt;p&gt;Cloud hosting has been around for as long as there has been a cloud. Though the server hardware is not at your location, earlier versions of cloud hosting required that your team learnt lots about server infrastructure. As things have evolved, providers now manage the infrastructure so that you can focus on your work. You can now host even just a single function in the cloud, if that’s what you need. In our research, we looked at general serverless hosting and at Cloud GPU AI providers.&lt;/p&gt;
&lt;h3 id=&quot;serverless-hosting&quot;&gt;Serverless Hosting&lt;/h3&gt;
&lt;p&gt;Serverless hosting can be defined as an architecture model that lets developers build and run applications and services without managing the servers they run on. The cloud provider manages things like security, provisioning, scaling, and connectivity.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;Diagram of serverless app services all running together on general hardware.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 478px) 478px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;478&quot; height=&quot;390&quot; src=&quot;https://img.ly/_astro/serverless2_9SKyE.webp&quot; srcset=&quot;/_astro/serverless2_9SKyE.webp 478w&quot;&gt;&lt;br&gt;
In a serverless CPU-loads hosting the host provisions your services to the most appropriate and available hardware. However, with most of the providers of GPU loads you get to choose.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Serverless Pros:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Pay-per-compute model: you only pay for the compute time you consume.&lt;/li&gt;
&lt;li&gt;Autoscaling: the provider will automatically scale up or down depending on load, from a few requests a day to thousands per second.&lt;/li&gt;
&lt;li&gt;No server management: eliminates the need for developers to &lt;em&gt;also&lt;/em&gt; understand server infrastructure. Often, just a Docker image holding an application is sufficient.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Serverless Cons:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Cold starts: instance deallocates after a certain idle time (enabling the great pay-per-compute model) so initial request after this can be noticeably slow.&lt;/li&gt;
&lt;li&gt;Limited control over specifics: certain GPU hardware or even server hardware may be unavailable at times which can impact performance.&lt;/li&gt;
&lt;li&gt;Limitations on time - there may be limitations on the execution time of functions, which can impact long-running processes.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;cloud-gpu-hosting&quot;&gt;Cloud GPU Hosting&lt;/h3&gt;
&lt;p&gt;Cloud GPU hosting provides access to GPU and TPU (Tensor Processing Unit) hardware that can perform the parallel operations essential for AI model training and inference. The provider allows users to configure specific hardware for their jobs.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;Diagram showing AI model running on specific GPU&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 545px) 545px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;545&quot; height=&quot;390&quot; src=&quot;https://img.ly/_astro/gpu3_Z27RcaD.webp&quot; srcset=&quot;/_astro/gpu3_Z27RcaD.webp 545w&quot;&gt;&lt;br&gt;
With cloud GPU each service or model gets its own GPU while running. Your other services communicate with the model through an API.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Cloud GPU Pros:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;High performance: GPUs are specifically designed to run AI models and other tasks like deep learning and complex simulations.&lt;/li&gt;
&lt;li&gt;Full control of hardware: users can specify specific hardware configurations for their projects.&lt;/li&gt;
&lt;li&gt;Persistent availability: resources are not deallocated, so there is no latency for provisioning for the first request.&lt;/li&gt;
&lt;li&gt;Cost-effective experiments: the upfront cost of purchasing GPU hardware to experiment with different configurations is eliminated. Services are priced with a pay-as-you-go model.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Cloud GPU Cons:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Costs over time: costs do not go down during periods of low demand. Over time, costs can potentially surpass the cost of investing in local hardware.&lt;/li&gt;
&lt;li&gt;Management overhead - managing and optimizing hardware configurations is not automatically part of the hosting. You’ve got to learn some server administration and manage security and upgrades.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;providers&quot;&gt;Providers&lt;/h2&gt;
&lt;p&gt;It’s important to understand that this isn’t a ranking of the best providers or an endorsement. It’s what we discovered with some web searching, reviewing the available documentation, and tinkering with any demo or free tools and models the provider makes available. The list could easily have been different providers and we think some of the pros and cons and qualities would be the same. Hopefully, some of the questions we raise and the pros or cons we noticed in our research can help you to guide your research.&lt;/p&gt;
&lt;p&gt;Our goal was to find potential hosts for various workflows with different models in a scalable manner. We want to be able to build applications around the workflows. Some of our, specific, requirements include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Autoscaling, ideally out-of-the-box without the need for custom Kubernetes setup or similar technologies.&lt;/li&gt;
&lt;li&gt;Minimal vendor lock-in.&lt;/li&gt;
&lt;li&gt;Compatibility with various technologies (REST API, WebSocket, Webhooks, etc.).&lt;/li&gt;
&lt;li&gt;Support for Windows Server.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;With those disclaimers and caveats, here is a short summary of our research.&lt;/p&gt;

























































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Provider&lt;/th&gt;&lt;th&gt;Best For&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Runpod IO (Serverless)&lt;/td&gt;&lt;td&gt;Deploy AI models with GPU support and require customizable API interfaces.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Vast AI (Serverless)&lt;/td&gt;&lt;td&gt;Affordable GPU resources and a variety of GPU options for AI model training.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Paperspace (Serverless)&lt;/td&gt;&lt;td&gt;Flexible workflows and support for different stages of AI model development.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;CoreWeave (Serverless)&lt;/td&gt;&lt;td&gt;Strong knowledge of Kubernetes and need autoscaling capabilities for AI workloads.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Modal (Serverless)&lt;/td&gt;&lt;td&gt;Comprehensive documentation and examples for deploying AI models in containers.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;ComfyICU (Serverless)&lt;/td&gt;&lt;td&gt;Serverless infrastructure tailored for hosting ComfyUI applications.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Replicate (Serverless)&lt;/td&gt;&lt;td&gt;Easy-to-use API for executing AI tasks without managing infrastructure.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Genesis Cloud (Cloud GPU)&lt;/td&gt;&lt;td&gt;Sustainability and need scalable GPU instances for AI model training.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Fly IO (Cloud GPU)&lt;/td&gt;&lt;td&gt;To deploy complete applications with GPU support in a scalable environment.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Runpod IO (Cloud GPU)&lt;/td&gt;&lt;td&gt;GPU resources in various regions and require customizable Docker-based deployments.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Lamda Labs (Cloud GPU)&lt;/td&gt;&lt;td&gt;On-demand GPU resources for model training and inference tasks.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Together AI (Cloud GPU)&lt;/td&gt;&lt;td&gt;A platform for testing serverless models and occasional access to GPU clusters.&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;If you want to skip ahead to a specific part, here are the providers we will be diving into:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Serverless Providers&lt;/strong&gt;&lt;br&gt;
&lt;a href=&quot;https://img.ly/blog/reviewing-cloud-gpu-providers-for-training-ai-models//#runpodioserverless&quot;&gt;Runpod IO (Serverless)&lt;/a&gt;&lt;br&gt;
&lt;a href=&quot;https://img.ly/blog/reviewing-cloud-gpu-providers-for-training-ai-models//#vastai&quot;&gt;Vast AI&lt;/a&gt;&lt;br&gt;
&lt;a href=&quot;https://img.ly/blog/reviewing-cloud-gpu-providers-for-training-ai-models//#paperspace&quot;&gt;Paperspace&lt;/a&gt;&lt;br&gt;
&lt;a href=&quot;https://img.ly/blog/reviewing-cloud-gpu-providers-for-training-ai-models//#bananadev&quot;&gt;Banana Dev&lt;/a&gt;&lt;br&gt;
&lt;a href=&quot;https://img.ly/blog/reviewing-cloud-gpu-providers-for-training-ai-models//#coreweave&quot;&gt;CoreWeave&lt;/a&gt;&lt;br&gt;
&lt;a href=&quot;https://img.ly/blog/reviewing-cloud-gpu-providers-for-training-ai-models//#modal&quot;&gt;Modal&lt;/a&gt;&lt;br&gt;
&lt;a href=&quot;https://img.ly/blog/reviewing-cloud-gpu-providers-for-training-ai-models//#comfyicu&quot;&gt;ComfyICU&lt;/a&gt;&lt;br&gt;
&lt;a href=&quot;https://img.ly/blog/reviewing-cloud-gpu-providers-for-training-ai-models//#replicate&quot;&gt;Replicate&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;GPU Cloud Providers&lt;/strong&gt;&lt;br&gt;
&lt;a href=&quot;https://img.ly/blog/reviewing-cloud-gpu-providers-for-training-ai-models//#genesiscloud&quot;&gt;Genesis Cloud&lt;/a&gt;&lt;br&gt;
&lt;a href=&quot;https://img.ly/blog/reviewing-cloud-gpu-providers-for-training-ai-models//#flyio&quot;&gt;Fly IO&lt;/a&gt;&lt;br&gt;
&lt;a href=&quot;https://img.ly/blog/reviewing-cloud-gpu-providers-for-training-ai-models//#runpodiocloudgpu&quot;&gt;Runpod IO (Cloud GPU)&lt;/a&gt;&lt;br&gt;
&lt;a href=&quot;https://img.ly/blog/reviewing-cloud-gpu-providers-for-training-ai-models//#lamdalabs&quot;&gt;Lamda Labs&lt;/a&gt;&lt;br&gt;
&lt;a href=&quot;https://img.ly/blog/reviewing-cloud-gpu-providers-for-training-ai-models//#togetherai&quot;&gt;Together AI&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;serverless-providers&quot;&gt;Serverless Providers&lt;/h2&gt;
&lt;h3 id=&quot;runpod-io-serverless&quot;&gt;Runpod IO (Serverless)&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://www.runpod.io/&quot;&gt;Runpod IO&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Concept:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A Docker image that includes the installation of Python + GPU packages, models, and ComfyUI.&lt;/li&gt;
&lt;li&gt;Python/Go handlers act as an API interface to ComfyUI, which is vendor-specific, but can be wrapped in a more general API for reuse. For more information, see &lt;a href=&quot;https://9elements.com/blog/hosting-a-comfyui-workflow-via-api/&quot;&gt;this article on hosting a ComfyUI workflow via API&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Pros:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Good documentation, including public GitHub repositories with examples.&lt;/li&gt;
&lt;li&gt;Relatively large community for a new provider.&lt;/li&gt;
&lt;li&gt;Compatibility with Windows Server.&lt;/li&gt;
&lt;li&gt;Handlers allow for webhook and WebSocket-like communication for API feedback.&lt;/li&gt;
&lt;li&gt;Network volume to store models/data and reduce cold start times.&lt;/li&gt;
&lt;li&gt;Control over the number of workers and the ability to define persistently active workers.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Cons:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Availability of GPUs, especially in Europe, needs to be validated.&lt;/li&gt;
&lt;li&gt;Handlers can only be written in Python and Go.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Open Questions:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;General open questions regarding serverless infrastructure and AI inference tasks.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Conclusion:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The overall package seems very mature. The setup can largely be adopted from the GitHub examples. Good documentation and community support (notably on Reddit). The open questions regarding pricing and cold starts are typical for serverless infrastructure.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;vast-ai&quot;&gt;Vast AI&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://vast.ai/&quot;&gt;Vast AI&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Concept:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Peer-to-Peer Sharing. Companies/organizations can rent out their unused GPUs.&lt;/li&gt;
&lt;li&gt;A GPU Marketplace approach.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Pros:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Affordable prices through their peer-to-peer GPU sharing model.&lt;/li&gt;
&lt;li&gt;A wide selection of different GPUs.&lt;/li&gt;
&lt;li&gt;Good global availability of GPUs.&lt;/li&gt;
&lt;li&gt;Ability to define autoscaler groups, allowing different workflows to scale differently.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Cons:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The autoscaler is currently only in beta mode.&lt;/li&gt;
&lt;li&gt;Data privacy/security concerns when renting GPUs from anonymous providers.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Open Questions:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;How will the autoscaler beta evolve?&lt;/li&gt;
&lt;li&gt;Control over GPU providers: Can one allow only certain trusted providers (e.g., those based in the EU)?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Conclusion:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Even though the pricing is more affordable, there may be significant issues, in terms of security and data protection, as well as the fact that the autoscaler is still in the beta phase.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;paperspace&quot;&gt;Paperspace&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://docs.digitalocean.com/products/paperspace/workflows/getting-started/your-first-workflow/&quot;&gt;Paperspace&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Concept:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The serverless approach (Workflows or Gradient) is still in beta &lt;a href=&quot;https://www.paperspace.com/gradient/workflows&quot;&gt;Paperspace Gradient Workflows&lt;/a&gt; is based on &lt;a href=&quot;https://argoproj.github.io/workflows/&quot;&gt;Argo Workflows&lt;/a&gt; which utilizes Kubernetes.&lt;/li&gt;
&lt;li&gt;A predefined API is available for communicating with workflows, as detailed in &lt;a href=&quot;https://docs.digitalocean.com/reference/paperspace/pspace/commands/completion/&quot;&gt;DigitalOcean’s documentation for Paperspace commands&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Pros:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The ability to use different machines (GPUs) at different stages of a workflow.&lt;/li&gt;
&lt;li&gt;Provided by Digital Ocean, allows for general hosting customers to expand into GPU hosting without finding a new vendor.&lt;/li&gt;
&lt;li&gt;Possible Windows support as outlined in &lt;a href=&quot;https://docs.digitalocean.com/products/paperspace/machines/getting-started/run-windows-app/&quot;&gt;DigitalOcean’s documentation on running Windows apps&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Cons:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Complex documentation: offers many features for various use cases (AI learning, data preparation, validation, and inference).&lt;/li&gt;
&lt;li&gt;Vendor lock-in through a proprietary system: Gradient Workflows and YAML config are specific to Paperspace.&lt;/li&gt;
&lt;li&gt;No real-time feedback over the API.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Open Questions:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Since it’s still in beta, how will the ecosystem continue to develop?&lt;/li&gt;
&lt;li&gt;How extensive is the knowledge of Kubernetes required to implement autoscaling?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Conclusion:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;It’s positive that it’s offered by Digital Ocean as they are a more mature company with general hosting experience. The approach seems very specific to Digital Ocean. Furthermore, it may require experience with Kubernetes.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;banana-dev&quot;&gt;Banana Dev&lt;/h3&gt;
&lt;p&gt;It has been excluded: Recently, they announced the termination of their serverless model as it was not cost-effective.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Learning from this: Currently, there are many new providers entering the market aiming to establish themselves as cloud GPU or serverless GPU providers. This highlights the importance of minimizing vendor lock-in.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id=&quot;coreweave&quot;&gt;CoreWeave&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://docs.coreweave.com/coreweave-kubernetes/serverless&quot;&gt;CoreWeave&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Concept:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Heavily based on Kubernetes.
&lt;ul&gt;
&lt;li&gt;A Kubernetes file is created for setup; scaling and additional infrastructure are managed by Core Weave.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Pros:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Autoscaling by default with the possibility of scaling to zero.&lt;/li&gt;
&lt;li&gt;Supports Windows.&lt;/li&gt;
&lt;li&gt;Minimal vendor lock-in due to Kubernetes configuration.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Cons:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Strong dependency on Kubernetes, with the serverless setup based on &lt;a href=&quot;https://knative.dev/docs/&quot;&gt;KNative documentation&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Does not offer a handler API, etc., to communicate directly with ComfyUI.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Open Questions:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;How complicated would it be to implement an API interface and resulting scaling to address the correct instances, etc.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Conclusion:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Good documentation and a close interface to Kubernetes. For a team with strong knowledge of Kubernetes, this could be a prime candidate.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;modal&quot;&gt;Modal&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Concept:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Container Setup: Containers are defined through Modal’s own container setup &lt;a href=&quot;https://modal.com/docs/guide/custom-container&quot;&gt;Modal custom container documentation&lt;/a&gt;.
&lt;ul&gt;
&lt;li&gt;Docker images can also be used.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Modal-specific handlers to communicate with ComfyUI and other models.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Pros:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Supports webhooks and custom endpoints &lt;a href=&quot;https://modal.com/docs/guide/webhooks#custom-domains&quot;&gt;Modal webhooks documentation&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Focus on fast startups/cold starts.&lt;/li&gt;
&lt;li&gt;Emphasis on AI inference tasks.&lt;/li&gt;
&lt;li&gt;Comprehensive documentation with many examples.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Cons:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Vendor lock-in if Modal’s container setup is used.&lt;/li&gt;
&lt;li&gt;Autoscaling and scaling configuration are not directly described.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Open Questions:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;How exactly does the autoscaling work?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Assessment:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;For us, this is a candidate for closer consideration. The container setup can be managed through Dockerfiles, and the API defined by Modal’s own interface.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;comfyicu&quot;&gt;ComfyICU&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://comfy.icu/serverless/&quot;&gt;ComfyICU&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Concept:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Pure focus on ComfyUI, serverless infrastructure.&lt;/li&gt;
&lt;li&gt;API interface for communication.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Pros:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Minimal setup effort.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Cons:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Limited control over the API.&lt;/li&gt;
&lt;li&gt;Limited GPU resources.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Open Questions:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;How does the autoscaling work, if it exists at all?&lt;/li&gt;
&lt;li&gt;Community-based open source. What is the long-term support for this project?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Conclusion:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Potentially useful for testing or building a demo site, but probably not suitable for developing our commercial applications.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;replicate&quot;&gt;Replicate&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://replicate.com/&quot;&gt;Replicate&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Concept:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Execution of AI tasks/models in the cloud via an API.&lt;/li&gt;
&lt;li&gt;No access to infrastructure, etc.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Pros:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Supports various languages: Node, Python, Swift.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Cons:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;No control over the infrastructure, number of GPUs, or workers.&lt;/li&gt;
&lt;li&gt;API rate limits.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Open Questions:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;How can autoscaling be enabled?&lt;/li&gt;
&lt;li&gt;Is it possible to create custom API endpoints, webhooks, websockets?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Conclusion:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;For testing or as a demo for one’s own model, this can be a very good platform. However, as a standalone application interface, it doesn’t meet some of our core requirements.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;gpu-cloud-providers&quot;&gt;GPU Cloud Providers&lt;/h2&gt;
&lt;h3 id=&quot;genesis-cloud&quot;&gt;Genesis Cloud&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://www.genesiscloud.com/&quot;&gt;Genesis Cloud&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Concept:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Focus on sustainability and renewable energy.&lt;/li&gt;
&lt;li&gt;Scaling through instances as detailed in &lt;a href=&quot;https://developers.genesiscloud.com/instances&quot;&gt;here&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Pros:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A REST API is available for managing instances.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Cons:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The availability of GPUs varies significantly by region.&lt;/li&gt;
&lt;li&gt;Limited selection of GPUs.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Open Questions:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;How quickly can new instances be scaled up or down?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Conclusion:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The use case for Genesis Cloud appears to be more suited for model training or tasks that require a significant amount of computing power for extended periods.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;fly-io&quot;&gt;Fly IO&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://fly.io/&quot;&gt;Fly IO&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Concept:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Focus on the deployment of complete applications.&lt;/li&gt;
&lt;li&gt;Also offers its own GPU servers.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Pros:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Docker File support with additional configuration via a TOML file.&lt;/li&gt;
&lt;li&gt;Quick scaling of GPUs up or down facilitated by the launch process.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Cons:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Limited selection of GPUs, with only very large GPUs available.&lt;/li&gt;
&lt;li&gt;Specifically tailored for Linux.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Open Questions:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;How well does the launch system perform for relatively fast inference tasks?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Conclusion:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Since primarily large GPUs are available, the focus here also appears to be more on model training or other long-duration tasks. However, the launch system might also potentially be used for inference.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;runpod-io-cloud-gpu&quot;&gt;Runpod IO (Cloud GPU)&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://www.runpod.io/&quot;&gt;Runpod IO&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Concept:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A wide range of GPUs available across various regions.&lt;/li&gt;
&lt;li&gt;Base Docker images for popular tasks or support for custom Docker images.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Pros:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Many different data center regions.&lt;/li&gt;
&lt;li&gt;A variety of CPUs available.&lt;/li&gt;
&lt;li&gt;Simple setup via Docker images.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Cons:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;No direct autoscaling (would need to use Runpod Serverless for that).&lt;/li&gt;
&lt;li&gt;Despite a large selection of GPUs and many different data center locations, the availability of GPUs is not very high.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Open Questions:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Can autoscaling be implemented without using serverless?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Conclusion:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The setup can largely be adopted from the GitHub examples. There is good documentation and a community (much of it on Reddit). The availability of GPUs could become a problem, especially for smaller GPUs.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;lamda-labs&quot;&gt;Lamda Labs&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Concept:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;On-demand cloud with a focus on model training and inference.&lt;/li&gt;
&lt;li&gt;Similar concept to Runpod, offering a variety of GPUs.
&lt;ul&gt;
&lt;li&gt;GPU availability is very limited.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Conclusion:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Runpod and Lambda Labs seem to have a similar approach and similar offerings. Runpod appears to have greater availability.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;together-ai&quot;&gt;Together AI&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Concept:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Offers an API and playground for testing serverless models.&lt;/li&gt;
&lt;li&gt;Also offers GPU clusters but only upon request.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Conclusion:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;We didn’t dig into the GPU clusters since information is available only upon request. Otherwise, in the API/serverless area, it appears to be similar to Replicate.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;established-providers&quot;&gt;Established Providers&lt;/h2&gt;
&lt;p&gt;As we said in the introduction we did not examine the old, large providers like Google Cloud, AWS, Azure, Nvidia, etc., in detail. Rather, we focused on the new providers aiming specifically at the market segment of AI GPUs. With the older providers, we are more in the realm of cloud GPUs and less in serverless. Given the size of these providers and the wide range of market segments they cover, it can make sense to opt for them if one is already familiar with their architecture and documentation.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Google Cloud Platform (GCP)&lt;/li&gt;
&lt;li&gt;AWS&lt;/li&gt;
&lt;li&gt;Microsoft Azure&lt;/li&gt;
&lt;li&gt;IBM Cloud&lt;/li&gt;
&lt;li&gt;NVIDIA GPU Cloud (NGC)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Just as we saw that performance can vary wildly for different models, pricing can be similarly complex. When evaluating costs, consider factors like response times, the number of required workers, and potential charges for features like caching. Many providers offer detailed pricing guidelines on their websites, which can be crucial for ensuring you only pay for the computing power you truly need. Experimenting with performance of your model and applications during development will be helpful to make sure your hardware and pricing are both optimized for your application.&lt;/p&gt;
&lt;p&gt;Another thing to consider is what kind of experience does your team already have? Most cloud GPU services provide tools like CLI or REST APIs to manage resources, which can be a steep learning curve if your team is not familiar with these technologies. Additionally, while serverless platforms may support multiple programming languages, compatibility with your team’s preferred language—be it JavaScript, Python, or Go—is essential. As exciting as it can be to learn new languages, it’s probably not the best use of your team’s time.&lt;/p&gt;
&lt;p&gt;The size of files you’ll be moving between your model and the other parts of your project may also be a factor. Your users may not notice latency for models that communicate using text only. Text moves quickly from point to point in a network. However, if your model takes large image files as input or output, you may find that moving data between data centers is too slow. Then you’d want to focus on providers who can offer more general hosting in addition to cloud GPU hosting.&lt;/p&gt;
&lt;p&gt;As we continue to research this for our own projects, we are thinking the best configuration for us is to use a cloud GPU exclusively for generation tasks and communicate with it via an API from our existing back end. We will have to experiment to see if we can have those functions geographically separate, or if we need to find one hosting company and one data center for both. As we learn more we may change our ideas, but that’s part of the fun of working in technology, things change. By using the higher-cost cloud GPU for as few tasks as possible, we’ll know we aren’t wasting compute power for things easily handled by a general CPU.&lt;/p&gt;
&lt;p&gt;We hope this has given you some useful background and ideas as you research hosting options for your AI projects. Understanding the subtle differences between serverless and cloud GPU hosting can spark innovative ideas tailored to your needs. Perhaps some of the lesser-known providers we’ve explored might just be the perfect fit for your next project. As always, the dynamic nature of technology keeps us on our toes—ready to adapt and evolve. Happy hosting!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Thanks for reading. Join over 3000 specialists with powerful apps and &lt;a href=&quot;https://share.hsforms.com/1IgAOV1wASXGPnFG4ZPLejg1hk3i&quot;&gt;subscribe&lt;/a&gt; to our newsletter. We keep you in the loop with brand-new features, early access, and updates.&lt;/strong&gt;&lt;/p&gt;</content:encoded><dc:creator>Walter</dc:creator><media:content url="https://blog.img.ly/2024/04/cloud-gpu-review.jpg" medium="image"/><category>AI</category><category>Cloud</category><category>Development</category><category>Machine Learning</category></item><item><title>How to Create Image Filters for iOS</title><link>https://img.ly/blog/how-to-create-image-filters-in-ios/</link><guid isPermaLink="true">https://img.ly/blog/how-to-create-image-filters-in-ios/</guid><description>Write a custom `CIFilter` in Metal and Swift, usable in any CoreImage pipeline.</description><pubDate>Tue, 24 Aug 2021 08:09:37 GMT</pubDate><content:encoded>&lt;p&gt;In this tutorial, you will learn how to write a custom &lt;code&gt;CIFilter&lt;/code&gt; in Metal and Swift. You can use this filter in any CoreImage pipeline.&lt;/p&gt;
&lt;p&gt;Apple offers over 200 image filters in the CoreImage framework, but sometimes you need a little extra to make your images perfect. Apple provides two ways to create customize image filters. You can chain &lt;code&gt;CIFilter&lt;/code&gt;s together in a &lt;code&gt;CIFilter&lt;/code&gt; subclass or wrap a &lt;code&gt;CIKernel&lt;/code&gt; in a &lt;code&gt;CIFilter&lt;/code&gt; subclass. Either method creates a filter that CoreImage can execute on the GPU. That makes the filter fast. CoreImage filters can filter live video without impacting the frame rate. Before iOS 11 the only way to write a custom &lt;code&gt;CIKernel&lt;/code&gt; was to pass a string containing the code to the GPU at runtime using the &lt;a href=&quot;https://www.khronos.org/opengl/wiki/OpenGL_Shading_Language&quot;&gt;OpenGL Shading Language&lt;/a&gt;. This had two drawbacks: it was difficult to find errors in the string of commands and the kernel was not compiled until runtime. This tutorial will show you how to add a custom filter with a Metal-based &lt;code&gt;CIKernel&lt;/code&gt; to any Swift project in Xcode. The code samples in this tutorial use Xcode 12.5 and Swift 5.&lt;/p&gt;
&lt;p&gt;Clone &lt;a href=&quot;https://github.com/waltertyree/core-image-metal&quot;&gt;this repository&lt;/a&gt; for a sample project and example code that supports this tutorial. The repo also contains some other custom filters, demonstrating other aspects of Metal filters.&lt;/p&gt;
&lt;h2 id=&quot;adding-metal-support-to-an-xcode-project&quot;&gt;Adding Metal Support to an Xcode Project&lt;/h2&gt;
&lt;p&gt;The first step is to add a Metal source code file to your project.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;new-file&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 738px) 738px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;738&quot; height=&quot;529&quot; src=&quot;https://img.ly/_astro/new-file_2hEg5C.webp&quot; srcset=&quot;/_astro/new-file_Z1TNBBR.webp 640w, /_astro/new-file_2hEg5C.webp 738w&quot;&gt;&lt;/p&gt;
&lt;p&gt;Find the “Metal File” template, select it and click &lt;code&gt;Next&lt;/code&gt;. Name the file and save it. At compile time, Xcode combines all of the &lt;code&gt;.metal&lt;/code&gt; files in your project into a single &lt;code&gt;.metallib&lt;/code&gt; file. So, organize your Metal code into lots of files or just one file as you prefer. The last step before you start writing code is to add compiler flags for &lt;code&gt;CIKernel&lt;/code&gt; objects.&lt;/p&gt;
&lt;p&gt;In the Build Settings, find the &lt;code&gt;other Metal Compiler Flags&lt;/code&gt; and add an entry of &lt;code&gt;-fcikernel&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;build-settings-1&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 630px) 630px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;630&quot; height=&quot;485&quot; src=&quot;https://img.ly/_astro/build-settings-1_1ww0y8.webp&quot; srcset=&quot;/_astro/build-settings-1_1ww0y8.webp 630w&quot;&gt;&lt;/p&gt;
&lt;p&gt;Then find the &lt;code&gt;Other Metal Linker Flags&lt;/code&gt; and add an entry of &lt;code&gt;-cikernel&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;other-metal-linker-flag&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 630px) 630px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;630&quot; height=&quot;145&quot; src=&quot;https://img.ly/_astro/other-metal-linker-flag_Z2a5E9F.webp&quot; srcset=&quot;/_astro/other-metal-linker-flag_Z2a5E9F.webp 630w&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Important note:&lt;/em&gt; any project without &lt;code&gt;.metal&lt;/code&gt; files hides the Metal Compiler and Linker sections from build Settings.&lt;/p&gt;
&lt;h2 id=&quot;setting-up-a-metal-file-for-coreimage-kernels&quot;&gt;Setting Up a Metal File for CoreImage Kernels&lt;/h2&gt;
&lt;p&gt;Metal is a general-purpose technology for writing code that will execute on the GPU. The compiler flags tell Metal that you will be writing code to work with &lt;code&gt;CoreImage&lt;/code&gt;. With a Metal source file in your project and the compiler flags set, you’re finally ready to write some code! Open the Metal file you created above. It should be empty except for&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;#include &amp;#x3C;metal_stdlib&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;using namespace metal;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Below these lines, import the CoreImage headers by adding:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;#include &amp;#x3C;CoreImage/CoreImage.h&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;and add a stub for your filter code with:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;extern &quot;C&quot; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  namespace coreimage {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    // KERNEL GOES HERE&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Prefix your kernel code with &lt;code&gt;extern &quot;C&quot;&lt;/code&gt; and the CoreImage namespace. You write kernel code in the &lt;a href=&quot;https://developer.apple.com/metal/Metal-Shading-Language-Specification.pdf&quot;&gt;Metal Shader Language&lt;/a&gt;, which is a variation of C. If you only know Swift, you should be able to rely on Xcode’s code-completion and symbol lookup to help you as you’re learning to write Metal code. In addition to the general language reference, Apple provides a &lt;a href=&quot;https://developer.apple.com/metal/MetalCIKLReference6.pdf&quot;&gt;Metal Shading Language for Core Image Kernels&lt;/a&gt; document.&lt;/p&gt;
&lt;p&gt;The example below creates a &lt;code&gt;CIColorKernel&lt;/code&gt;. This kernel is optimized for changing the color value of each pixel in an image. It will change each pixel to a grayscale version of itself. A filter applying his kernel will send the color value of each pixel of the image to the kernel.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;float4 grayscaleFilterKernel(sample_t s) { //1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  float gray = (s.r + s.g + s.b) / 3;      //2&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  return float4(gray, gray, gray, s.a);    //3&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here is how this kernel works:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The filter provides the color of the current pixel to the kernel as a &lt;code&gt;sample_t&lt;/code&gt; type. The kernel will return the new color for that pixel as a &lt;code&gt;float4&lt;/code&gt; type. The &lt;code&gt;sample_t&lt;/code&gt; type is equivalent to a &lt;code&gt;float4&lt;/code&gt; type and is only named differently because of historical convention. A &lt;code&gt;float4&lt;/code&gt; type contains four float values. In the case of color, they are the red, green, blue and alpha values for the pixel. Each float has a value between zero and 1.&lt;/li&gt;
&lt;li&gt;Calculate a grayscale version of the pixel by averaging the three color channels.&lt;/li&gt;
&lt;li&gt;Create a new &lt;code&gt;float4&lt;/code&gt; with the averaged value for the three color channels and the original alpha value for the fourth.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;CoreImage also provides a &lt;code&gt;CIWarpKernel&lt;/code&gt; class that is optimized for changing the position of each pixel and a &lt;code&gt;CIBlendKernel&lt;/code&gt; for blending two images. For more complex tasks, CoreImage also provides a general &lt;code&gt;CIKernel&lt;/code&gt;. Apple suggests that chaining together optimized kernels in a pipeline is usually more efficient than trying to write a larger, general kernel. A color-optimized kernel only has access to the color of the current pixel. A transform-optimized kernel can only affect the position of the current pixel. A general kernel can impact color and position as well as read other values from the source image.&lt;/p&gt;
&lt;h2 id=&quot;wrapping-a-cikernel-with-a-cifilter&quot;&gt;Wrapping a `CIKernel` with a `CIFilter`&lt;/h2&gt;
&lt;p&gt;Now you’ll make a &lt;code&gt;CIFilter&lt;/code&gt; subclass as the interface between the Metal code and your Swift code. Every &lt;code&gt;CIFilter&lt;/code&gt; has an &lt;code&gt;outputImage&lt;/code&gt; variable that returns a &lt;code&gt;CIImage&lt;/code&gt;. Generally, input variables for a filter begin with &lt;code&gt;input&lt;/code&gt;. The grayscale filter you are making has a single input and a single output. Create a new Swift file in your project and title it “GrayscaleFilter.swift”. Then be sure to import CoreImage by adding&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;import CoreImage&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;to the top of the file. Then create the filter as a subclass of &lt;code&gt;CIFilter&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;class GrayscaleFilter: CIFilter {&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, create a &lt;code&gt;kernel&lt;/code&gt; variable as either &lt;code&gt;static&lt;/code&gt; or &lt;code&gt;lazy&lt;/code&gt;, so that it only gets created one time, regardless of how often it’s called.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;static var kernel: CIColorKernel = { () -&gt; CIColorKernel in&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    let url = Bundle.main.url(forResource: &quot;default&quot;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                            withExtension: &quot;metallib&quot;)! //1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    let data = try! Data(contentsOf: url)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    return try! CIColorKernel(functionName: &quot;grayscaleFilterKernel&quot;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                      fromMetalLibraryData: data) //2&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}()&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;Look in the bundle for the compiled metal code. The metal code will have a filename of “default”.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;.metalib&lt;/code&gt; may contain many kernels so use the one called “grayscaleFilterKernel”&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Now add a variable to hold the input image.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;var inputImage: CIImage?&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally override the &lt;code&gt;outputImage&lt;/code&gt; variable.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;override var outputImage: CIImage? {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    guard let inputImage = inputImage else { return .none }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    return GrayscaleFilter.kernel.apply(extent: inputImage.extent,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                                   roiCallback: { (index, rect) -&gt; CGRect in&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                                                  return rect&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                                  }, arguments: [inputImage])&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;apply&lt;/code&gt; function of the kernel takes an &lt;code&gt;extent&lt;/code&gt; argument. For a &lt;code&gt;CIImage&lt;/code&gt; the &lt;code&gt;extent&lt;/code&gt; property is the width and height of the image. The function has an &lt;code&gt;roiCallback&lt;/code&gt; that allows you to specify what part of the image the kernel uses for each calculation. For a color filter, you generally just pass back the &lt;code&gt;rect&lt;/code&gt;. Finally, pass in any &lt;code&gt;arguments&lt;/code&gt; as an array. Notice that there is not any type checking here. It is up to you to pass in the correct types in the correct order.&lt;/p&gt;
&lt;h2 id=&quot;using-the-filter&quot;&gt;Using the Filter&lt;/h2&gt;
&lt;p&gt;With the Metal code wrapped in a &lt;code&gt;CIFilter&lt;/code&gt; you can now apply the filter images the same as using one of the built-in filters. Create an instance of the filter, then assign the &lt;code&gt;inputImage&lt;/code&gt; variable a &lt;code&gt;CIImage&lt;/code&gt; and display the &lt;code&gt;outputImage&lt;/code&gt;. Your code might look something like this:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;let filter = GrayscaleFilter()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;filter.inputImage = image&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;displayView.image = UIImage(ciImage: (filter.outputImage ?? image) ?? CIImage())&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;and it can render a grayscale version of an image, like in this example.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;filter-example&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 820px) 820px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;820&quot; height=&quot;766&quot; src=&quot;https://img.ly/_astro/filter-example_Z1xaewd.webp&quot; srcset=&quot;/_astro/filter-example_5m9Hh.webp 640w, /_astro/filter-example_ZL1iK4.webp 750w, /_astro/filter-example_Z1xaewd.webp 820w&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;going-further&quot;&gt;Going Further&lt;/h2&gt;
&lt;p&gt;You can write custom filters and chain them to the built-in filters to invent new effects. Many of the math formulas for effects are available on the Internet. Most are easy to translate into the Metal Shader Language to make them perfect for your application. Writing an entire image or video editing application to showcase your filters is a much larger task. Using an SDK like the &lt;a href=&quot;https://img.ly/?utm_source=imgly&amp;#x26;utm_medium=blog&amp;#x26;utm_campaign=howtos&quot;&gt;IMG.LY&lt;/a&gt; &lt;code&gt;PhotoEditorSDK&lt;/code&gt; or &lt;code&gt;VideoEditorSDK&lt;/code&gt; can help you provide an app that has the features that users will expect in addition to your cool filters.&lt;/p&gt;
&lt;p&gt;To add your filter to the &lt;code&gt;PhotoEditorSDK&lt;/code&gt; you first wrap your &lt;code&gt;CIFilter&lt;/code&gt; with an &lt;code&gt;Effect&lt;/code&gt;. For a basic filter, you only need to override the &lt;code&gt;newEffectFilter&lt;/code&gt; variable.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;import PhotoEditorSDK&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;class GrayscaleEffect: Effect {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  override var newEffectFilter: CIFilter? {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      GrayscaleFilter()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then add the filter to the &lt;code&gt;Effect.all&lt;/code&gt; array when configuring the SDK, using something like&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Effect.all = [NoEffect(), GrayscaleEffect(identifier: &quot;grayscaleFilter&quot;, displayName: &quot;Best Gray&quot;)]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now your filter appears the same as the other 60+ filters that ship with the SDK.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 316px) 316px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;316&quot; height=&quot;640&quot; src=&quot;https://img.ly/_astro/create-filter-for-iOS-2_1EN1kE.webp&quot; srcset=&quot;/_astro/create-filter-for-iOS-2_1EN1kE.webp 316w&quot;&gt;&lt;/p&gt;
&lt;p&gt;It applies to live camera previews as well as still images.&lt;/p&gt;
&lt;h2 id=&quot;wrapping-up&quot;&gt;Wrapping Up&lt;/h2&gt;
&lt;p&gt;In this tutorial, you learned how to configure Xcode for custom Metal code. You also learned how to create a color filter kernel in Metal and wrap it in a &lt;code&gt;CIFilter&lt;/code&gt;. By combining your filters with Apple’s filters in pipelines, you can create unique effects for your next app. Further, using an SDK such as &lt;code&gt;PhotoEditorSDK&lt;/code&gt; or &lt;code&gt;VideoEditorSDK&lt;/code&gt; allows you to showcase your filters in a full-featured image or video editing application. The PhotoEditor SDK for iOS &lt;a href=&quot;https://img.ly/docs/pesdk/ios/introduction/overview/&quot;&gt;documentation&lt;/a&gt; will give you deeper look into all other image adjustments including, ofcourse, the image filtering we discussed above.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Thanks for reading! We hope that you found this tutorial helpful. Feel free to reach out to us on &lt;a href=&quot;https://twitter.com/imgly&quot;&gt;Twitter&lt;/a&gt; with any questions, comments, or suggestions.&lt;/strong&gt;&lt;/p&gt;</content:encoded><dc:creator>Walter</dc:creator><media:content url="https://blog.img.ly/2021/08/create-a-photo-filter-for-iOS.jpg" medium="image"/><category>App Development</category><category>iOS App Development</category><category>iOS</category><category>Photo Editing</category><category>Tutorial</category><category>Development</category><category>Mobile App Development</category><category>Photo Filter</category><category>How-To</category></item><item><title>A Photo and Video Editor for React Native Apps</title><link>https://img.ly/blog/a-photo-and-video-editor-for-your-react-native-apps/</link><guid isPermaLink="true">https://img.ly/blog/a-photo-and-video-editor-for-your-react-native-apps/</guid><pubDate>Fri, 29 May 2020 10:30:01 GMT</pubDate><content:encoded>&lt;hr&gt;
&lt;h3 id=&quot;how-to-integrate-a-photo-and-video-editor-into-your-react-native-app&quot;&gt;How to integrate a Photo and Video Editor into your React Native App&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;This tutorial walks you through the integration process of PhotoEditor SDK and VideoEditor SDK into your React Native app for iOS and Android. You’ll learn how to use our React Native modules to facilitate the integration and to customize our editors. For this tutorial we presume that all the necessary development tools for building an iOS and Android app are met, so make sure to complete the official &lt;a href=&quot;https://reactnative.dev/docs/getting-started&quot;&gt;React Native CLI Quickstart&lt;/a&gt; guides for iOS and Android beforehand.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Please make sure to acquire the licenses for &lt;a href=&quot;https://img.ly/products/photo-sdk/&quot;&gt;PhotoEditor SDK&lt;/a&gt; and &lt;a href=&quot;https://img.ly/products/video-sdk/&quot;&gt;VideoEditor SDK&lt;/a&gt; before integrating them.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/embed/e9JiCMQKrJY?feature=oembed&quot;&gt;https://www.youtube.com/embed/e9JiCMQKrJY?feature=oembed&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;In this tutorial, we’re going to show how to integrate PhotoEditor SDK and VideoEditor SDK for iOS and Android into your React Native app. Therefore, we created React Native modules for our products to simplify this process for you as much as possible. We’re going to use &lt;a href=&quot;https://github.com/imgly/vesdk-react-native&quot;&gt;VideoEditor SDK’s README&lt;/a&gt;, which is in most parts identical to the PhotoEditor SDK’s README, and Visual Studio Code. So, let’s get started.&lt;/p&gt;
&lt;p&gt;First, we create a React Native project with the name “Demo” based on the default template by using the command &lt;code&gt;npx react-native init Demo&lt;/code&gt;. The project will now be initialized and automatically install all dependencies of the current React Native version. Afterwards, we can find the new React Native project ready to use in the folder “Demo”. So, we’ll speed this up a little.&lt;/p&gt;
&lt;p&gt;We already prepared another folder with resources and assets that we want to integrate in our app. Here we chose an image, the required licenses for our PhotoEditor SDK and VideoEditor SDK for both target platforms, a video and two logos that we will later use to show how we can customize our editors. We copy these resources into the root of our project to make the resources accessible for our app.&lt;/p&gt;
&lt;p&gt;Now, we switch to the folder of the “Demo” project*.* We can now copy and execute the command &lt;code&gt;yarn add react-native-videoeditorsdk&lt;/code&gt; from the README to install the dependencies to the React Native module for our VideoEditor SDK … and to the React Native module for PhotoEditor SDK by issuing the command &lt;code&gt;yarn add react-native-photoeditorsdk&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Now, we’re going to set up the dependencies for our native iOS libraries. We can simply copy the command &lt;code&gt;cd ios &amp;#x26;&amp;#x26; pod install &amp;#x26;&amp;#x26; cd ..&lt;/code&gt; from the README and execute it to install all iOS dependencies. They include the native PhotoEditor SDK and VideoEditor SDK libraries that are required by our React Native modules.&lt;/p&gt;
&lt;p&gt;And now, we set up the dependencies for our native Android libraries. The required steps that we will now take are described in detail in the README.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;android {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  defaultConfig {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    multiDexEnabled true&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;dependencies {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  implementation ‘androidx.multidex:multidex:2.0.1’&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We copy the lines and add them at the end of our &lt;code&gt;android/app/build.gradle&lt;/code&gt; file. Now we need to change the superclass of our &lt;code&gt;MainApplication&lt;/code&gt; class to enable Multidex. Next, we add the img.ly repository and the plugin by copying the following lines and add them at the top of our &lt;code&gt;android/build.gradle&lt;/code&gt; file located in our Android folder.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;buildscript {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  repositories {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    jcenter()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    maven { url &quot;https://plugins.gradle.org/m2/&quot; }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    maven { url &quot;https://artifactory.img.ly/artifactory/imgly&quot; }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  dependencies {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    classpath &quot;org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.61&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    classpath &apos;ly.img.android.sdk:plugin:7.1.8&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, we can configure our PhotoEditor SDK and VideoEditor SDK by opening the &lt;code&gt;android/app/build.gradle&lt;/code&gt; file and add these lines under &lt;code&gt;apply plugin: “com.android.application&quot;&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;apply plugin: &apos;ly.img.android.sdk&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;apply plugin: &apos;kotlin-android&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;// Comment out the modules you don&apos;t need, to save size.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;imglyConfig {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  modules {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    include &apos;ui:text&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    include &apos;ui:focus&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    include &apos;ui:frame&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    include &apos;ui:brush&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    include &apos;ui:filter&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    include &apos;ui:sticker&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    include &apos;ui:overlay&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    include &apos;ui:transform&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    include &apos;ui:adjustment&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    include &apos;ui:text-design&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    include &apos;ui:video-trim&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    // This module is big, remove the serializer if you don&apos;t need that feature.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    include &apos;backend:serializer&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    // Remove the asset packs you don&apos;t need, these are also big in size.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    include &apos;assets:font-basic&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    include &apos;assets:frame-basic&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    include &apos;assets:filter-basic&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    include &apos;assets:overlay-basic&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    include &apos;assets:sticker-shapes&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    include &apos;assets:sticker-emoticons&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Getting back to our iOS version, we can now launch our demo project on iOS, which will currently look like a plain React Native project that we initialized with the first command.&lt;/p&gt;
&lt;p&gt;The main difference to an off-the-shelf React Native project is that our React Native modules are installed and ready-to-use in the &lt;code&gt;App.js&lt;/code&gt; file once the native projects are compiled. Then, it won’t be necessary to recompile the native projects for the remainder of this tutorial. We sped up the compilation a little and here we go — our React Native app is running on the iOS simulator. Now, we do the same for the Android version and wait until the project is compiled.&lt;/p&gt;
&lt;p&gt;Now, the demo project launched on both platforms as we can see on the right on the iOS simulator at the top, and on the Android emulator at the bottom of the screen.&lt;/p&gt;
&lt;p&gt;We’ve decided that we want to start our photo editor by pressing a button. So next, we’re going to actually customize our React Native app by adding this button. Therefore, we open the &lt;code&gt;App.js&lt;/code&gt; file and import the &lt;code&gt;Button&lt;/code&gt; component in order to create a button with the title “Edit a sample image”. For now, we leave the &lt;code&gt;onPress&lt;/code&gt; function empty.&lt;/p&gt;
&lt;p&gt;We save the &lt;code&gt;App.js&lt;/code&gt; file to trigger a refresh of the running apps and immediately see the result on the right. The new button appears in both, the iOS and the Android app.&lt;/p&gt;
&lt;p&gt;Now we create a second button with the title “Edit a sample video”. This will respectively start our video editor. And again, we save the &lt;code&gt;App.js&lt;/code&gt; file and see the second button appear on the right side.&lt;/p&gt;
&lt;p&gt;Next, we are going to add the code that actually opens our editors when we press the buttons. Visual Studio Code automatically imported the respective React Native PhotoEditor SDK module for us at the very top of the file, while writing the code that makes use of our SDK. We do the same for the VideoEditor SDK. We use the &lt;code&gt;require&lt;/code&gt; function to make static assets available to our app. Here, we “require” our sample image and our sample video that we copied to the app’s folder in the beginning and pass them as the first argument to our &lt;code&gt;openEditor&lt;/code&gt; functions. The first argument can also be a regular URI.&lt;/p&gt;
&lt;p&gt;We save the &lt;code&gt;App.js&lt;/code&gt; file again and now we can click the buttons to start our photo editor or video editor. There we go! We still see a watermark here. The reason for this watermark is that we haven’t unlocked our SDKs so far which we will do next.&lt;/p&gt;
&lt;p&gt;We unlock both products with our licenses to get rid of the watermark. If not unlocked, the watermark will be on both the image and video previews as well as on the exported images and videos. To unlock the products, we use the &lt;code&gt;unlockWithLicense&lt;/code&gt; function of each SDK. In total, we need four license files, one license file for each product and platform combination. The license files should be named &lt;code&gt;pesdk_license&lt;/code&gt; and &lt;code&gt;vesdk_license&lt;/code&gt; with platform-specific extensions &lt;code&gt;.ios.json&lt;/code&gt; and &lt;code&gt;.android.json&lt;/code&gt;. React Native will then automatically pick the right file for the corresponding platform. After this, the watermarks will be removed for PhotoEditor SDK and VideoEditor SDK on both platforms. And now you can also see it in the simulator —  no watermarks anymore.&lt;/p&gt;
&lt;p&gt;In the next step, we’re going to change the configuration of the editors. If no changes are made to the configuration, our default stickers are available with the editor. To customize them, we need to import the &lt;code&gt;Configuration&lt;/code&gt; from either the PhotoEditor SDK or VideoEditor SDK. The configurations are compatible between both products. So, for this tutorial, we decided to use the VideoEditor SDK configuration which we are now using by adding &lt;code&gt;Configuration&lt;/code&gt; to the &lt;code&gt;react-native-videoeditorsdk&lt;/code&gt; imports. We decided that we want to add custom stickers to our editors. Therefore we define a non-default configuration to the sticker tool.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const configuration: Configuration = {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  sticker: {}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To customize the sticker assets, we need to define the sticker “categories” array. Here, we define a new category and name the identifier &lt;code&gt;demo_sticker_category&lt;/code&gt;. These asset identifiers must always be unique. Next, we set a name for the category and we name it “Logos”.&lt;/p&gt;
&lt;p&gt;Each category also requires a thumbnail image to be displayed in the editor. For the thumbnail, we use the React logo that we added to the folder of our app at the very beginning. Next, we define the items for this new sticker category. These items are the actual stickers that we can apply to the edited image or video. We now create a new sticker for the React logo. Therefore, we call the identifier &lt;code&gt;demo_sticker_react&lt;/code&gt; and name it “React”. These sticker names won’t appear in the UI, but they are used for accessibility. Now, we need to define the actual image that should be used for that sticker. Here we use the React image again.&lt;/p&gt;
&lt;p&gt;To create a second sticker, we can now copy and paste the code of the first sticker. We create a sticker with our img.ly logo and rename the identifier of the pasted code to &lt;code&gt;demo_sticker_imgly&lt;/code&gt;. Accordingly, we set the name to “img.ly” and change the file to &lt;code&gt;imgly.png&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;In addition, we want to specify a non-default tint mode for our second sticker by using &lt;code&gt;tintMode: TintMode.SOLID&lt;/code&gt; which enables us to change the color of the sticker. The &lt;code&gt;TintMode&lt;/code&gt; type is automatically added to the VideoEditor SDK imports for us by Visual Studio Code. Now, that we completed our configuration, we need to pass it as the second argument to the &lt;code&gt;openEditor&lt;/code&gt; functions in order to take effect.&lt;/p&gt;
&lt;p&gt;We save the &lt;code&gt;App.js&lt;/code&gt; file again to refresh the running apps and we can see the result live after starting a new editing session. Please note that you cannot alter the configuration of a running editor instance. You always need to start a new editing session to see configuration changes.&lt;/p&gt;
&lt;p&gt;We want to use another feature of our SDKs which is called serialization. With the serialization feature, we can capture all image and video editing operations that are applied in the editor and export them. This allows us to import the editing operations in later sessions and continue editing. The serializations are compatible between both products as well. The input serialization is the third parameter of the &lt;code&gt;openEditor&lt;/code&gt; functions of our SDKs and the output serialization is optionally part of their result type.&lt;/p&gt;
&lt;p&gt;First, we check if the result is “null”. This is the case when a user clicks the “Discard” button in the editor and thus does not export an image or video. If the result is not ”null”, we know that the user exported an image or video. Then we can assign the exported serialization to the previously defined global serialization variable which will then be input to the next editing session. We copy the code and add it to the video editor as well to enable the serialization function here too.&lt;/p&gt;
&lt;p&gt;Now, one thing is left to enable the actual serialization export in the configuration. The serialization export is disabled per default because not every user needs the serialization feature. Here, we enable it now and also change the export type to &lt;code&gt;object&lt;/code&gt;. By doing so, the result type of the editor will contain the serialization as an &lt;code&gt;object&lt;/code&gt;. Per default, the serialization is exported to a file and that file name is returned as part of the export result. Writing the serialization to a file is a reasonable default as serializations can be quite large, especially if large amounts of binary data for personal stickers are embedded.&lt;/p&gt;
&lt;p&gt;Now, we can run the app on the simulator and use all the parameters that we configured in this tutorial. First, we can add our custom stickers, both the React logo and the img.ly logo. Here we can also change the colors which we enabled with the tint mode.&lt;/p&gt;
&lt;p&gt;We can also use the text design tool to add a phrase to our image. Here we can pick different designs, so we’re trying a couple and place the text design fitting to the image and logo.&lt;/p&gt;
&lt;p&gt;Next, we export our image with the serialization. With the serialization function enabled, it is now possible to import the editing operations into our video editor. This allows us to keep on editing because the serialization is compatible between both products. So here we can add further words to our text design. We can also put filters on our video. For example, we can choose the peach duo tone and increase the contrast a little. And here we go! We successfully integrated PhotoEditor SDK and VideoEditor SDK into our React Native app.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Thanks for reading! To stay in the loop, subscribe to our&lt;/strong&gt; &lt;a href=&quot;https://photoeditorsdk.us13.list-manage.com/subscribe?u=dc9f652839dbb620d14d6d28d&amp;#x26;id=04a306e4b2&quot;&gt;&lt;strong&gt;Newsletter&lt;/strong&gt;&lt;/a&gt;&lt;strong&gt;.&lt;/strong&gt;&lt;/p&gt;</content:encoded><dc:creator>Julia</dc:creator><dc:creator>Alexander</dc:creator><media:content url="https://blog.img.ly/2020/05/_Integration_--1-.png" medium="image"/><category>App Development</category><category>Code</category><category>Developer Tools</category><category>Developer</category><category>Development</category><category>Image Editing</category><category>JavaScript</category><category>Mobile App Development</category><category>Photo Editing</category><category>React</category><category>React Native</category><category>Software Development</category><category>Tech</category><category>Tutorial</category><category>Video Editing</category><category>How-To</category><category>Company</category></item><item><title>Designing a Photo Editor</title><link>https://img.ly/blog/designing-a-photo-editor-42c9ae750783/</link><guid isPermaLink="true">https://img.ly/blog/designing-a-photo-editor-42c9ae750783/</guid><pubDate>Mon, 10 Apr 2017 12:30:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;part-1-design-as-a-team&quot;&gt;Part 1: Design as a team&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;One designer on your team, in outright conviction that the current 2677FD can not be the right choice of blue, changes the color slightly to a more saturated 2676FB, unaware he again forgot to turn off flux. The next day Lisa is infuriated. The color she loved so much now looks terrible on her display. Aziz is furious. He was working on illustrations for the product website, nitpicking the right colors based on the original blue. Oh noes, what a mess…&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Heard or seen that before? Bear with me, you´re not alone: Even though we are just a small team of three designers, the journey we had redesigning our product, the &lt;a href=&quot;https://img.ly/products/photo-sdk/&quot;&gt;PhotoEditor SDK&lt;/a&gt;, left us with numerous insights about design collaboration. Most of those lessons were learned the hard way and many of the following suggestions are techniques that matured over the last year. The methods discussed below can be applied to any team, be it two or fifty designers. So, whether your team also struggles with the inability of Sketch to allow for real collaboration or if you’re just curious about how we dealt with the struggle, read on my fellow designer!&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;final-147 product&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 2000px) 2000px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;2000&quot; height=&quot;1218&quot; src=&quot;https://img.ly/_astro/image-4_2s9801.webp&quot; srcset=&quot;/_astro/image-4_Z198ndm.webp 640w, /_astro/image-4_ZnEAi3.webp 750w, /_astro/image-4_1KTtlr.webp 828w, /_astro/image-4_Zz7Xgz.webp 1080w, /_astro/image-4_Z1iVSD3.webp 1280w, /_astro/image-4_Z1SddY2.webp 1668w, /_astro/image-4_2s9801.webp 2000w&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;use-style-guides-early-on&quot;&gt;Use style guides early on&lt;/h2&gt;
&lt;p&gt;Right after the first screen is considered finished, sit together as a team of designers and discuss colors (don’t let the developers get wind of it). Schedule the whole thing so you have enough time to get finished while the sun is still out. Otherwise you will look at the interface the next day and have a really hard time reading the ten percent opacity labels.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;Small section of our Design System.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 1140px) 1140px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1140&quot; height=&quot;920&quot; src=&quot;https://img.ly/_astro/image-5_1osUR2.webp&quot; srcset=&quot;/_astro/image-5_1Ds5hV.webp 640w, /_astro/image-5_1QkMsz.webp 750w, /_astro/image-5_2fdxFB.webp 828w, /_astro/image-5_Z1q47bs.webp 1080w, /_astro/image-5_1osUR2.webp 1140w&quot;&gt;&lt;/p&gt;
&lt;p&gt;The naming of colors/styles is crucial, they help to communicate with the developers and your fellow designers (we’ll get to that) and keeping a clean state of used styles.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;Styles&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 176px) 176px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;176&quot; height=&quot;683&quot; src=&quot;https://img.ly/_astro/image-6_1EYTEv.webp&quot; srcset=&quot;/_astro/image-6_1EYTEv.webp 176w&quot;&gt;&lt;/p&gt;
&lt;p&gt;As you can see, naming styles with a predefined set of simple rules, facilitates finding the style you are searching for. Especially since the colors that come into question for the given element are right by your selection. The prefix indicates the type of color: &lt;strong&gt;s&lt;/strong&gt; for shadow, &lt;strong&gt;n&lt;/strong&gt; for neutral, &lt;strong&gt;p&lt;/strong&gt; for primary …&lt;/p&gt;
&lt;p&gt;Why start counting at 100? Sketch doesn’t order styles by their absolute value, thus 10 comes before 1. This also gives you the flexibility to define several color ranges for one color type.&lt;/p&gt;
&lt;p&gt;If your team defines their neutral colors in a dark spectrum, bright, neutral colors can then be defined as a detached unit by starting from the next hundreds.&lt;/p&gt;
&lt;h2 id=&quot;use-grids&quot;&gt;Use Grids&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;I am from a really cozy Bavarian village, miles away from any accumulation of at least 12 houses (at least it felt that way in my youth, where the fastest transport we could use was the new lawn mower from the neighbours). To visit my friends in the next village, walking across an area of meadows, cow shit and finally woodland was the fastest path to take. There was no real pathway so everyone took a slightly different route to uber between the villages, which was just fine during the summer. However, come winter, not so much.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Every year it took us a few weeks, a lot wet socks and thus angry mom’s, to settle for one path. Ultimately, one of us took the courage to create the path of truth by preparing a trail with really tiny steps. By this time the snow usually hit the 30 cm mark.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;I encountered a similar situation 10 years later. I started in a team that did give zero fucks about any type of layout or grid. Basically, every member took his own route. Switching out a specific component was often accompanied by the change of multiple components on the project since the perception of equal spacing between each one wasn’t given anymore.&lt;/p&gt;
&lt;p&gt;Nowadays, I use grids everywhere, no matter how small the project, alone or in a team — and if you don’t already, I encourage you to start now! It makes it easier to let your creation feel coherent, speeds up the design process and helps any possible future collaborator to navigate trough and comprehend your design philosophy.&lt;/p&gt;
&lt;p&gt;*&lt;strong&gt;*Tip**&lt;/strong&gt;: choose an easy to reach shortcut for &lt;strong&gt;show grid&lt;/strong&gt; and &lt;strong&gt;show rulers&lt;/strong&gt; or make a macro to show and hide both (mine is one of the mouse buttons).&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;PhotoEditor SDK grid system&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 1700px) 1700px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1700&quot; height=&quot;1083&quot; src=&quot;https://img.ly/_astro/image-7_Z2hFL8A.webp&quot; srcset=&quot;/_astro/image-7_2vzbrV.webp 640w, /_astro/image-7_RGFzt.webp 750w, /_astro/image-7_ZnwpO1.webp 828w, /_astro/image-7_Z1XYw8G.webp 1080w, /_astro/image-7_52nrF.webp 1280w, /_astro/image-7_1mlc4U.webp 1668w, /_astro/image-7_Z2hFL8A.webp 1700w&quot;&gt;&lt;/p&gt;
&lt;p&gt;For the photo editor we chose a primary grid of 8pt and a secondary one of 4pt. All components are aligned on the primary grid and fonts on the secondary. Designing isolated components based on the grid helps to keep things evenly spaced, even if components get switched out or rearranged.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 240px) 240px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;240&quot; height=&quot;240&quot; src=&quot;https://img.ly/_astro/image-8_2vwDqS.webp&quot; srcset=&quot;/_astro/image-8_2vwDqS.webp 240w&quot;&gt;&lt;/p&gt;
&lt;p&gt;We used a lot of different icon dimensions across all platforms (iOS, Android, HTML5), to guarantee a coherent look across them. It was essential to specify a layout and predefined rules for them as well. I will discuss the creation of icons in a future post, where I will also delve into finding a style that is unique and fits your project. So stay tuned!&lt;/p&gt;
&lt;p&gt;If you haven’t started yet, create grids from the start on in every component of the project! It will probably get your feet wet at first, but your moms will be happy, I promise.&lt;/p&gt;
&lt;h2 id=&quot;keep-your-symbols-organized&quot;&gt;Keep your symbols organized&lt;/h2&gt;
&lt;p&gt;Keeping all the components ordered and close to each other is, again, great for consistency. After defining a component once in the page view, you will most likely return to the symbols page for future refinements. Now you can easily compare the component to every other used across the app. It will also make you slightly more liked by developers, something you should strive for as a designer at every time (to increase the chances of your next masonry grid not to get waived off as: “to time consuming to implement in the current release — and the release after”). Framing the cooperation between the two parties as smooth as possible is paramount at all steps of the product cycle.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;Sample of our symbols page&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 1712px) 1712px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1712&quot; height=&quot;1033&quot; src=&quot;https://img.ly/_astro/image-9_1vPF9O.webp&quot; srcset=&quot;/_astro/image-9_Z1XVScj.webp 640w, /_astro/image-9_Xxzlb.webp 750w, /_astro/image-9_Z1W4tOg.webp 828w, /_astro/image-9_lLQqV.webp 1080w, /_astro/image-9_21HnrO.webp 1280w, /_astro/image-9_LfCqH.webp 1668w, /_astro/image-9_1vPF9O.webp 1712w&quot;&gt;&lt;/p&gt;
&lt;p&gt;And also, defining grids on both, the parent artboard and the components. This way pixel imperfections and inconsistencies will be visible immediately.&lt;/p&gt;
&lt;p&gt;Also now, all elements inside a component share similar rules about placement and margins. They are easily interchangeable and allow for fast prototyping and iteration even at the final design stage.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;Gridception&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 1298px) 1298px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1298&quot; height=&quot;466&quot; src=&quot;https://img.ly/_astro/image-10_Z1FVycY.webp&quot; srcset=&quot;/_astro/image-10_16wwgv.webp 640w, /_astro/image-10_11Q90F.webp 750w, /_astro/image-10_2c6C8b.webp 828w, /_astro/image-10_VlQBi.webp 1080w, /_astro/image-10_1zCuf7.webp 1280w, /_astro/image-10_Z1FVycY.webp 1298w&quot;&gt;&lt;/p&gt;
&lt;p&gt;One of the hardest things to accomplish while working day in day out on a project like this was keeping everything neatly organized while more and more components were created and added from different parties. In the sprints before an upcoming release, we often were sloppy keeping the symbols page organized, which in turn did cost us a lot of time later on. As a solution, we created a plugin, that lints your symbols page on safe.&lt;/p&gt;
&lt;h2 id=&quot;whats-next&quot;&gt;What’s next?&lt;/h2&gt;
&lt;p&gt;The project is far from being finished and we will most likely change things here and there, building upon the methods discussed above. We are on an exciting journey crafting, designing, redefining a creative editor that is already used by hundreds of customers with millions of users. So expect more insights and learnings from our team in future articles!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Thanks for reading! To stay in the loop, subscribe to our &lt;a href=&quot;https://photoeditorsdk.us13.list-manage.com/subscribe?u=dc9f652839dbb620d14d6d28d&amp;#x26;id=04a306e4b2&quot;&gt;Newsletter&lt;/a&gt;.&lt;/strong&gt;&lt;/p&gt;</content:encoded><dc:creator>Ludwig</dc:creator><media:content url="https://blog.img.ly/2020/04/image-47.png" medium="image"/><category>Design</category><category>Sketch</category><category>Teamwork</category><category>Development</category><category>Design Process</category><category>Insights</category></item></channel></rss>