WebP cuts image file sizes by 25–35% versus JPEG at equivalent quality — critical for web performance. In .NET, ImageSharp is the most deployment-friendly option because it's 100% managed code. Magick.NET gives more fine-grained control over WebP encoding settings. The ChangeThisFile API is the right call for serverless functions or any environment where you want zero native deps.
Method 1: Magick.NET (libwebp, rich encoding options)
Magick.NET wraps ImageMagick's libwebp encoder. Full control over lossless vs lossy, quality, method (speed vs compression ratio).
dotnet add package Magick.NET-Q16-AnyCPU
using ImageMagick;
public static class JpgToWebp
{
/// <summary>Converts a JPG file to WebP with configurable quality.</summary>
public static void Convert(string inputPath, string outputPath, int quality = 80)
{
using var image = new MagickImage(inputPath);
// Method 6 = best compression (slower). Range: 0-6.
image.Settings.SetDefine(MagickFormat.WebP, "method", "6");
image.Quality = (uint)quality;
image.Format = MagickFormat.WebP;
image.Write(outputPath);
}
/// <summary>Batch convert a directory of JPGs to WebP.</summary>
public static void ConvertDirectory(string inputDir, string outputDir, int quality = 80)
{
Directory.CreateDirectory(outputDir);
foreach (var file in Directory.EnumerateFiles(inputDir, "*.jpg",
SearchOption.TopDirectoryOnly))
{
var outPath = Path.Combine(outputDir,
Path.GetFileNameWithoutExtension(file) + ".webp");
Convert(file, outPath, quality);
}
}
}
// Usage
JpgToWebp.Convert("photo.jpg", "photo.webp", quality: 80);
Quality 75–85 is the sweet spot for photographic content — visually lossless with 25–35% smaller files than JPEG. For icons and simple graphics, try lossless: image.Settings.SetDefine(MagickFormat.WebP, "lossless", "true").
Method 2: ImageSharp (pure .NET, no native deps)
ImageSharp is entirely managed .NET — no libwebp, no native binaries. Best choice for containers and cross-platform deploys that need a minimal footprint.
dotnet add package SixLabors.ImageSharp
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Formats.Webp;
public static class JpgToWebpSharp
{
public static async Task ConvertAsync(
string inputPath,
string outputPath,
int quality = 80,
CancellationToken ct = default)
{
using var image = await Image.LoadAsync(inputPath, ct);
var encoder = new WebpEncoder
{
Quality = quality,
Method = WebpEncodingMethod.BestQuality, // 0-6, higher = slower + smaller
FileFormat = WebpFileFormatType.Lossy
};
await using var outStream = File.Create(outputPath);
await image.SaveAsync(outStream, encoder, ct);
}
public static async Task ConvertBatchAsync(
IEnumerable<string> inputPaths,
string outputDir,
int quality = 80,
CancellationToken ct = default)
{
Directory.CreateDirectory(outputDir);
foreach (var path in inputPaths)
{
var outPath = Path.Combine(outputDir,
Path.GetFileNameWithoutExtension(path) + ".webp");
await ConvertAsync(path, outPath, quality, ct);
}
}
}
ImageSharp is MIT-licensed. The Six Labors Split License applies to businesses with >1M USD/year revenue — check the pricing page if your project is at that scale.
Method 3: ChangeThisFile API (HttpClient, no native deps)
POST the JPG, receive WebP. Source format is auto-detected. Free tier: 1,000 conversions/month.
# Verify with curl first
curl -X POST https://changethisfile.com/v1/convert \
-H "Authorization: Bearer ctf_sk_your_key" \
-F "file=@photo.jpg" \
-F "target=webp" \
--output photo.webp
using System.Net.Http;
using System.Net.Http.Headers;
public class ImageConvertService
{
private readonly HttpClient _http;
private const string ApiKey = "ctf_sk_your_key_here";
public ImageConvertService(IHttpClientFactory factory)
=> _http = factory.CreateClient("ctf");
public async Task<byte[]> JpgToWebpAsync(
Stream jpgStream,
string fileName,
CancellationToken ct = default)
{
using var form = new MultipartFormDataContent();
var fileContent = new StreamContent(jpgStream);
fileContent.Headers.ContentType =
new MediaTypeHeaderValue("image/jpeg");
form.Add(fileContent, "file", fileName);
form.Add(new StringContent("webp"), "target");
using var request = new HttpRequestMessage(HttpMethod.Post, "/v1/convert")
{
Content = form,
Headers = { Authorization =
new AuthenticationHeaderValue("Bearer", ApiKey) }
};
using var response = await _http.SendAsync(request, ct);
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsByteArrayAsync(ct);
}
}
When to use each
| Approach | Best for | Tradeoff |
|---|---|---|
| Magick.NET | Fine-grained WebP encoding, lossless option, existing ImageMagick pipeline | Native libwebp bundled; larger NuGet package |
| ImageSharp | Pure .NET, minimal container footprint, async-first API | Six Labors license at high revenue; slightly slower than libwebp |
| ChangeThisFile API | Serverless, no native deps at all, free tier | Network call; 25MB file limit on free tier |
Production tips
- Quality 80 is the right default. WebP at 80 is visually lossless for most photographic content and 25–35% smaller than JPEG 90. Go to 60–70 for thumbnails where pixel-peeping won't happen.
- Use async file I/O throughout. ImageSharp's async API pairs cleanly with async controller actions. Use
Image.LoadAsyncandSaveAsyncto avoid blocking thread-pool threads on I/O. - Use IHttpClientFactory for the API. Register a named client in
Program.csand injectIHttpClientFactory— avoids socket exhaustion on high-volume image endpoints. - Parallelize batch conversions with SemaphoreSlim. For batch jobs, use
Parallel.ForEachAsync(.NET 6+) or aSemaphoreSlimto cap concurrency and avoid memory pressure from too many images loaded simultaneously. - Resize before converting for web thumbnails. Both Magick.NET and ImageSharp support resize + convert in a single pass — process the image once, not twice.
ImageSharp is the cleanest drop-in for most .NET projects — no native deps, MIT license, and fully async. Magick.NET is the better choice when you need lossless WebP or already have an ImageMagick pipeline. The ChangeThisFile API eliminates both for low-to-medium volume workloads. Free tier: 1,000 conversions/month.