Sr. Content Developer at Microsoft, working remotely in PA, TechBash conference organizer, former Microsoft MVP, Husband, Dad and Geek.
161537 stories
·
33 followers

Blurry before beautiful: image previews for the web

1 Share

You've probably already seen this common pattern in action: blurry versions of the images of a site appear immediately and, moments later, their full versions replace them.

This pattern has become so common that many image CDNs, frameworks, and libraries support it out of the box. But every implementation has to do the same thing:

  • Fetch both the preview and the final images.
  • Display the preview while the final image loads.
  • Swap the preview and final images.
  • Clean up after the transition.

Below is an example of the effect, which shows a blurry preview image. Click the button below to load and display the final image:

The Louvre pyramid in Paris.

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum volutpat accumsan tristique. Phasellus sit amet mauris odio. Aenean urna felis, laoreet vel ipsum ut, dignissim fermentum mi. Fusce posuere efficitur laoreet. Donec lacinia massa cursus eros ultrices maximus. Proin sit amet vestibulum nibh. Nulla luctus eleifend nisl. Aliquam vel orci a nisl maximus bibendum ut sit amet turpis. Curabitur sodales risus placerat nulla congue bibendum. Aliquam ut mi et dui efficitur semper. Donec tempus nibh eget est aliquet bibendum. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas.

We are proposing to make this a built-in capability of the web, and we'd love to know if this is worth pursuing and what the exact scope should be.

Before diving into the proposal, let's take a look at how this pattern is typically implemented today.

How to do this today?

There's currently no standard way to provide a preview for an image, so the solution depends on the framework or library you're using.

For example, Next.js can generate a blurred placeholder when an image is imported, by using the placeholder=blur property:

import mountain from "./mountain.jpg";

<Image src={mountain} alt="A mountain" placeholder="blur" />

Libraries such as blurhash encode a tiny preview as a short string, which your application can then decode to show the preview while the real image is loading:

// First, create blurhash strings for your images.
import { encode } from "blurhash";
async function encodeImageToBlurhash(width, height, imagePixelData) {
  return encode(imagePixelData, width, height, 4, 4);
};
// At runtime, while the image is loading, decode the string to display a preview.
import { decode } from "blurhash";
const pixels = decode("<the blurhash string>", 32, 32);
const canvas = document.createElement("canvas");
const ctx = canvas.getContext("2d");
const imageData = ctx.createImageData(width, height);
imageData.data.set(pixels);
ctx.putImageData(imageData, 0, 0);
document.body.append(canvas);

You can also use write your own custom implementation, for example by creating image previews at build-time, and sending them as data URLs used in CSS background images, while the final image is loading:

<div class="image" style="background-image: url(data:image/png;base64,....)">
  <img src="image.jpg" alt="A mountain">
</div>

These approaches differ, but they all have to coordinate the preview and final image themselves.

Why a native solution?

This preview effect is well established, and for good reasons. It allows your UI to come to life much sooner, giving the impression of a more responsive site which lets users access the rest of the content while images are still loading.

We don't want to change this pattern, but instead implement it in the browser so that you don't need to write, maintain, and run as much code. With a solution that's built-in, you let the browser handle most of the complexity.

What's in the proposal?

For now, our proposal focuses only on loading, displaying, and replacing the preview image. To achieve this, we propose to introduce a new attribute for the <img> element called previewsrc:

<img previewsrc="tiny-blurry-preview.png" src="full-image.avif">

With the previewsrc attribute, the browser would:

  • Load the preview and final images, always giving priority to the final image.
  • Display the preview.
  • Replace the preview with the final image when it becomes ready.

This already simplifies the process of handling image previews a lot because you don't need to use or write code to load the preview, handle cases where the preview doesn't exist or fails to load before the final image is ready, and handle the image swap yourself.

We'd love your feedback on this initial proposal: do you consider this a valuable addition to the web platform? Is it enough on this own for you to use it?

Possible future enhancements

If you're using a library or framework to handle previews today, your solution might also support the following features:

  1. Customize the transition between the preview and the final image, for example by adding a fade effect, to make the swap look nicer.
  2. Support compact formats such as blurhash directly, without needing to write additional code.

Our initial proposal doesn't support these features. When the final image becomes ready to paint, the browser directly replaces the preview with the final image, with no customizable transition. Also, it doesn't natively support compact formats such as blurhash.

We're currently considering them as optional enhancements that could be added in the future, but your feedback on their importance would help us prioritize them: would you use previewsrc alone or would you require support for these optional enhancements (and if so, which ones) before adopting it?

Let us know!

Let us know how you feel about this proposal.

Is the problem worth solving at the web platform level? What would consitute the minimal viable solution for you to adopt the API?

Please send your feedback by opening a new issue on our GitHub repository.


Common questions

You might be wondering about the following questions, so let me provide some answers below.

Doesn't a preview image create a second request?

Sure, if you set previewsrc to another image URL, then an additional network request will be made, which potentially will consume bandwidth. However, consider this:

  • The final image loading would never be delayed by the preview.
  • The browser would always treat preview loading as "best effort".
  • The browser would assign a lower priority to preview fetches and decodes.
  • The browser would, in fact, completely skip previews when they are unlikely to be useful.
  • And you could still continue to use blurhash strings or data URLs for previews, avoiding a second network request altogether.

It's important top keep in mind that this is not a replacement for proper image optimization, and not a performance feature.

Does this avoid abuse?

It doesn't. There is actually no enforcement mechanism in the proposal for ensuring that developers use the previewsrc attribute responsibly.

The proposal depends on developer discipline, the same discipline they apply when using existing image preview techniques.

However, this is important: the browser may skip the preview altogether if it determines that it is unlikely to be useful before the final image is loaded or if resources are constrained. That's definitely a plus compared to today's solutions.

Why not use progressive image delivery instead then?

JPEG images can be progressively delivered, whereby the image is loaded in multiple passes, gradually increasing in quality. This allows users to see a low-quality version of the image almost immediately, while waiting for the full-quality image to load.

In the end, this depends on your use case. A progressive JPEG may be preferable for sites where more fidelity is required, where seeing the real image, even if in low quality, is more important than having an instant, but very blurry preview.

Generally speaking, blurred placeholders seem to be favored for providing a quick visual indication, responsiveness, and layout stability. I feel like developers have mostly moved away from progressive images.

But also, not all sites generate progressive image versions at all.

What about responsive images?

They keep working as before.

An <img> inside a <picture> element can still use the previewsrc attribute. The existing <picture>, srcset, and sizes algorithms continue selecting the final image.

Read the whole story
alvinashcraft
23 seconds ago
reply
Pennsylvania, USA
Share this story
Delete

Swagger vs. Scalar: A comprehensive comparison for ASP.NET Core developers

1 Share

This blog post is originally published on https://blog.elmah.io/swagger-vs-scalar-a-comprehensive-comparison-for-asp-net-core-developers/

Swagger UI used to come bundled with every new ASP.NET Core Web API project. Recent .NET versions replaced it with a bare OpenAPI document, leaving you to pick your own documentation UI, and Scalar has become one of the most popular choices. This post compares Swagger and Scalar, and walks through setting up each one in .NET 10.

Swagger vs. Scalar: A comprehensive comparison for ASP.NET Core developers

You should test APIs before giving them to the frontend team. API testing and documenting them together is a tedious task, especially if the application is large enough. Many people prefer Postman documentation. However, there are graphical representations too that require a little less effort than documenting and testing your APIs. Swagger has been a long-time partner in interactive API documentation. The .NET 8 API template included Swashbuckle by default to provide both the OpenAPI JSON and the Swagger UI. However, later versions have brought novelty. The API template now has a default OpenAPI document, which developers typically pair with modern UI tools like Scalar.

Difference between Swagger and Swagger UI

There is a lot of confusion about the exact separation, even though I myself used to think they are the same. Swagger is an open-source ecosystem of tools developed by SmartBear. It provides tools for designing, building, and testing APIs using the OpenAPI specification.

  • Swagger UI: A web-based interface consuming the OpenAPI document. It allows developers to visualize and test endpoints.
  • Swagger Editor: A visual OpenAPI editor that allows writing and editing in YAML or JSON formats in a user-friendly manner.
  • Swagger Codegen: An engine to generate server stubs and client SDKs in 40+ languages directly from OpenAPI definitions.
  • SwaggerHub: A commercial platform for API design, collaboration, and governance of enterprise-grade API lifecycle management.

Why did Microsoft remove Swashbuckle from its default template?

Swashbuckle was a long-time companion of the ASP.NET Core standard and Microsoft's default for OpenAPI documentation. However, declining maintenance and departing contributors led to issues, and it had poor .NET 8 support. Microsoft removed it from default templates in .NET 9. In .NET 10, the third-party UI is no longer bundled by default in new Web API templates. Hence, .NET prioritizes developer experience and modernity by removing Swagger UI by default and allowing developers to opt for any other library that supports an OpenAPI document. Note that Scalar didn't replace OpenAPI; it provides a modern documentation UI for visualizing and interacting with your OpenAPI description.

Implementing Swagger UI and Scalar in ASP.NET Core API

Here's how each one is implemented in ASP.NET Core.

Step 1: Create project

dotnet new webapi SwaggerVsScalar
cd SwaggerVsScalar

As I am working on .NET 10, the default Program.cs will look.

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
// Learn more about configuring OpenAPI at https://aka.ms/aspnet/openapi
builder.Services.AddOpenApi();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.MapOpenApi();
}

app.UseHttpsRedirection();

var summaries = new[]
{
    "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};

app.MapGet("/weatherforecast", () =>
    {
        var forecast = Enumerable.Range(1, 5).Select(index =>
                new WeatherForecast
                (
                    DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
                    Random.Shared.Next(-20, 55),
                    summaries[Random.Shared.Next(summaries.Length)]
                ))
            .ToArray();
        return forecast;
    })
    .WithName("GetWeatherForecast");

app.Run();

record WeatherForecast(DateOnly Date, int TemperatureC, string? Summary)
{
    public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
}

As you can see, OpenAPI support is built in by default.

Add Swagger UI

As we already have an endpoint, we can display it in Swagger.

Step 2: Add the package

dotnet add package Swashbuckle.AspNetCore

Step 3: Configure Program.cs

using Microsoft.OpenApi;

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddOpenApi();


builder.Services.AddSwaggerGen(options =>
{
    options.SwaggerDoc("v1", new OpenApiInfo
    {
        Title = "Swagger Demo API",
        Version = "v1",
        Description = "Demo API for comparing Swagger and Scalar"
    });
});

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.MapOpenApi();
    app.UseSwagger();

    app.UseSwaggerUI(options =>
    {
        options.SwaggerEndpoint("/swagger/v1/swagger.json", "Swagger Demo API v1");
    });
}

app.UseHttpsRedirection();

var summaries = new[]
{
    "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};

app.MapGet("/weatherforecast", () =>
    {
        var forecast = Enumerable.Range(1, 5).Select(index =>
                new WeatherForecast
                (
                    DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
                    Random.Shared.Next(-20, 55),
                    summaries[Random.Shared.Next(summaries.Length)]
                ))
            .ToArray();
        return forecast;
    })
    .WithName("GetWeatherForecast");

app.Run();

record WeatherForecast(DateOnly Date, int TemperatureC, string? Summary)
{
    public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
}

Swashbuckle.AspNetCore provides AddSwaggerGen() to register a service in the generation of the OpenAPI document, while SwaggerDoc specifies that an OpenAPI document called v1 should be generated using this metadata.

app.UseSwagger() makes the generated OpenAPI document available through HTTP and accessible at /swagger/v1/swagger.json.

The app.UseSwaggerUI is the actual Swagger UI and creates a web interface reading the document via the given path /swagger/v1/swagger.json. Finally, it displays it as an interactive API documentation page.

Step 4: Run the project

dotnet run

The Swagger UI is visible.

Swagger

Add Scalar UI

Step 2: Add the package

dotnet add package Scalar.AspNetCore

Step 3: Configure program file

using Scalar.AspNetCore;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddOpenApi();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.MapOpenApi();
    app.MapScalarApiReference();
}

app.UseHttpsRedirection();

var summaries = new[]
{
    "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};

app.MapGet("/weatherforecast", () =>
    {
        var forecast = Enumerable.Range(1, 5).Select(index =>
                new WeatherForecast
                (
                    DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
                    Random.Shared.Next(-20, 55),
                    summaries[Random.Shared.Next(summaries.Length)]
                ))
            .ToArray();
        return forecast;
    })
    .WithName("GetWeatherForecast");

app.Run();

record WeatherForecast(DateOnly Date, int TemperatureC, string? Summary)
{
    public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
}

app.MapScalarApiReference() registers Scalar's API documentation UI as an endpoint in the ASP.NET Core application. Navigating to /scalar will open the Scalar interface by reading the document exposed by app.MapOpenApi().

Step 4: Run the project

dotnet run

Now, the UI is accessible.

Scalar UI

In the top-left corner is the menu that displays all endpoints using the OpenAPI document.

Scalar UI

Scroll down to where the endpoints are displayed so we can test one.

Endpoint

Let's test by clicking the Test Request button.

Calling API

Here's the response I got.

Response
Response

Attach OpenAPI metadata to the endpoint

You can add more details to the endpoint for the OpenAPI document.

app
    .MapGet("/weatherforecast", () =>
    {
        var forecast = Enumerable.Range(1, 5).Select(index =>
                new WeatherForecast
                (
                    DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
                    Random.Shared.Next(-20, 55),
                    summaries[Random.Shared.Next(summaries.Length)]
                ))
            .ToArray();
        return forecast;
    })
    .WithName("GetWeatherForecast")
    .WithSummary("weather data for the state")
    .WithDescription("This endpoint returns weather report for different counties.")
    .WithTags("My place")
    .Produces(200, typeof(object))
    .Produces(400)
    .ProducesProblem(500)
    .RequireAuthorization();

WithSummary provides a short summary for the endpoint, while WithDescription adds a longer description. WithTags categorizes endpoints, while the Produces fluent methods define successful and failure responses.

Scalar API

Import the Specification into Postman

The raw specification will be accessible at /openapi/v1.json, which is http://localhost:5282/openapi/v1.json in our case.

API JSON

Postman can automatically build request collections from this native endpoint using the following steps:

  1. Open Postman and select the Import button in the top-left sidebar.
  2. Either import via Link: Choose the Link tab and paste the local URL.
  3. Or via File: Save the JSON from the browser, then drag the file into the Postman import window.
Import

Our API document is imported.

Postman

Add a descriptive document with API controller

Now, we've seen how the minimal API looks with Scalar. Although for Swagger, you may already know what it looks like with controllers. I will show how Scalar works with the API.

Step 1: Create model

namespace SwaggerVsScalar.Models;

public class Product
{
    public int Id { get; set; }
    public string Name { get; set; } = string.Empty;
}

Step 2: Add controller

using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using SwaggerVsScalar.Models;

[ApiController]
[Route("api/[controller]")]
[Tags("Products")]
public class ProductsController : ControllerBase
{
    private readonly List<Product> _products;

    public ProductsController()
    {
        _products = new List<Product>
        {
            new Product { Id = 1, Name = "Matcha" },
            new Product { Id = 2, Name = "Cappuccino" }
        };
    }

    [HttpPost]
    [EndpointName("CreateProduct")]
    [EndpointSummary("Creates a new product")]
    [EndpointDescription("This endpoint creates a new product and returns the created product.")]
    [ProducesResponseType(typeof(Product), StatusCodes.Status201Created)]
    [ProducesResponseType(StatusCodes.Status400BadRequest)]
    public IActionResult CreateProduct(Product input)
    {
        _products.Add(input);

        return CreatedAtAction(
            nameof(GetProduct),
            new { id = input.Id },
            input);
    }

    [HttpGet]
    [EndpointName("GetProducts")]
    [EndpointSummary("Gets all products")]
    [EndpointDescription("This endpoint returns all available products.")]
    [ProducesResponseType(typeof(IEnumerable<Product>), StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status400BadRequest)]
    public IActionResult GetProducts()
    {
        return Ok(_products);
    }

    [HttpGet("{id}")]
    [EndpointName("GetProduct")]
    [EndpointSummary("Gets a product by ID")]
    [EndpointDescription("This endpoint returns a product matching the specified ID.")]
    [ProducesResponseType(typeof(Product), StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status404NotFound)]
    public IActionResult GetProduct(int id)
    {
        var result = _products.FirstOrDefault(x => x.Id == id);

        if (result is null)
            return NotFound();

        return Ok(result);
    }
}

Here, we have a controller with some in-memory values. A noticeable addition is the description attributes [EndpointName], [EndpointSummary], [EndpointDescription], and [ProducesResponseType], which are alternatives to the fluent API methods we used with the minimal API.

Step 3: Configure Program.cs

There won't be much change from our previous setup. We just need to inject controllers so the API controllers get mapped. The final file looks like this:

using Scalar.AspNetCore;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddOpenApi();

builder.Services.AddControllers();
var app = builder.Build();
app.MapControllers();

if (app.Environment.IsDevelopment())
{
    app.MapOpenApi();
    app.MapScalarApiReference();
}

app.UseHttpsRedirection();

app.Run();

After running the project, we get the following result.

Scalar UI

Each endpoint looks like this.

Create

Get All.

Get All

GetById method.

GetById

Finally, we can see the model.

Models

When to pick Swagger and when to pick Scalar?

We have to peel back the main discussion point: if Swagger is legendary and Scalar is modern, when to choose which? Scalar offers a modern developer experience with its search-oriented interface. Scalar specifically highlights responsive UI, dark mode, navigation, and improved handling of large APIs. Its built-in sidebar navigation for instant search is an advantage over the standard Swagger UI experience. Moreover, Scalar maintains request history, environment variables, code snippets and an advanced request configuration. You can customize themes with a wide range of options available by default. Such features make it a competitive choice for modern applications, a choice earlier competitors lacked.

Swagger is a mature and trusted option used by legacy systems. It has been powering APIs since 2011 and has earned developers' familiarity. It is simple and ideal for users who want to "Try it out". For a small and simple API project, Swagger suits better without adding a new tool to navigate and learn. Besides, an existing system that already uses Swagger shouldn't rush to switch to a new option, where the team may get tangled with learning another tool.

Conclusion

Scalar is a transformation in API documentation. For a long time, Swagger has been the default option for ASP.NET Core APIs that were developed using the template. However, in recent versions, Microsoft has removed the default Swagger and provided an OpenAPI document by default. The NuGet store offers several options for consuming the document, among which Scalar has emerged as a futuristic solution. Its features range from customized UI themes to search-friendly navigation. In this post, we discussed in depth the differences between Swagger and Scalar. We implemented each of them in .NET 10 to see the actual visual differences between them.

Code: https://github.com/elmahio-blog/SwaggerVsScalar



Read the whole story
alvinashcraft
56 seconds ago
reply
Pennsylvania, USA
Share this story
Delete

.NET 11 for Duende IdentityServer Developers

1 Share
.NET 11 and C# 15 will be released this November and with it comes changes, updates, and new features. Of course every new update brings the same familiar question, what changes for the software I maintain? For developers of Duende IdentityServer applications, the short answer is, not a whole lot. Like every year, there are some features to think through and test when choosing to upgrade.
Read the whole story
alvinashcraft
1 minute ago
reply
Pennsylvania, USA
Share this story
Delete

The Rise of Jev: A Leap Beyond Traditional Large Language Models

1 Share

In a landscape where AI development seems to evolve at breakneck speed, a new contender has entered the ring—changing the game in unexpected ways. Meet Jev, a groundbreaking approach to artificial intelligence that defies convention, created by ex-OpenAI researcher Diogo Almeida and his company, Typesafe AI.

Jev distinguishes itself by removing language capabilities entirely, focusing instead on rapid classification. Almeida’s vision led this development, seeking to address what he saw as the primary shortcoming of large language models—their verbose responses and proclivity for hallucination.

Based on content from Fireship

Traditional models are known for their eloquent yet sometimes incorrect answers, occasionally veering off into uncharted territories with their verbosity. Jev’s radical approach is to omit the linguistic element entirely, resulting in improved efficiency and cost-effectiveness. The model runs approximately 200 times faster and at a fraction—400 times cheaper—of the cost of its linguistic predecessors.

The stark difference lies in Jev’s design principle: it operates more like a type-safe language, providing results that are more structured and reliable. This method, derived from Daniel Kahneman’s “System 1” fast-thinking framework, promotes instinctive decision-making—distinguishing Jev from the slow, deliberative processing seen in traditional “System 2” AI models.

For developers and businesses, Jev’s introduction signifies a new frontier. It’s particularly suited for applications requiring quick, computationally cheap decisions. Notably, developers have integrated Jev for real-time operations such as NPC behaviors in gaming and even AI calculators. The model’s robust performance, backed by what’s referred to as RLCD (Reinforcement Learning for Calibrated Decisions), ensures each output is paired with a “calibrated confidence number,” enhancing reliability while minimizing incorrect outputs.

With Typesafe AI recently securing $40 million in funding, Jev’s potential seems limitless. Yet, the precise workings of Jev’s architecture remain a closely guarded secret, with the promise of future research publications to illuminate its inner workings. Despite its potential, some critics point out similarities with earlier zero-shot classifier work, challenging the novelty they claim Jev brings to the table.

Ultimately, Jev represents an exciting leap for artificial intelligence, providing developers with an innovative tool to enhance application functionality while reducing both time and costs. As we wait for more detailed insights into Jev’s architecture and capabilities, it stands as a compelling example of creative thinking in AI, shaking up how we approach evolving technological challenges.

Stay tuned for more AI developments and don’t hesitate to explore Mux—our video API partner—who’s redefining video features integration with their dynamic and customizable solutions.

Read the whole story
alvinashcraft
1 minute ago
reply
Pennsylvania, USA
Share this story
Delete

XAML.io Lets You Build .NET Apps in a Web Browser

1 Share

XAML.io is an impressive web-based software development IDE that lets you build C#/XAML .NET apps using a prompt.

The post XAML.io Lets You Build .NET Apps in a Web Browser appeared first on Thurrott.com.

Read the whole story
alvinashcraft
6 hours ago
reply
Pennsylvania, USA
Share this story
Delete

Microsoft keeps rebuilding Windows for developers, but a new poll puts Windows at just 12%

1 Share

Apart from fixing their OS, 2026 for Microsoft was spent making Windows more attractive to developers. WSL got faster, Linux containers are now a built-in part of Windows 11, Microsoft shipped Linux-like Coreutils, and the more recent of them, Windows Developer Configurations, can turn a fresh PC into a ready-to-code machine with one command.

However, a new developer poll, spotted by Windows Latest, looks to render those efforts futile. Gergely Orosz, who runs The Pragmatic Engineer, asked his audience on X about which OS they use to build software, and got roughly 4,000 responses. macOS took a staggering 61.6%, Linux 24.2%, and Windows 12.8%

Poll on what OS developers are using to build software by Gergely Orosz
Poll on what OS developers are using to build software by Gergely Orosz. Source: Screenshot from X

Yes, Windows came in a distant third. Another poll on LinkedIn for research for the same newsletter received 6000 votes, with the numbers being 66%, 20%, and 12%, which is similar as the ones on X.

Poll numbers collected by The Pragmatic Engineer newsletter about software used by developers
Poll numbers collected by The Pragmatic Engineer newsletter about software used by developers. Source: LinkedIn

Of course, this isn’t a survey of every developer in the world. Orosz described the audience as his “bubble,” made up largely of developers at startups and Big Tech. It does not overturn the much larger surveys where Windows is still widely used.

But I feel this is an uncomfortable situation for Microsoft. Redmond is doing so much to make Windows developer-friendly, and yet the developers are still reaching for Macs and Linux boxes.

The 12% number for Windows is more of a preference than a market share

The 2025 Stack Overflow Developer Survey, with tens of thousands of respondents globally, brings us back to the real world. It found Windows to be the most widely used primary operating system among professional developers, at roughly 49.5%, compared with 32.9% for macOS.

And it being a multiple selections survey, makes it fundamentally different from Orosz’s single-choice poll. You cannot put 12.8% next to 49.5% and declare one of them wrong.

Either way, this gap between the two surveys suggests Windows is particularly weak among the highly visible startup and Big Tech crowd, even though it is still widely used across the global developer population.

Gergely Orosz about OS market share

The Statcounter chart for OS market share is misleading

The screenshot posted by Omarchy creator, which was what triggered Orosz to explain his poll, shows Statcounter’s US desktop chart with Linux near 18% and Windows above 50%.

StatCounter-os_combined-US-monthly-202508-202608
Source: Statcounter

Well, Statcounter measures page views, not people or developer machines, although its methodology says it tries to remove bot activity from a sample of billions of monthly page views.

Also, Statcounter still lists OS X and macOS as separate operating systems, years after Apple renamed OS X to macOS. Windows Latest has flagged this kind of misreading before, when a viral screenshot claimed Windows had dropped from 79% to 56% in two months. It turned out to be a classification error that Statcounter later corrected, not an exodus from Windows.

We also checked a viral news story about a spike in Linux against Cloudflare’s human versus automated traffic data and found Linux at around 4.7% for human-only North American traffic, while automated traffic pushed it as high as 26% on individual days.

Why do developers keep choosing macOS and Linux?

One word: Unix

Yes, MacBooks have their charm, but more importantly, macOS gives developers a Unix-based environment with familiar command-line tools and package managers, wrapped in a desktop that mostly stays out of the way, without bloatware and upsells. It also helps that the M-series chips are the best in class at performance and battery life.

MacBook Pro showing code for an app using Visual Studio Code
Source: Apple

There is also Xcode that has always required macOS, and it provides the only SDKs and simulators for iOS, iPadOS, watchOS, and visionOS. For anyone building Apple-platform software, choosing a Mac is the only option. And if X posts are anything to go by, Apple users are more likely to pay for apps, and hence give more revenue to the developers.

Linux is closer to where modern software runs today

Developers working with servers, containers, cloud infrastructure, and plenty of AI workloads want their development environment to resemble the Linux environments where their software eventually ships, which explains why Linux is preferred around Docker, Kubernetes, Python, and cloud infrastructure.

Microsoft’s WSL support documents mention Linux environments, containers, and GPU acceleration, while also warning that storing project files on the Windows filesystem while running Linux build tools through WSL introduces I/O overhead.

Windows is often very good at the things Microsoft controls

Windows still has enormous advantages. Visual Studio is still one of the dominant development environments, .NET runs officially across Windows, Linux, and macOS, and Windows keeps a huge enterprise footprint along with clear dominance in PC gaming.

WSL gives developers Linux access without abandoning Windows completely, and Microsoft has poured a lot of resources into Dev Drive, Windows Terminal, WinGet, and developer tooling.

The problem is that many developers do not want Windows because they need Windows. They want a Unix-like environment, and Microsoft has increasingly responded by putting Linux inside Windows.

Microsoft is practically rebuilding Windows around developers and AI

Steve Ballmer once screamed “developers, developers, developers” across a stage. It worked then, but two decades later, Microsoft is still chasing them, though the problem can’t be any more different.

WSL is no longer Microsoft’s side project

Microsoft has spent 2026 upgrading WSL with faster file access, better networking, and easier setup, describing it as foundational for running Linux workloads on Windows, work that led to WSL Containers, a built-in wslc tool for building and running Linux containers that removes the need for third-party tools like Docker Desktop.

WSL Containers in action on Windows 11
WSL Containers in action on Windows 11. Credit: Windows Latest

Funnily enough, Canonical’s VP of Engineering, Jon Seager, told The Pragmatic Engineer that Ubuntu usage inside WSL is growing faster than native Ubuntu desktop installs, and expects WSL to overtake native Ubuntu within months, driven largely by developers handed Windows laptops by their employers who then need Linux for AI and machine learning work.

Ubuntu running via Windows Subsystem for Linux
Ubuntu running via Windows Subsystem for Linux. Source: Ubuntu

Microsoft built WSL so developers would not have to leave Windows. It may be succeeding in one sense, but the fact that Microsoft had to do this at all tells us exactly what problem it was solving!

Build 2026 went full-throttle into developer tooling

At Build 2026, Microsoft pledged to make Windows 11 the OS for building AI, shipping Coreutils for Windows (a Rust-based reimplementation of GNU command-line utilities that run natively on Windows), along with Windows Developer Configurations, powered by WinGet, that set up VS Code, GitHub Copilot, WSL, and PowerShell 7 with one command.

GitHub Copilot

The same wave included Windows Development Skills for agentic native app development, an experimental Intelligent Terminal, and Microsoft Execution Containers, a policy-driven layer that lets developers control what an AI agent can touch on a device.

Project Zenith admits AI developers need a quieter Windows

Announced September 4, 2026, Project Zenith is Windows with different defaults, requiring at least 64GB of unified memory and 250GB/s of bandwidth so it can run 30-billion-parameter coding models locally.

Microsoft calls it “ready-to-code,” pinning Windows Terminal and VS Code to the taskbar and preinstalling GitHub Copilot, PowerToys, and Windows Dev Skills.

Project Zenith

Microsoft is also throwing AI-focused hardware to lure in developers. The Surface RTX Spark Dev Box and the Surface Laptop Ultra both pair NVIDIA RTX Spark with large unified-memory configurations and CUDA support, aimed squarely at local AI development. Yusuf Mehdi has pledged to spend his final year reimagining Windows for the agentic era before he leaves, and Microsoft is expected to talk more about this direction at its October Windows event, the software giant’s first major Windows event in two years.

Contrary to popular belief, Microsoft is not ignoring developers. It is adding Linux containers, improving WSL, shipping Linux-like command-line tools, building developer-specific Windows configurations, and putting AI-focused hardware to back it all. Yet an informal poll of 10,000 votes shows that Windows is still far behind macOS and Linux.

Windows is clearly capable of being a good developer platform. The harder problem is convincing developers they should want it as their development environment.

The post Microsoft keeps rebuilding Windows for developers, but a new poll puts Windows at just 12% appeared first on Windows Latest

Read the whole story
alvinashcraft
6 hours ago
reply
Pennsylvania, USA
Share this story
Delete
Next Page of Stories