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

Adopting AV1 for Real-Time Communication (RTC) at Scale

1 Share
  • Adopting AV1 for real-time communication at Meta has been a multi-year effort spanning codec selection, device eligibility, rate control, and error resilience.
  • We’re sharing the technical and operational challenges while deploying AV1 and expanding coverage, and how we addressed them for real-time communication.
  • We’re presenting several technologies for improving AV1 call quality, including rate control and error resilience.

The AV1 video codec, first standardized by AOMedia in 2018, has rapidly evolved and gained widespread industry support. Today, leading companies like YouTube, Netflix, and Meta stream video using AV1 at scale. Meta introduced AV1 for real-time video calls on high-end devices in 2023, aiming to deliver superior call quality. Since then, we have made notable progress in expanding AV1’s reach and improving the experience for AV1-powered calls. Today, AV1 is enabled on the majority of mobile devices in Meta Real-Time Communication (RTC) applications such as Messenger and WhatsApp.

Why Is Meta Interested in Adopting AV1 for RTC? 

The motivation for switching to a more advanced video codec is straightforward — it delivers the same visual quality while using much less bandwidth. In offline tests, we observed at least a 20% bitrate reduction with AV1 compared with H.264/AVC under our product settings on low-end and mid-range devices. If devices can accommodate higher encoding complexity, the bitrate reductions are even greater. For real-time video calls, this means people on slower or limited networks can enjoy significantly better video quality. This is important to our users because, to meet low-latency requirements, the RTC product must handle bitrate fluctuations. In real-world networks — especially in emerging markets — video bitrates for RTC products typically range from 10 kbps to 400 kbps. Maintaining good video quality below 100 kbps remains challenging.

To evaluate the user experience across codecs, we enabled AV1 in the Messenger app and conducted a side-by-side comparison using two Android phones. In the examples below, AV1 is displayed on the right and H.264/AVC on the left, both limited to 100 kbps. The H.264/AVC video appears noticeably blurry, while the AV1 video remains much clearer — highlighting the significant advantage of AV1 for video calls under bandwidth constraints.

H.264/AVC (left) versus AV1 (right).


An increased focus on screen content, needs support from high-quality computer generated content encoding. Traditionally, video encoders aren’t that well suited to complex content such as text with a lot of high-frequency content, and people are very sensitive to reading blurry text. AV1 has a set of coding tools — palette mode and intra-block copy — that drastically improve performance for screen content. 

Palette mode is designed according to the observation that the pixel values in a screen-content frame usually concentrate on the limited number of color values. It can represent the screen content efficiently by signaling the color clusters instead of the quantized transform-domain coefficients. In addition, for typical screen content, repetitive patterns can usually be found within the same picture. Intra-block copy facilitates block prediction within the same frame, so that the compression efficiency can be improved significantly. AV1 has the benefit of providing these two tools at the main profile.

The Challenges in Adopting AV1

While the comparison clearly illustrates AV1’s advantages, there are significant challenges to its adoption in RTC. Unlike video on demand (VOD), RTC systems must manage end-to-end video latency, which ideally should remain below 300 milliseconds. If latency exceeds this threshold, people begin to notice delays in the conversation.

Maintaining both high video quality and low latency is challenging. For example, multi-pass encoding techniques — which can improve quality — introduce additional delay. On the decoder side, extensive buffering further increases latency. Additionally, any sudden spikes in bitrate can cause video freezes during calls, degrading the user experience.

RTC products must also dynamically adapt to network conditions during a call. Two challenges are fluctuations in network bandwidth and packet loss.To cope with bandwidth changes, the video encoder adjusts parameters such as resolution and frame rate. However, switching resolutions typically requires a new key frame, which can cause a sudden bitrate spike and temporary video freezing. Similarly, packet loss can trigger retransmissions or force the encoder to send another key frame, both of which may lead to video freezes. Effectively managing these issues helps enable delivery of high-quality, uninterrupted video calls.

Additionally, the RTC client must perform both real-time encoding and decoding, both of which consume significant power — making power efficiency important, especially on mobile devices.

Encoder and Decoder Selection

Choosing the right encoder and decoder is the most critical step in adopting a new codec. The computational complexity of video codecs is a significant consideration for mobile devices. While AV1 offers improved compression efficiency through advanced coding tools, these benefits come at the burden of increased computational demands, particularly during encoding.

To assess this increased complexity, in an offline experiment we integrated an open-source AV1 encoder and measured power consumption on a Pixel 8 device during a video call. The results showed a 14% increase in power usage compared to H.264/AVC — a significant challenge for mobile deployment. To address this, we adopted an internal low-complexity encoder that has similar power consumption as H.264 baseline, as detailed in the next section.

Beyond power, AV1 encoding also increases memory usage compared to H.264/AVC, leading to app crash regressions that further complicate mobile adoption.

Low-Complexity Encoder

A strong encoder should balance visual quality against computational complexity. Low complexity encoding helps enable AV1 encoding on mid-range and low-end devices.

Compared to older codecs like H.264/AVC, newer codecs such as AV1 deliver better compression efficiency. However, these benefits are thought of to come only with higher computational complexity — this represents an obstacle to extending AV1 coverage to low-end devices.

However, a newer codec should not necessarily require a higher-complexity encoder. Because modern codecs support a larger set of coding tools, a well-designed encoder has more opportunities to find better trade-offs between quality and complexity. These trade-offs are also referred to as presets. Ideally, the encoder offers multiple presets, spanning a range from high to low complexity while still maintaining a consistent compression efficiency gain. An ultra-low-complexity preset comparable to H.264/AVC could enable shipping AV1 on low-end phones. 

To address this, we adopted a low-complexity encoder implementation of AV1 for the RTC use cases. In addition to optimizing the quality of the high-complexity preset, we developed an ultra-low-complexity preset. This new preset delivers encoding complexity comparable to H.264/AVC. With it in place, we designed a mechanism that adjusts the encoder preset based on device capabilities, enabling us to ship AV1 to a much broader range of devices.

Decoder Selection

After selecting the encoder, the next step is choosing the decoder. Although video decoders are generally less complex than encoders, we found that decoding complexity remains significant on mobile devices and video calling usecases, especially low-end models. In our initial A/B tests, some low-end devices could not perform real-time decoding, resulting in video freezes and audio/video synchronization issues.

We compared several open-source decoders and, after A/B testing, we selected dav1d for its superior power efficiency and reliability. Our experiments also showed an increase in talk time with the dav1d decoder.

Binary Size

Integrating the AV1 encoder and decoder into the mobile app introduces another challenge: binary size. Using libAOM as an example, AV1 support adds 1.7 MB to the application (600 kB compressed). While this may sound negligible, it’s a major challenge for a company that serves billions of users. Binary size affects update success rates, application startup time, and software health metrics like memory usage and crash rates which can negatively impact user experience. A larger binary leaves more people on older app versions and delays incoming call setup. For example a 600 kB increase could consume an entire year’s binary size budget for a large organization.

We explored several approaches to reduce the binary size. 

  • Our initial approach was to use a dynamic-download framework to deliver AV1 as a separate component. However, download failures — whether from poor network conditions, device issues, or random occurrences — degraded the user experience, making this approach insufficient.
  • We then focused on direct binary size optimizations. For example, the quantization matrix (QM) tool accounts for about 10% of the encoder’s library size; optimization could halve it. We also contributed size reductions optimizations to the dav1d project.

This strategy extends to end-to-end pipeline optimization, removing unused tools from the library entirely. For instance, removing QM frees 60 kB of binary space. At the application level, we can share codec libraries across features — such as video message transcoding — and leverage built-in platform codec support to avoid bundling additional libraries.

Expanding AV1 Coverage

After selecting the encoder and decoder, the next challenge was identifying which devices are eligible to use AV1. Compiling eligible iOS models was straightforward given the limited number of variants, but Android posed a far greater challenge due to the vast number of device models.

We initially tried selecting devices based on memory, release year, and Android OS version, but none of these strategies proved sufficiently reliable. Ultimately, we leveraged Meta’s in-house ML-based device eligibility framework to generate a reliable list of eligible Android devices.

AV1 Device Eligibility

We created a machine learning (ML)-based device eligibility  framework to support advanced video and audio features based on device capability:

Figure 1: Our ML-based device eligibility framework.

The idea is to use large-scale real-world statistical data to categorize device capabilities, rather than relying on lab data. This helps us scale our device eligibility system and make more accurate decisions. We propose an ML-based device eligibility approach that uses low-level performance statistical metrics collected through our logging pipeline to assess a device’s AV1 capability. The model takes these measurements as input features and outputs an rtc_score, which quantifies the device’s overall AV1 performance. This score then informs decisions such as optimizing call settings and determining whether a device can run the AV1 codec efficiently.

In 2025, we iteratively refined our model using AV1-specific data and significantly expanded device support. Our first milestone, Model V1.1, rolled out in August 2025 and broadened AV1 traffic across an increasing set of devices. That additional traffic contributed to a dedicated AV1-only dataset that became both larger and more representative over time. With this richer data, we built Model V2, introducing a two-tier approach that differentiates between higher-end and lower-end devices—reflecting the reality that entry-level phones and flagship devices can have very different AV1 encoding capabilities. Across these iterations, we substantially increased AV1 enablement across the device landscape, with an approach designed to keep improving as traffic grows and more data becomes available. 

As AV1 traffic continues to grow, we expect iterative optimization will further improve both call duration and quality.

Codec Complexity Adaptation

Device eligibility  lets us identify capable devices, but we discovered an additional challenge: During A/B tests, we observed calls with significant audio/video sync regressions, primarily caused by devices unable to encode or decode video in real time. Surprisingly, even a 2023 smartphone with an octa-core processor could not handle encoding at 320×180@15fps. This issue affected both H.264 and AV1, though it was more prevalent with AV1. We suspect these devices throttle CPU frequency during calls, reducing their effective capability.

As a result, enabling AV1 purely based on device name is not sufficient. We needed a more robust mechanism to adjust codec complexity based on both local and peer device status. We developed three mechanisms: adaptive encoder preset adjustment, encoding latency-aware codec switching, and decoding latency-aware codec switching.

Adaptive Encoder Preset Adjustment

We designed multiple encoder presets ranging from low to high complexity. A monitoring mechanism continuously tracks encoding latency during calls to select the appropriate preset. If encoding latency becomes too high — meaning the device is close to being unable to encode in real time — we reduce encoder complexity. Conversely, if the device can sustain higher complexity, we increase the preset to achieve better quality.

Local Device Encoding Latency-Aware Codec Switch

If lowering the encoder preset still does not reduce encoding latency to an appropriate level, we apply codec switching. In this case, the device switches to H.264/AVC, which may be  less computationally intensive than AV1 for that specific content. To enable this, we negotiate support for both codecs at call setup, and the client continuously monitors device conditions to determine the most appropriate codec. Encoder preset and codec selection are decided jointly to optimize call quality and prevent codec-selection oscillation.

Peer Device Decoding Latency-Aware Codec Switch

Because AV1 also has higher decoding complexity, we want to ensure the peer device can decode AV1 frames in real time. This is especially important when a high-end phone calls a low-end phone: the sender may be able to encode AV1, while the receiver may not be able to decode it in real time.

To address this, each device continuously feeds back its video decoding latency during the call. If the sender detects that the peer cannot decode AV1 in real time, it switches back to H.264/AVC.

Together, these mechanisms adaptively adjust both the encoder preset and the codec based on encoding and decoding latency. Beyond latency, we also consider other device health signals, such as battery level. For example, when the battery is low, we switch to H.264/AVC. This helps maintain call quality and extends call duration.

Asymmetric Codec Design

With the improved codec-selection strategy, we rolled out AV1 support to mid-range and low-end Android devices. While some mid-range devices cannot perform real-time AV1 encoding, many can decode AV1 in real time. This enables an asymmetric codec design: mid-range devices continue to encode and send H.264/AVC, but can receive AV1 from high-end peers. As a result, we significantly increased AV1 coverage across Android devices.

Figure 2: Asymmetric codec design.

Improving AV1 Call Quality

The preceding sections described our framework for enabling AV1 on a wide range of devices. With this system in place, AV1 now powers the majority of mobile devices in Meta RTC (Real-Time Communication) applications. . The next challenge is further improving AV1 call quality.

As discussed earlier, RTC products must dynamically adapt to network conditions during a call. Two notable challenges are fluctuations in network bandwidth and packet loss. Accurate rate control helps address bandwidth changes. Error-resilient strategies play an important role in ensuring reliable quality in the presence of packet loss.

Accurate Rate Control

In RTC, maintaining a constant bitrate (CBR) is important. Any instantaneous bitrate overshoot can lead to congestion and video freeze on the peer’s side. RTC applications are sensitive to instant bitrate overshoots, so simply checking average bitrate is insufficient. We use Video Buffering Verifier (VBV) delay as a metric to evaluate CBR accuracy.

VBV Delay

The Video Buffering Verifier (VBV) is a leaky-bucket-based measurement used to ensure that an encoded video stream can be correctly buffered and played back at the decoder.

We use a similar method to measure CBR rate control accuracy. The figure below shows an example:

Assume the current network bandwidth allocated to video is 100 kbps and we ask the encoder to encode frames at 100 kbps. The encoder encodes Frame (Frm) N at 20 kbits. At the same time, Frame (Frm) N-1 has not been fully transmitted, and 5 kbits remain in the buffer (likely from an overshoot on Frame N-1).

Sending Frame N would therefore take at least (20 kbits + 5 kbits) / 100 kbps = 0.25 s = 250 ms. Consider a system in which the desired VBV delay for RTC is below 200 ms. In this example, encoder overshoot and a large VBV delay are likely to lead to a poor user experience—for example, higher latency, network congestion, or video freezes. This highlights the importance of accurate rate control for RTC use cases.

Figure 3: An example of VBV delay calculation.

Rate Control Optimization

We made several rate-control improvements to ensure the encoder does not overshoot. During encoding, the encoder tracks VBV buffer status and uses it to guide bitrate allocation. When an overshoot occurs, it reduces the rate of subsequent frames to keep VBV delay under control. In our experience, many video encoders do not handle this well, allowing VBV delay to grow and potentially cause network congestion.

Similarly, encoders often allocate a high bitrate to intra-only (key) frames to maintain quality consistency between key frames and inter frames. Some encoders even “boost” key-frame quality to improve reference-frame quality. In RTC, however, we want to avoid bitrate spikes. The encoder therefore strictly controls key-frame bitrate and reduces the rate of subsequent frames to compensate for any overshoot.

Rate control in RTC also presents challenges:

  • Frequent target bitrate changes. The client may update the encoder target bitrate frequently. A robust encoder must keep VBV delay under control — especially when the target bitrate drops sharply.
  • Frequent resolution changes. The client may also change resolution often during a call. A rate-control algorithm should therefore remain stable and effective under frequent resolution changes. In addition, AV1 supports a useful feature to address this issue, called Reference Picture Resampling (RPR), which allows resolution changes without generating a key frame. This can reduce bitrate spike significantly and improve the video freeze.

Because the video encoder interacts closely with the network congestion-control module, we found that preventing undershoot is as important as preventing overshoot. In our early versions of the rate-control algorithm, we used conservative rate allocation to avoid overshoot, but this increased the tendency to undershoot. Undershooting can mislead bandwidth estimation, slow bitrate ramp-up, and ultimately degrade video quality. We therefore revised the algorithm to address undershoot and improve bitrate accuracy.

Overall, an accurate rate-control algorithm that produces a stable bitrate — without significant overshoot or undershoot — can substantially improve video-call quality.

Error Resilience

RTC imposes strict latency constraints, while modern video codecs rely on long, tight chains of inter-frame dependencies. When a packet is lost, the receiver must send a NACK and wait a round trip for retransmission. If that fails, the dependency chain breaks and the video freezes. The receiver then requests a keyframe, which costs another round trip, but because keyframes are roughly 10x larger than typical P-frames, they can congest the network and increase packet loss, creating a problematic cycle. To mitigate this, we tuned AV1 for fast recovery and drift containment under packet loss by leveraging temporal layers (TL) and Long-Term Reference (LTR) frames.

Temporal Layer (TL)

Temporal layers are a form of temporal scalability used in modern video codecs (including AV1) where the encoder organizes frames into a time-based hierarchy. The base layer (temporal layer 0) provides a lower frame rate on its own, while enhancement layers (temporal layer N) add intermediate frames to reach higher frame rates when conditions allow. Figure 4 shows the two-layer structure we use for AV1.

Figure 4: Two temporal layer structure.

A notable property of this structure is that the base layer maintains continuity, without relying on enhancement-layer frames.If enhancement-layer packets are lost or arrive too late, decoding can still proceed using the base layer without stalling. We take advantage of this by prioritizing robustness by layer: We apply FEC to protect base-layer data rather than spending redundancy on enhancement data. We also treat enhancement-layer retransmissions more conservatively — when round trip time (RTT) is low, retransmitting a missing enhancement packet can help; when RTT is high, we may skip retransmissions without breaking the decode flow.

There is a trade-off: Compared to a tightly dependent prediction chain (where each frame references the immediately preceding frame), a temporal-layer structure is typically less compression-efficient, so leaving TL enabled all the time can degrade quality at a given bitrate. But TL’s benefits show up mainly under lossy or unstable networks, which are only a subset of real-world calls. For that reason, we enable TL adaptively. The sender monitors network feedback, turns TL on when loss rises, and turns it back off once conditions recover. This gives us resilience when we need it without sacrificing efficiency when we don’t.

Long-Term Reference (LTR)

LTR is an error-resilience feature that allows a video encoder to store reference frames in the buffer longer than regular reference frames and send LTR-predicted (LTRP) frames as requested. When the decoding chain is broken due to frame loss, an incoming LTRP frame—predicted from a previously decoded LTR frame—instantly resynchronizes sender and receiver, recovering from the loss. Figure 5 illustrates how LTR and LTRP frames work in lossless and lossy scenarios.

Figure 5: LTR and LTRP in lossless and lossy scenarios.

Implementing LTR requires close coordination with the network layer. Figure 6 shows how the AV1 encoder interacts with the network layer. The encoder periodically emits LTR frames and pins them in its bounded reference buffer of size 4, evicting the oldest pinned LTR when a new one is added. From the network layer’s perspective, however, an encoded LTR frame looks the same as any other frame, so the network cannot tell when to send an ACK back to the encoder. To make this reliable, the encoder sends an explicit LTR indicator when handing the frame to the network layer. This differs from H.264, where LTR and non-LTR reference frames are distinguished by bitstream syntax — the network layer can parse the H.264 slice header to recognize an LTR frame and ACK the sender upon receipt.

The explicit LTR indicator is a binary flag carried in our proprietary RTP header extension, which we use to transport per-frame metadata on the primary channel. We also expose the frame_id to the network layer through LTR bitstream syntax. ACK feedback is sent via a separate proprietary RTP header extension. Each ACK includes the corresponding frame_id, allowing the sender to unambiguously identify which LTR was received. When servicing an LTRP request, the encoder always uses the most recently ACKed LTR as the prediction reference.

The network layer requests an LTRP frame from the encoder in two cases. The first is reactive recovery, when the receiver experiences a freeze and sends an RPSI to request an LTRP. The second is proactive protection, when the sender detects elevated packet loss via a feedback channel and asks the encoder to send LTRPs periodically. While the proactive path can be somewhat redundant, it significantly improves reliability and reduces freezes. From the encoder’s perspective, the reason does not matter — it simply receives an LTRP request and responds based on whether it has an ACKed LTR reference in the buffer. If an LTR is available, the encoder produces an LTRP frame. If not, it assumes resynchronization is needed and sends a key frame instead.

While LTR is more efficient for loss recovery than forcing a key frame or relying on retransmissions, it can reduce overall coding efficiency because an LTRP frame may reference an older LTR with weaker temporal correlation, making motion prediction less accurate. We mitigate this by leveraging an existing encoder design choice — the encoder already emits a periodic, slightly higher-quality frame to improve overall quality. We simply mark that frame as LTR, so the LTR remains high quality even as it ages.

Figure 6: AV1 encoder interaction with the network layer.

Meta’s Ongoing Journey With AV1

Adopting AV1 for real-time communication at Meta has been a multi-year effort spanning codec selection, device eligibility, rate control, and error resilience. By combining a low-complexity encoder with ML-based device eligibility, adaptive codec switching, and robust error-resilience mechanisms, we have enabled AV1 on the majority of mobile devices — delivering meaningful quality improvements, especially for users on bandwidth-constrained networks. This initiative complements our ongoing efforts to expand AV1 for VOD applications. As device capabilities continue to improve and ML models leverage more data, we expect AV1 coverage and call quality to keep advancing.

Meanwhile, we are working on extending AV1 to group calls. Unlike 1:1 calls, participants in group calls must decode multiple video streams, which makes increasing AV1 coverage in group calls more challenging.  While software AV1 implementations aid the steady expansion of AV1 coverage, higher quality and improved features will likely require AV1 hardware support.

The benefits of AV1 are clear, and most content and RTC service providers are moving to AV1 as their flagship codec. We encourage SoC vendors to invest in HW AV1 across all device tiers to meet the AV1 requirements to deliver an improved viewer experience, device battery savings and enhanced network operator infrastructure efficiency.

The post Adopting AV1 for Real-Time Communication (RTC) at Scale appeared first on Engineering at Meta.

Read the whole story
alvinashcraft
just a second ago
reply
Pennsylvania, USA
Share this story
Delete

AWS Weekly Roundup: NY Summit recap, Local Zone in Hanoi, Grok 4.3 in Bedrock, price reductions, and more (June 22, 2026)

1 Share

Last week AWS Summit New York City brought together thousands of customers, partners, and builders for a free, one-day event showcasing the latest in cloud and AI innovation. Dr. Swami Sivasubramanian, VP of Agentic AI at AWS unveiled a stack of AI launches in his keynote, all built around one thesis: agents that compound value over time.

  • Agents for working – You can launch autonomous agents and access a smarter activity feed with new Amazon Quick features, which now let you create and run multi-step agents directly in the desktop app and consolidates email, Slack, calendar, and tasks into a single prioritized view with personalized rules.
  • Agents for securing – You can shift from reactive to proactive security with AWS Continuum, a new AI-native security service that reasons, validates, and acts at machine speed across the full code vulnerability lifecycle. AWS Security Agent (now part of AWS Continuum) adds new features: threat modeling; pull request code scanning with remediation across major Git platforms; and IDE integrations via Kiro power, Claude Code plugin, and MCP.
  • Agents for building – You can write, ship, and modernize code in one continuous loop with Kiro, AWS DevOps Agent, and AWS Transform. Kiro introduces a native iOS app; AWS DevOps Agent adds release management capabilities to assess code changes before production; and AWS Transform continuous modernization reduces tech debt autonomously.
  • Agents customers create – You can go from agent idea to production in minutes with Amazon Bedrock AgentCore, which now includes a GA harness for infrastructure and orchestration, Web Search, Managed Knowledge Base, policy integrations with Guardrails, and the new AWS Context service for mapping organizational data relationships.

To learn more, visit the Summit recap from our top announcements blog post and Amazon News post.

Last week’s launches
Here are last week’s launches that caught my attention:

  • AWS Local Zone in Hanoi, Vietnam  —This new Local Zone is one of the first AWS Local Zones in the Asia Pacific with support for Amazon S3 and Amazon EBS Local Snapshots, enabling customers to meet data residency requirements by storing and backing up data locally. To get started, enable the Hanoi Local Zone (ap-southeast-1-han-1a) from the Regions and Zones tab in the AWS Global View or by using the ModifyAvailabilityZoneGroup API.
  • AWS Blocks, an open-source TypeScript framework for application developers (preview) — AWS Blocks runs a fully functional local environment with Postgres, authentication, and real-time messaging, no AWS account required. When you’re ready to deploy, the same application code runs on production AWS services with zero changes, and you can drop into AWS CDK at any point for direct resource configuration.
  • Grok 4.3 from xAI in Amazon Bedrock —You can use the Grok 4.3 model on Amazon Bedrock, giving you even more choice as you build generative AI applications across reasoning, agentic, and enterprise workflows. Grok 4.3 runs on a new inference engine in Bedrock designed for price performance, with support for tool calling, structured output, and response streaming.
  • Amazon S3 annotations: attach rich, queryable context directly to your objects — Amazon S3 now lets you attach up to 1 GB of rich, mutable, and queryable context directly to your objects using annotations, purpose-built for AI agents and autonomous workflows that need to discover, understand, and act on data at scale without maintaining separate metadata systems.
  • Amazon ECS announces faster service auto scaling — Amazon ECS service auto scaling now detects and responds to load changes faster with support for high resolution (20-second) metrics and metric publishing optimizations. In AWS benchmarking tests, time to trigger scale-out improved from 363 seconds to 86 seconds (76% faster), and total time to scale and provision new tasks improved from 386 seconds to 109 seconds (72% faster).
  • Amazon EC2 G7 instances accelerated by NVIDIA RTX PRO 4500 Blackwell Server Edition GPUs — AWS is the first major cloud provider to support NVIDIA RTX PRO 4500 Blackwell Server Edition GPUs. G7 instances are accelerated by these GPUs with custom sixth-generation Intel Xeon Scalable processors, delivering up to 4.6x AI inference performance and up to 2.1x graphics performance compared to G6 instances.
  • Strands Agents introduces new capabilities — Strands is an open source toolkit for building production agents. You can now use better context management in Harness SDK, a new isolated execution environment with Strands Shell, and chaos testing and red teaming in Strands Evals.
  • AWS Management Console Private Access – You can access the AWS Console from VPCs without internet connectivity, allowing enterprises to manage their AWS infrastructure through the console while maintaining strict network security controls in air-gapped environments.
  • AWS Marketplace Storefront is now generally available – AWS Partners can create and deploy their own branded catalog of solutions and services on their website or application in hours. Channel Partners and Independent Software Vendors can now simplify how they manage their cloud marketplace business and make it easier for customers to discover and purchase their solutions from AWS Marketplace.
  • Palo Alto Networks (PANW) Advanced DNS Security on Amazon Route 53 Resolver DNS Firewall (preview) – You can now enforce DNS threat protections from Palo Alto Networks directly on Route 53 DNS Firewall rules, without deploying separate firewalls or modifying VPC configurations — by subscribing to PANW from the DNS Firewall console through the embedded AWS Marketplace widget.

For a full list of AWS announcements, be sure to keep an eye on the What’s New with AWS page.

Price reductions 
AWS continues to look for ways to increase performance and lower prices for our customers. I noticed a few such efforts last week, so I’d like to share them:

Learn more about AWS, browse and join upcoming AWS-led in-person and virtual events, startup events, and developer-focused events as well as AWS Summits and AWS Community Days. Join the AWS Builder Center to connect with builders, share solutions, and access content that supports your development.

That’s all for this week. Check back next Monday for another Weekly Roundup!

— Channy

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

Why No Passkeys? Naming the Top Sites That Still Don't Support Them

1 Share
Why No Passkeys? Naming the Top Sites That Still Don't Support Them

Back in 2017, Troy Hunt and I built a little website called whynohttps.com. The idea was simple: take the most popular sites on the internet, check which ones still weren't redirecting visitors to HTTPS, and put the laggards on a list for everyone to see. No lecture, no 40-page report, just a leaderboard of who hadn't done the thing yet. It turned out that a list is a surprisingly effective motivator. Nobody wants to be on the list.

We're at exactly the same moment again, but this time the technology is passkeys. So, Troy provided the domain, and I've built the obvious sequel: whynopasskeys.com

Why No Passkeys? Naming the Top Sites That Still Don't Support Them

We've already had the passkeys argument

Don't worry, I'm not going to tread the same ground again. I've written plenty about passkeys already, from Passkeys 101 covering how they actually work, to the sharper edges of the threat model that nobody seems to be talking about. The short version is the part that matters here: passkeys are phishing-resistant by design. They're hard to phish, they can't leak in a breach, and they can't be replayed. Whether a passkey replaces your password entirely, or just backs a password up as a 2FA mechanism, it removes a whole category of attacks that we've been fighting, and losing, for decades.

The technology works and it's widely supported. We aren't waiting on engineering, we're waiting on adoption. And just like HTTPS in 2017, the thing standing between users and a meaningfully more secure internet is a long list of websites that haven't gotten around to it yet.

That's the gap I want to make visible.

What the site shows

whynopasskeys.com takes the world's most popular websites and tells you which ones support passkeys and which ones don't. There's a global Top 25, and there are per-country lists so you can see how your own corner of the internet is doing, covering well over a hundred countries.

The launch-day headline number is the whole reason this site exists:

7 of the top 25 sites globally still have no passkey support. That's 28% of the most-visited destinations on the internet.

If they do not support passkeys, passkeys still feel optional everywhere else, and these aren't small shops without a security team. The current no-passkeys list at the top end includes names like Instagram, Netflix, Spotify, Samsung, Roblox and Baidu. Sites with hundreds of millions, in some cases billions, of accounts, all still protected by nothing more than a password and possibly MFA. These are the sites that shape user expectations.

I've also tried to be honest in the other direction, because "supports passkeys" is doing a lot of work as a phrase. A site that lets you log in with a passkey and skip the password entirely is in a very different place to one that only allows a passkey as a second factor on top of your existing password. So where I can, the list distinguishes between passwordless passkey support and MFA-only support.

How it's built

People asked the same thing about whynohttps.com all those years ago, so let me get ahead of it: how do you know?

For ranking the sites I use Cloudflare Radar for the global and US lists, which is about as good a successor to the old Alexa rankings as we have, and the Tranco list for per-country rankings, attributing sites to countries by their national domain so you get that country's popular sites rather than the same handful of global giants on every page. There's a fair bit of unglamorous plumbing to strip out the CDNs, ad networks and API endpoints that clog up raw rankings, because nobody needs to know whether an analytics beacon supports passkeys.

The passkey support data itself comes from passkeys.directory, the excellent community-maintained list run by the folks at 2factorauth. This is the honest limitation of the whole project, and I'd rather say it out loud than have someone "gotcha" me with it: passkey support cannot be reliably auto-detected. WebAuthn lives behind a login flow, so there's no header to scan and no endpoint to probe the way there was with HTTPS. The list is therefore only as complete as the directory it draws from.

Which leads nicely to the most important feature.

If a site is wrong, you can fix it!

Every "No passkeys" entry on the site links straight to a way to correct it. If a site does support passkeys and we've got it wrong, the fix is to submit it to passkeys.directory, which improves the data for the whole community, not just my little list. I would genuinely love for this site to get less accurate over time, in the sense that I have to keep moving names from the red column to the green one.

Because that's the actual goal. whynohttps.com wasn't really about the shaming, satisfying as it was. It was about giving people a clear, sharable, undeniable picture of where we were, so that the conversation inside these companies shifted from "should we?" to "why are we on this list?". HTTPS went from a 'nice-to-have' to being 'essential' in a remarkably short space of time, and a bit of friendly public accountability was part of that.

Passkeys are at the same crossroads now. The sites at the top of these lists set the tone for everyone else. When the biggest names make passkeys popular, it stops being exotic and starts being expected.

A note for the sites doing the work

If you're rolling passkeys out, brilliant. It's harder than it looks to do well, and the threat model has subtleties that bite you precisely because passkeys are so strong everywhere else, which is the whole reason we had our own implementation independently security tested before we shipped it. If you're standing up passkeys and want visibility into what's actually happening in your users' browsers during sign-in, that's exactly the kind of thing Report URI is built to watch. The best time to know your auth flow is misbehaving is before your users tell you.

Go and have a look

whynopasskeys.com is live. Go and find your favourite sites, find your country, and if there's a name on there that really ought to know better, share it with them. The fastest way to get a site off the list is for enough of its users to ask why it's on there in the first place.

And if you run one of these sites: you already know what to do. Let's get you off the list.

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

Astro 7.0

1 Share
Astro 7.0 brings faster builds with Vite 8, a new Rust compiler, Advanced Routing, background dev server support, and structured logging.
Read the whole story
alvinashcraft
30 seconds ago
reply
Pennsylvania, USA
Share this story
Delete

Using Scroll-Driven Animations for Opposing Scroll Directions

1 Share

Sometimes designers have silly ideas that eventually grow on you. That happened to me with this concept where I had to build columns of items moving in opposite directions when a user scrolls the page.

Note: This demo respects reduced motion settings, so you’ll need to enable motion to see the effect. And we’re looking at Chrome and Safari support as I’m writing this.

It’s really not as hard as you might think, thanks to modern CSS features, specifically scroll-driven animations. Not only that, but it’s fun to make, too! Let me show you how I approached it — and maybe you will want to share how you would do it differently.

The HTML

The HTML consists of a parent element (.opposing-columns), its children (.opposing-column), and its children’s children (.opposing-item):

<div class="opposing-columns">
  <!-- Column 1 -->
  <div class="opposing-column">
    <div class="opposing-item">...</div>
    <div class="opposing-item">...</div>
    <div class="opposing-item">...</div>
  </div>
  <!-- Column 2 -->
  <div class="opposing-column">
    <div class="opposing-item">...</div>
    <div class="opposing-item">...</div>
    <div class="opposing-item">...</div>
  </div>
  <!-- Column 3 -->
  <div class="opposing-column">
    <div class="opposing-item">...</div>
    <div class="opposing-item">...</div>
    <div class="opposing-item">...</div>
  </div>
</div>

This is all we need in the markup. CSS will do the rest!

Styling the parent container

First off, we’re going to set things up so that this effect only applies to larger screens — there’s no real sense in supporting something like this on smaller screens because we need the additional space for the effect.

/* Just on larger screens */
@media screen and (width >= 50rem) {
  .opposing-columns {
    display: flex;
    gap: 2rem;
    max-inline-size: min(90dvi, 50rem);
    margin-inline: auto;
  }
}

Setting up a “masking” effect

We need to do a few more things with the parent container to get the illusion that items in each .opposing-column are disappearing as they scroll past it. The items in the outer columns move upward on scroll, and items in the center column move downward. As they cross the parents’ boundaries, we want them to sorta fade out.

So, we’re going to do a few things. First, we’ll set a background color variable on the document as a whole:

@media screen and (width >= 50rem) {
  :root {
    --opposing-bg: lightcyan;
    background-color: var(--opposing-bg);
  }
  
  .opposing-columns {
    /* same styles as before */
  }
}

Second, we’ll apply that same background color on the parent’s :before and :after pseudo-elements:

@media screen and (width >= 50rem) {
  :root {
    --opposing-bg: lightcyan;
    background-color: var(--opposing-bg);
  }
  
  .opposing-columns {
    /* same styles as before */
  
    &:before,
    &:after {
      content: "";
      position: absolute;
      inset-inline: 0;
      block-size: calc(var(--opposing-mask) * 3);
      pointer-events: none;
      z-index: 1;
    }
  }
}

Notice that we’ve established a stacking context on the pseudos and set them one layer above the parent and its descendants. This is key for masking the items in each column as they scroll in and out of the container. The items are technically sliding under the pseudo masks.

Speaking of which, let’s create another variable called --opposing-mask that adds vertical space between the parent element and the three columns:

@media screen and (width >= 50rem) {
  :root {
    --opposing-bg: lightcyan;
    --opposing-mask: 3rem;
    background-color: var(--opposing-bg);
  }
  
  .opposing-columns {
    display: flex;
    gap: 2rem;
    max-inline-size: min(90dvi, 50rem);
    margin-inline: auto;
    margin-block: var(--opposing-mask, 3rem);
    position: relative;
  }
}
Highlighting the vertical space between the parent container and its child elements.

Let’s do the same thing to the parents’ pseudos, only applying --opposing-mask to their block-size by a multiple of three. This way, there’s additional vertical space between them and the parent.

@media screen and (width >= 50rem) {
  :root {
    --opposing-bg: lightcyan;
    --opposing-mask: 3rem;
    background-color: var(--opposing-bg);
  }

  .opposing-columns {
    /* same styles as before */
  
    &:before,
    &:after {
      content: "";
      position: absolute;
      inset-inline: 0;
      block-size: calc(var(--opposing-mask) * 3);
      pointer-events: none;
      z-index: 1;
    }
  }
}
Highlighting the vertical space between the parent container and its before pseudo element.

You might see where this is going. We have a nice amount of space between the parent container and its pseudos. We want the column items to appear as if they are fading out as they scroll out of the parent container. We don’t have to mess with their opacity or anything like that. Instead, we can add background gradients on the pseudos.

The :before pseudo is at the top of the container, so we’ll give it a gradient that goes from a solid color that matches the document’s underlying background color to transparent, top-to-bottom. And since the :after pseudo sits at the bottom of the parent container, we’ll reverse the gradient so it goes transparent to the document’s background color, bottom-to-top.

@media screen and (width >= 50rem) {
  :root {
    /* same styles as before */
  }
  
  .opposing-columns {
      /* same styles as before */
    
      &:before,
      &:after {
        /* same styles as before */
      }
      
      &:before {
        background-image: linear-gradient(
          to bottom,
          var(--opposing-bg) var(--opposing-mask),
          transparent
        );
        inset-block-start: calc(var(--opposing-mask) * -1);
      }

      &:after {
        background-image: linear-gradient(
          to top,
          var(--opposing-bg) var(--opposing-mask),
          transparent
        );
        inset-block-end: calc(var(--opposing-mask) * -1);
      }
    }
  }
}

The column layouts

Before we get to the magic, we ought to lay out the items in each column. Each column is a flex item inside the parent, which is a flex container. We’ll let them shrink (flex-shrink: 1) and grow (flex-grow: 1), capping the size at a certain point (flex-basis: 10rem).

We can define all that with the flex shorthand property:

@media screen and (width >= 50rem) {
  /* same styles as before */

  .opposing-column {
    flex: 1 1 10rem;
  }
}

Now I want those columns to be grid containers so I can use the gap property to insert space between items:

@media screen and (width >= 50rem) {
  /* same styles as before */

  .opposing-column {
    flex: 1 1 10rem;
    display: grid;
    gap: 2rem;
  }
}

We totally could have used Flexbox here as well to get access to gap, but the default layout is set to row and we’d have to override that to column. Grid is a little more concise in this situation.

The animation!

This is what you came for, right? We’ve set everything up so that column items can flow in and out of the parent container on scroll. Now we need to add that scrolling behavior.

This is where the animation-timeline property comes real handy. Normally, a CSS animation just runs on its own. It starts when the page loads (or after a specific delay you set) and ends after however long you set the duration. With animation-timeline, we tell the animation to run based on its scroll position… hence the term “scroll-driven” animation.

We have two supported functions here, scroll() and view(). They’re related but super different in that scroll() runs the animation based on an element’s scroll position. The view() function is similar, but tracks the element’s progress as it enters and exits the scrollport (i.e., the scrollable area of the container it is in).

We’re going with the view() function because we’ve set this up where there is a clear scrollable area inside the parent container. We need to run the animation based on where it enters and exits that area rather than the scroll position of the column items.

This is real interesting because we can tell view() where exactly we want the animation to start once it enters the scrollable area and where to stop once it exits that same area. Like this:

/* Official syntax */
animation-timeline:  view([ <axis> || <'view-timeline-inset'>]?);

Let’s start by defining the axes:

@media screen and (width >= 50rem) {
  /* same styles as before */

  .opposing-column {
    /* ... */
    animation-timeline: view();
    animation-range: entry cover;
  }
}

This is just partially what we want, but what we’re saying is we want the animation to (1) start the very moment is enters the scrollport (entry), and (2) end when it completely leaves the area (cover). We need to be explicitly about the insets because that’s what establishes the animation’s range relative to where it enters and exits. We want the full range, so the entry begins at 0% and the exit is when an item is covered at 100%.

@media screen and (width >= 50rem) {
  /* same styles as before */

  .opposing-column {
    /* ... */
    animation-timeline: view();
    animation-range: entry 0% cover 100%;
  }
}

Lastly, we’ll set the animation to run linearly — no need for the items to slow up or down as they scroll.

@media screen and (width >= 50rem) {
  /* same styles as before */

  .opposing-column {
    /* ... */
    animation-timing-function: linear;
    animation-timeline: view();
    animation-range: entry 0% cover 100%;
  }
}

OK, great. But what we haven’t done is create an animation. We’ve set up what we want it to do when it runs, but we need to define the actual movement.

I want to set up three separate CSS animations:

  1. One that translates (moves) the items upward in the first column.
  2. One that’s the reverse of the first animation for the items in the other column.

We could technically set the first animation on both of the outer columns, but I want a third one that is a little bit offset from the first so those columns appear staggered.

@keyframes scroll1 {
  from { transform: translateY(var(--opposing-mask)); }
  to { transform: translateY(calc(var(--opposing-mask) * -1)); }
}

@keyframes scroll2 {
  from { transform: translateY(calc(var(--opposing-mask) * -1)); }
  to { transform: translateY(var(--opposing-mask)); }
}

@keyframes scroll3 {
  from { transform: translateY(calc(var(--opposing-mask) * .66)); }
  to { transform: translateY(calc(var(--opposing-mask) * -.33)); }
}

We can create variables for these, of course, should we ever need to update them:

@media screen and (width >= 50rem) {
  :root {
    --opposing-bg: lightcyan;
    --opposing-mask: 3rem;
    --animation-1: scroll1;
    --animation-2: scroll2;
    --animation-3: scroll3;

    /* ... */
  }
}

…and apply them to each column:

@media screen and (width >= 50rem) {
  /* same styles as before */

  .opposing-column {
    /* same styles as before */
  }

  :where(.opposing-column:nth-of-type(1)) {
    animation-name: var(--animation-1);
  }
  
  :where(.opposing-column:nth-of-type(2)) {
    animation-name: var(--animation-2);
  }

  :where(.opposing-column:nth-of-type(3)) {
    animation-name: var(--animation-3);
  }
}

While we’re at it, we should disable the animations to respect the user’s settings for reduced motion (and remove the mask, otherwise it might look weird):

@media (prefers-reduced-motion: reduce) { 
  .opposing-column {
    animation: unset;

    &:before,
    &:after {
      content: unset;
    }
  }
}

Wrapping up

So yeah, scroll-driven animations are really, really cool. We’re still waiting for Firefox support as I’m writing this, but you can certainly wrap this in @supports to provide a default experience that uses thew scroll annotations and then set a fallback experience for non-supporting browsers, like running on a normal animation timeline:

@supports (animation-timeline: view()) {
  /* ... */
}

This is just toe-dipping into what scroll-driven animations can do, of course. What sort of things have you made or experimented with? Or would you approach this one differently? Let me know!


Using Scroll-Driven Animations for Opposing Scroll Directions originally handwritten and published with love on CSS-Tricks. You should really get the newsletter as well.

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

Migrating Agentic Code Python -> C# Part 5

1 Share

In the previous post we looked at implementing the Researcher in C#. In this, as promised, we’ll look at the Author and the Reviewer.

The Author is handed two objects when instantiated: the llm (an IChatClient object) and the chatOptions. Its primary method is InvokeAsync, which is passed the current ResearchState.

public class AuthorChain(IChatClient llm, ChatOptions chatOptions) : IAuthorChain
{
    public async Task<string> InvokeAsync(ResearchState state)
    {
        List<string> research = state.ResearchFindings;
        string researchText = research.Count > 0 ? string.Join("\n\n", research) : "No research available.";

Its expectation is that the state object will have research information from the Researcher. It creates its prompt based on the state and then sends that prompt, along with its options, to the llm. What it gets back is its first draft which it will pass to the Reviewer

       string prompt = Prompts.AuthorPromptTemplate
            .Replace("{main_task}", state.MainTask)
            .Replace("{research_findings}", researchText)
            .Replace("{draft}", state.Draft)
            .Replace("{review_notes}", state.ReviewNotes);

        try
        {
            ChatResponse response = await llm.GetResponseAsync(prompt, chatOptions);
            string content = response.Text;
            return !string.IsNullOrEmpty(content) ? content : "Draft in progress...";
        }
        catch (Exception e)
        {
            Console.WriteLine($"Author error: {e.Message}");
            return "Error generating draft. Please try again.";
        }
    }

All that’s left for the Author is to create its node

    public async Task<ResearchState> AuthorNodeAsync(ResearchState state)
    {
        Console.WriteLine("\n>>>Author");

        string draft = await InvokeAsync(state);
        Console.WriteLine($"Draft created: {draft.Length} characters");

        state.Draft = draft;
        state.RevisionNumber += 1;
        return state;
    }

That was short enough that we should look at the Reviewer while we’re here.

Like the Author, the Reviewer is passed the llm and the chatOptions. InvokeAsync gets the ResearchState and from the state it can get the current draft that the author created.

   public async Task<string> InvokeAsync(ResearchState state)
    {
        string draft = state.Draft;
        int revisionNum = state.RevisionNumber;

        if (draft.Trim().Length < 100)
        {
            return "APPROVED - Draft is minimal but acceptable.";
        }

        if (revisionNum >= 4)
        {
            return "APPROVED - Maximum revisions reached. The report is satisfactory.";
        }

        string prompt = Prompts.ReviewerPromptTemplate
            .Replace("{main_task}", state.MainTask)
            .Replace("{draft}", draft);

        try
        {
            ChatResponse response = await llm.GetResponseAsync(prompt, chatOptions);
            string content = response.Text;
            return !string.IsNullOrEmpty(content) ? content : "APPROVED";
        }
        catch (Exception e)
        {
            Console.WriteLine($"Review error: {e.Message}");
            return "APPROVED - Error in review, proceeding with current draft.";
        }
    }

Notice that the Reviewer is quite generous in marking drafts as APPROVED. You may want to handle some conditions differently.

Let’s create the Reviewer’s node and in the next post we can finally see how these nodes are used.

   public async Task<ResearchState> ReviewerNodeAsync(ResearchState state)
    {
        Console.WriteLine("\n>>REVIEWER");

        string review = await InvokeAsync(state);
        string preview = review.Length > 100 ? review[..100] : review;
        Console.WriteLine($"Review: {preview}...");

        bool isApproved = review.ToUpperInvariant().Contains("APPROVED");

        if (isApproved)
        {
            Console.WriteLine("\u2713 Draft APPROVED");
            state.ReviewNotes = "APPROVED";
            state.NextStep = "END";
        }
        else
        {
            Console.WriteLine("\u2717 Revisions needed");
            state.ReviewNotes = review;
            state.NextStep = "author";
        }

        return state;
    }

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