---
title: "Convert an image to WebP in C#"
url: https://recursiveai.org/oneimage/articles/convert-image-to-webp-csharp/
description: "Convert JPG, PNG or GIF to WebP in C# with one line of pure .NET code: quality and lossless settings, resizing, ASP.NET Core uploads, batches and animation."
date: 2026-10-22
author: OneImage team
---

# Convert an image to WebP in C#

Convert JPG, PNG or GIF to WebP in C# with one line of pure .NET code: quality and lossless settings, resizing, ASP.NET Core uploads, batches and animation.

October 22, 2026 · OneImage team

To convert an image to WebP in C#, load it and save it with a `.webp` extension. With OneImage that is one line, and it converts JPG, PNG, GIF, TIFF, BMP or ICO to lossy WebP at quality 75. It runs in pure C#, so there is no native libwebp to install and the same code runs on Windows, Linux, macOS and in containers. The rest of this page covers quality, lossless output, resizing, ASP.NET Core uploads, whole folders and animated GIFs. Every sample was run against OneImage 1.5.0 on .NET 10.

*Terminal · NuGet*

```
dotnet add package RecursiveAI.OneImage
```

## Convert JPG or PNG to WebP

*Program.cs · .NET 10*

```
using OneImage;
 
// JPEG, PNG, GIF, TIFF, BMP or ICO in; lossy WebP at quality 75 out.
Image.Load("photo.jpg").Save("photo.webp");
```

The output format comes from the file extension, and the input format is detected from the file’s content, so a PNG saved as `photo.jpg` still loads. On a 3000×3002 photograph (a NASA public-domain image, 6,511,067 bytes as JPEG) this wrote a 489,990-byte WebP at the same size.

A plain load and save keeps what the file carried: the ICC profile and the EXIF block travel into the WebP, and the orientation is applied to the pixels, with the tag reset to 1. That is right for archiving and wrong for publishing photos from phones, which can carry GPS positions. For web output, use `Image.Thumbnail`, [shown below](#resize-and-convert), which strips EXIF, XMP and IPTC.

## Set WebP quality and compression effort

Encoder settings go in `SaveOptions.EncoderOptions`:

*Program.cs · .NET 10*

```
using OneImage;
 
var options = new SaveOptions
{
    EncoderOptions = new WebPEncoderOptions { Quality = 80, Effort = 6 }
};
 
Image.Load("photo.jpg").Save("photo-q80.webp", options);
```

| `WebPEncoderOptions` | Default | What it does |
|---|---|---|
| `Quality` | 75 | Lossy quality from 0 (smallest) to 100 (best). 75 is also libwebp’s default. Ignored for lossless files. |
| `Effort` | 4 | Search effort from 0 (fastest) to 6 (smallest output). Higher values take longer to encode. |
| `Lossless` | `false` | Write lossless (VP8L) data instead of lossy (VP8). |
| `Exact` | `true` | Keep the color values of fully transparent pixels. Set it to `false` to let the encoder replace them for better compression. |

On the same photograph, quality 80 at effort 6 wrote 598,152 bytes, against 489,990 at the defaults. Quality costs bytes quickly. Compare a few settings on your own images and pick the lowest one that looks right at the size you display.

## Lossless WebP for logos, screenshots and transparency

Lossy WebP suits photographs. For logos, UI screenshots and line art, where sharp edges and exact colors matter, write lossless WebP:

*Program.cs · .NET 10*

```
using OneImage;
 
Image.Load("logo.png").Save("logo.webp", new SaveOptions
{
    EncoderOptions = new WebPEncoderOptions { Lossless = true }
});
```

Our 600×300 test logo with a transparent background went from a 2,346-byte PNG to a 720-byte WebP. Decoded again, every pixel matched the PNG, alpha channel included. Transparency survives lossy WebP too: lossy files store the alpha channel losslessly.

## Resize and convert to WebP in one step

Most WebP conversions feed a web page, so the image should also be smaller, upright and free of private metadata. `Image.Thumbnail` does all of that:

*Program.cs · .NET 10*

```
using OneImage;
 
// At most 1200 px wide, upright, sRGB, EXIF and GPS removed, saved as WebP.
Image.Thumbnail("photo.jpg", 1200).Save("photo-1200.webp");
```

In order, it applies the [EXIF orientation](https://recursiveai.org/oneimage/articles/exif-orientation-csharp/), resizes (never enlarging unless you pass `upscale: true`), converts to sRGB with a compact embedded profile and removes EXIF, XMP and IPTC. The 6.5 MB photograph became a 1200×1201 WebP of 136,772 bytes with no EXIF block. For JPEG input it decodes at 1/2, 1/4 or 1/8 scale when that is enough, so a large photo is never held in memory at full size. Pass a height and a `fit:` (`Fit.Cover`, `Fit.Pad` and others) for fixed boxes. For widths, boxes and crops without WebP, see [resize an image in C#](https://recursiveai.org/oneimage/articles/resize-image-csharp/).

## Convert uploads to WebP in ASP.NET Core

The same calls work on streams. This minimal API endpoint takes an uploaded image and returns a WebP:

*Program.cs · ASP.NET Core · .NET 10*

```
using OneImage;
 
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
 
app.MapPost("/images", async (IFormFile file) =>
{
    try
    {
        await using Stream upload = file.OpenReadStream();
        Image image = Image.Thumbnail(upload, 1600); // upright, sRGB, no EXIF/GPS
 
        var webp = new MemoryStream();
        await image.SaveAsync(webp, WebPFormat.Instance);
        webp.Position = 0;
        return Results.File(webp, "image/webp");
    }
    catch (ImageException ex) // not an image, damaged, or over the size limits
    {
        return Results.BadRequest(ex.Message);
    }
}).DisableAntiforgery();
 
app.Run();
```

A stream has no extension, so `SaveAsync` names the format with `WebPFormat.Instance`, and you can pass `SaveOptions` as a third argument for quality. `Thumbnail` copies the compressed bytes when it reads a stream, so the upload stream isn’t needed afterwards. Posting our 3000×3002 JPEG returned `200`, `image/webp`, 1600×1601, 204,576 bytes. Posting a text file returned `400` with “The data is not in a recognized image format.” Size limits are on by default and checked from the image headers before large buffers are allocated, which matters for files you didn’t create. The [guide](https://recursiveai.org/oneimage/docs/guide/#limits) shows how to change them.

If you would rather resize on request than at upload time, the separate [OneImage.Web](https://recursiveai.org/oneimage/docs/web/) package is middleware that serves resized images from URL commands, including the output format, with a disk cache.

## Batch convert a folder to WebP

*Program.cs · .NET 10*

```
using OneImage;
 
var options = new SaveOptions { EncoderOptions = new WebPEncoderOptions { Quality = 80 } };
 
foreach (string path in Directory.GetFiles("uploads"))
{
    if (Path.GetExtension(path).Equals(".webp", StringComparison.OrdinalIgnoreCase))
        continue;
 
    if (!Image.TryLoad(path, out Image? image))
    {
        Console.WriteLine($"skipped {Path.GetFileName(path)}: not a supported image");
        continue;
    }
 
    string target = Path.ChangeExtension(path, ".webp");
    image.Save(target, options);
    Console.WriteLine($"{Path.GetFileName(path)} -> {Path.GetFileName(target)}, {new FileInfo(target).Length / 1024} KiB");
}
```

`TryLoad` returns `false` for files that aren’t images or that exceed the size limits, so one bad file doesn’t stop the batch. Skipping existing `.webp` files keeps a second run from converting its own output. On a folder with our JPEG, a PNG, the logo and a text file it printed:

*Output · *

```
skipped notes.txt: not a supported image
c.png -> c.webp, 3 KiB
b.png -> b.webp, 331 KiB
a.jpg -> a.webp, 604 KiB
```

`Image` objects are immutable and safe to share between threads, so `Parallel.ForEach` over the files works when you have many. Each `Save` already uses up to four threads by default.

## Convert an animated GIF to animated WebP

*Program.cs · .NET 10*

```
using OneImage;
 
Image gif = Image.Load("spinner.gif");
gif.Save("spinner.webp"); // every frame, with its delay and the loop count
 
Console.WriteLine($"{gif.Frames.Count} frames written");
```

WebP, PNG (as APNG), GIF and TIFF keep all frames, while JPEG and BMP write only the first. Our 12-frame test GIF came out as a 12-frame WebP with the same 80 ms delays and infinite loop. Don’t assume the file shrinks: this small flat-color animation grew from 4,642 bytes as GIF to 8,080 as lossy WebP. Results depend on the content, so compare lossy, lossless (`Lossless = true`) and the original GIF on your own files.

## Check the result

`Image.Identify` reads only the headers, which makes it a cheap way to confirm a conversion in a test or a health check:

*Program.cs · .NET 10*

```
using OneImage;
 
ImageInfo info = Image.Identify("photo.webp");
Console.WriteLine($"{info.Format.Name}, {info.Width}x{info.Height}");
```

For the first example it prints `webp, 3000x3002`.

## How fast is WebP encoding in pure C#?

In OneImage’s published benchmarks, converting a PNG to lossy WebP at quality 75 took 79.1 to 95.8 ms, depending on the image content. ImageSharp 3.1.12 took 236.5 to 1,511.5 ms for the same task, and the fastest native wrapper (NetVips or Magick.NET) took 155.1 to 162.3 ms. Lossy WebP encoding of a 2000×1500 image took 101.6 ms against ImageSharp’s 590.5 ms. These are BenchmarkDotNet means on one shared workstation (AMD Ryzen 9 7950X3D, .NET 10), so treat them as local measurements rather than promises for your hardware. The [performance page](https://recursiveai.org/oneimage/docs/performance/) has the full table, memory use and method.

## Other ways to write WebP in .NET

- **ImageSharp** has a [WebP encoder](https://docs.sixlabors.com/articles/imagesharp/webp.html). Its 3.2.0 and 4.x releases need a Six Labors license key to build in Release, and closed-source use by larger businesses needs a paid license. Our [ImageSharp license guide](https://recursiveai.org/oneimage/articles/imagesharp-license/) has the details.
- **SkiaSharp and Magick.NET** encode WebP through native libraries that ship per platform. See our [ImageSharp alternatives](https://recursiveai.org/oneimage/articles/imagesharp-alternatives/) comparison.
- **Shelling out to `cwebp`** works, but you then deploy and patch a separate binary and manage processes and temporary files.

OneImage requires .NET 10. For what else it reads and writes, see the [formats](https://recursiveai.org/oneimage/#formats) on the overview page.
