15 Aug 2026
TalkAndroid
The epic finale that crowns Rupert Grint’s career—why Harry Potter’s last chapter is hailed as his top-rated, must-see film
When it comes to Rupert Grint, one role will always define his place in movie history: Ron Weasley. To many fans, the epic conclusion of the Harry Potter saga not only marked a milestone in his career but also delivered the series' most celebrated and unforgettable chapter.
15 Aug 2026 6:00am GMT
Boba Story Lid Recipes – 2026
Look no further for all the latest Boba Story Lid Recipes. They are all right here!
15 Aug 2026 3:10am GMT
Dice Dreams Free Rolls – Updated Daily
Get the latest Dice Dreams free rolls links, updated daily! Complete with a guide on how to redeem the links.
15 Aug 2026 3:09am GMT
12 Aug 2026
Android Developers Blog
Enhance your app for the new Pixel lineup: Unveiled at Made by Google
Made by Google expands what's possible across the Android ecosystem. With the introduction of the Pixel 11 Pro Fold, Pixel Watch 5, and the entire Pixel family, users are moving seamlessly across diverse screen sizes, unique postures, and intelligent experiences. For you, the developer, this represents a massive opportunity: foldable users spend about 14x more than standard phone users. To help you elevate your existing experience without starting from scratch, we're sharing our latest platform guidance alongside real-world examples from developers already putting these features into production.
Deliver adaptive experiences across foldables and expanded displays
The Pixel 11 Pro Fold gives your app a chance to flex its capabilities with an expanded inner display and a standard size outer screen. Building for the foldable form factor requires dropping hardcoded layout rules and designing around available window space. Leveraging Jetpack Compose APIs like Navigation 3 with Scene strategies or our newest layout APIs like Grid and FlexBox allows your layout containers to automatically wrap, span, and reflow. You can also use the experimental MediaQuery API to dynamically adapt your UI to environmental signals like foldable posture, and keyboard states.Building adaptively requires tracking actual app dimensions rather than physical device size, especially during split-screen and multitasking flows. Using Window Size Classes from the WindowManager library allows your layout to respect folds and hinges as natural content separators.
For instance, Notability leveraged Material 3 Window Size Classes to create a responsive two-pane layout that transitions smoothly between folded and expanded screens. As Ryan Shea, Android Engineering Manager at Notability, shared, tracking the window itself allows their layout and canvas zoom to ensure notes stay fit to the page through every fold, rotation, or split-screen resize, noting that they wanted the app "to feel native at every size, not just stretched to fit."Ensuring these transitions feel seamless also requires state preservation across configuration changes. Using ViewModel retains UI state so interactions like scroll position, form inputs, and open dialogs remain uninterrupted when transitioning between inner and outer screens.
Taking this approach, Flo Health used Jetpack Compose state primitives, ViewModel, and Window Size Classes to make their highest-traffic user journeys resilient to rotation, fold/unfold and resizing transitions. As Aleksandr Kolodiazhnyi, Senior Android Engineer at Flo Health, shared, "Android's adaptive guidance turned what looked like a major refactor into a templated rollout," allowing them to adopt Compose primitives without a rewrite, "cutting [their] state-preservation code by roughly 30% while fixing lifecycle and analytics correctness issues that improved the app on every form factor."
To take full advantage of the foldable form factor, leverage FoldingFeature updates to trigger posture-specific layouts. When a user partially folds their device into tabletop posture, you can split your UI automatically by placing primary controls on the lower display and main content or viewfinders on the upper display.
Handling camera previews across foldable state changes, requires managing orientation shifts carefully. Migrating to the CameraX library ensures automatic handling of sensor rotation and display scaling across screens, while existing Camera2 codebases can also achieve stability using the CameraViewfinder library. These camera and display capabilities allow you to power dual-screen previewing and high-resolution rear camera selfies with minimal custom logic.
Prepare your app for these form factors today by exploring our complete adaptive development guidance at Build adaptive apps.
Bring delightful, gesture-driven experiences to the wrist
The new Pixel Watch 5 is here, and we've optimized it to take advantage of the intelligent, power-efficient, touch-free convenience of Wear OS 7. Thanks to system-wide performance optimizations and a collection of new features built to help users complete tasks efficiently, you can provide rich experiences that require only a single user action to complete.The one-handed gestures framework provides a convenient way for users to interact with their watches without needing to touch the screen with their opposite hand. Starting with the 1.7 beta release of Compose for Wear OS 7, you can seamlessly integrate one-handed gesture control into your Wear Compose apps with simple physical inputs on the watch-wearing arm, like a double-pinch or wrist turn.
Spotify is adopting this framework to make controlling media more effortless. By mapping Wear OS gesture events directly to the media player state, users will be able to pause or resume playback using a simple double-pinch, keeping music controls accessible even when their hands are full.
Wear OS 7 also brings Live Updates directly to the wrist to surface real-time information like live sports scores, workout progress, and delivery status, which can also appear in the At-a-Glance surface on Pixel Watch 5. For example, Just Eat uses Live Updates to keep users informed on order arrival times at a glance. You can publish updates locally from your watch app or leverage phone notification bridging on supported devices to deliver real-time tracking across screens.
You can also extend glanceable interactions across watch surfaces on Wear OS 7 by using Wear Widgets, powered by Jetpack Glance and RemoteCompose. Wear Widgets with Compose offer greater expressiveness and consistency than the old Tiles framework, and the two available widget layouts-small and large- align perfectly with the 2x1 and 2x2 formats on mobile, ensuring your designs feel cohesive across devices.
On top of all these great new features, Wear OS 7 delivers up to a 10 percent improvement in battery life over Wear OS 6, making the Pixel Watch 5 a truly indispensable all-day companion for your users.
To get started developing for Wear OS 7, use the new emulator, and check out all of our Wear OS resources and guidance at Build apps for the wrist with Wear OS.
Unlock on-device intelligence with Gemini Nano 4
Pixel 11 devices are built to run Gemini Nano 4, bringing fast, responsive, on-device intelligence to the hardware. By running AI workflows directly on device, you can offer low-latency, real-time interactions that feel instant and integrated without needing round trips to the cloud.
Through the ML Kit GenAI Prompt API, you can send natural language requests directly to Gemini Nano on device. The model supports over 140 languages, better multimodal understanding, and much more. Build intelligent on-device features using advanced capabilities like structured output and thinking mode.
Build smart capabilities into your app using our self-service tools and Gemini models.
Shape the next generation of experiences for the Pixel ecosystem today
Made by Google showcases what's possible when hardware and software evolve together, and you are at the center of that innovation. You can begin optimizing your apps today by exploring our updated adaptive guidance, creating glanceable experiences for Wear OS 7, and integrating on-device AI with ML Kit.
To help you implement these updates even faster, you can now leverage Android skills, which provide AI-optimized instructions for agents and tools. Whether you are using Gemini in Android Studio or running the Android CLI through other agents, Android skills give your AI tools the context needed to execute complex workflows automatically. For instance, you can prompt your agent with the CameraX skill to handle camera display scaling across foldables, or use the Adaptive skill to set up dynamic Compose layouts without additional manual work.
Take advantage of these new surfaces, accelerate your workflow with agentic tools, and share your latest builds with the Android community! Head over to developer.android.com to access full documentation, explore the Android skills GitHub repository, and start building today.
12 Aug 2026 7:00pm GMT
Bring one-handed gestures to your Wear OS app

One-handed gestures offer a convenient and touch-free way for users to interact with their watches, enabling them to perform key actions using only the hand on which the device is worn.
First introduced on Pixel Watch with Wear OS 6.1, one-handed gestures made quick interactions effortless, such as starting and stopping a timer, accepting calls, and controlling media.
Now, with Wear OS 7, we're expanding this functionality with a new Gestures framework that allows OEMs to map gestures to primary actions and dismissals, and an API to bring gesture control to the developer community.
Starting with the 1.7 beta release of Compose for Wear OS, you can seamlessly integrate gesture control into your Wear Compose apps. To use this release, upgrade your Wear Compose dependency to:
androidx.wear.compose:compose-material3:1.7.0-beta01
Designing for one-handed interaction
The one-handed gestures framework is designed around two primary interaction patterns that allow users to take action without touching the screen:
- Primary action, which on Pixel Watch is mapped to a double-pinch gesture: this action should be mapped to the most important task in a given context. For example, users can perform this gesture to take a photo in a camera app, start/stop a timer, or accept an incoming call.
- Dismiss action, which on Pixel Watch is mapped to a wrist turn gesture: this action is mapped to system back by default and provides an intuitive way to close interruptive screens or get back to the watch face. It may be overridden for specific use cases, such as silencing an incoming phone call.
These gestures are currently available on Pixel Watch 3 and newer, and the Wear OS gesture framework is available to all Wear OS device manufactures to adopt.
Check out our new design guidance for integrating one-handed gestures into your Wear app.
Integrating gestures with Compose on Wear OS
To provide seamless gesture support in Wear OS 7, we are introducing a new Modifier.oneHandedGesture that you can apply to any existing interactive composable to make it gesture-aware.
Implementing gestures with Compose on Wear OS requires these steps:
- Define the gesture configuration. Start by using
rememberOneHandedGestureConfigurationto define the nature of the interaction. This configuration dictates the basic behavior by providing theGestureAction(e.g. tracking a primary pinch or a dismiss wrist flick). - Initialize the indicator state. Depending on your UI component, initialize a specific state object, such as
OneHandedGestureClickIndicatorStatefor buttons orOneHandedGestureScrollIndicatorStatefor scrollable lists. This state is used to coordinate visual feedback between the gesture detection modifier and the visual UI indicators, seamlessly managing visibility, timing, and animations. - Apply Modifier.oneHandedGesture to your interactive component. You'll pass in your configuration and state, and you'll provide standard callbacks:
onGestureAvailableto activate the visual hint when the system prepares the gesture, andonGestureto execute your action when the gesture happens.
The following sample shows how those three steps translate into code when configuring an IconButton:
val gestureConfig = rememberOneHandedGestureConfiguration(action = OneHandedGestureAction.Primary)
val indicatorState = remember { OneHandedGestureClickIndicatorState() }
val coroutineScope = rememberCoroutineScope()
OutlinedIconButton(
onClick = onPlayPauseButtonClicked,
modifier = Modifier.touchTargetAwareSize(IconButtonDefaults.LargeButtonSize)
.oneHandedGesture(
gestureConfiguration = gestureConfig,
interactionSource = interactionSource,
onGestureLabel = "play or pause",
onGestureAvailable = {
coroutineScope.launch { indicatorState.showIndicator() }
},
onGesture = onPlayPauseButtonClicked,
),
) {
// button content goes here
// See "Guided discovery with gesture indicators" section of this post for recommendations on adding a gesture indicator.
}
The GestureAction.Primary can also be used to scroll when the content is the end goal of the user journey, or there is a gesture actionable button off screen that the user can scroll to. Some examples include:
- Scrolling through a notification to view the content and/or initiate a reply (available in
TransformingLazyColumnandScalingLazyColumn). - Paging through workout metrics or other content that doesn't require the user to tap to continue the user journey (available in
HorizontalPagerandVerticalPager).
val scrollGestureConfig = rememberOneHandedGestureConfiguration(action = GestureAction.Primary)
val scrollIndicatorState = remember { OneHandedGestureScrollIndicatorState() }
val coroutineScope = rememberCoroutineScope()
TransformingLazyColumn(
state = scrollState,
contentPadding = contentPadding,
modifier = Modifier
.fillMaxSize()
.oneHandedGesture(
gestureConfiguration = scrollGestureConfig,
onGestureLabel = "scroll",
onGestureAvailable = {
coroutineScope.launch { scrollIndicatorState.showIndicator() }
},
onGesture = { OneHandedGestureDefaults.scrollDown(scrollState) }
)
) {
// list content goes here
// See "Guided discovery with gesture indicators" section of this post for recommendations on adding a gesture indicator.
}
Guided discovery with gesture indicators
To help users learn which gestures are available, gesture indicators work as hints to help discovery about which gestures are available on a screen.
These hints provide animated cues that inform users where they can perform a gesture. The framework manages the cadence and appearance of these hints, ensuring that they are helpful without being intrusive. System settings let users change the cadence to something less frequent if desired.
To integrate with hints, the API provides the following gesture indicator components:
- the OneHandedGestureClickIndicator for components like a
Button - the OneHandedGestureScrollIndicator component for scrolling
- the OneHandedGestureHorizontalPageIndicator for the
HorizontalPager - the OneHandedGestureVerticalPageIndicator for the
VerticalPager
The following example shows how to use the OneHandedGestureClickIndicator for a Button. See another example for using the OneHandedGestureScrollIndicator in our guidance.
val gestureConfig = rememberOneHandedGestureConfiguration(action = GestureAction.Primary)
val indicatorState = remember { OneHandedGestureClickIndicatorState() }
val coroutineScope = rememberCoroutineScope()
OutlinedIconButton(
onClick = onPlayPauseButtonClicked,
modifier = Modifier.touchTargetAwareSize(IconButtonDefaults.LargeButtonSize)
.oneHandedGesture(
gestureConfiguration = gestureConfig,
interactionSource = interactionSource,
onGestureLabel = "play or pause",
onGestureAvailable = {
coroutineScope.launch { indicatorState.showIndicator() }
},
onGesture = onPlayPauseButtonClicked,
),
) {
OneHandedGestureClickIndicator(
gestureConfiguration = gestureConfig,
indicatorState = indicatorState,
) {
val icon = if (playerUiModel.playbackState.isPlaying) Icons.Filled.Pause else Icons.Filled.PlayArrow
Icon(icon, contentDescription = "Play or Pause")
}
}
We are already seeing early adoption of these APIs from partners like Spotify, who are using one-handed gestures to make music control more seamless on the go. By adopting the Modifier.oneHandedGesture into their Wear OS app, Spotify allows users to play or pause their music with the primary gesture action, which on Pixel Watch devices is the double-pinch gesture. This action triggers the same behavior as the physical play/pause button, and the user doesn't need to touch the screen.
. Bring one-handed gestures to your app
You can begin experimenting with one-handed gestures today in the 1.7 beta release of Compose for Wear OS.
Ensure your app is running on Wear OS 7, which provides the underlying platform support for gesture detection. Check out our new one-handed gestures developer guide to see how you can start building more convenient experiences for your users.
12 Aug 2026 5:00pm GMT
What's new in the Jetpack Compose August '26 release

Today, the Jetpack Compose August '26 release is stable! This release brings version 1.12 across core Compose modules (see the full BOM mapping), introducing rich visual APIs like Mesh Gradients and Wide Color Gamut (WCG) support, structural layout features like named areas in Grid, seamless integration with Android's Credential Manager, and significant testing and performance improvements.
To update your project to today's release, upgrade your Compose BOM version to 2026.08.00:
implementation(platform("androidx.compose:compose-bom:2026.08.00"))
Breaking Changes
AGP & Compile SDK: Compose 1.12 updates compileSdk to API 37, requiring a minimum AGP 9.1.1. As a reminder, Compose will always target the latest compileSdk. Learn more about this change here.
Modifier.onFirstVisible() is deprecated: Migrate to Modifier.onVisibilityChanged(), which provides more precise visibility threshold tracking.
Graphics
Mesh Gradients
Compose 1.12 introduces MeshGradientPainter to help you create multi-point, organic color gradients.
val rows = 1
val columns = 1
val gradientPainter = remember {
MeshGradientPainter(rows, columns) {
// Parameters: row, column, position, color
setVertex(0, 0, Offset(0f, 0f), Color.Red) // Top-Left
setVertex(0, 1, Offset(1f, 0f), Color.Blue) // Top-Right
setVertex(1, 0, Offset(0f, 1f), Color.Green) // Bottom-Left
setVertex(1, 1, Offset(1f, 1f), Color.Yellow) // Bottom-Right
}
}
Box(
modifier = modifier
.aspectRatio(16/9f)
.fillMaxWidth()
.paint(gradientPainter)
)
For more information and examples, see the documentation.
Wide Color Gamut & HDR Support
Modern displays offer extended color fidelity and higher dynamic range. In Compose 1.12, full pipeline support for Wide Color Gamut (P3) and HDR rendering has been enabled across Compose graphics, paint, and shaders. Colors defined in non-sRGB color spaces (such as Display P3) are preserved through to platform rendering without color clamping. Colors will safely fall back to sRGB if they use an unsupported color space (e.g. CieXyz, CieLab, or Oklab), rely on a color space on an unsupported Android version (e.g Bt2020Hlg on Android 13 and below), or if the app is running on Android 9 (API 28) and below.
Other notable changes:
LayerOutsetswas added toGraphicsLayer&Modifier.graphicsLayer, which you can use to increase the visual bounds of the layer beyond its measured size. ApplyLayerOutsetsto avoid the implicitclipToBoundsbehavior when the layer is promoted to an offscreen buffer.
Styles
At Google I/O, we shared our early vision for the Compose Styles API-a unified, performant way to style components. Since then, we have continued building the underlying architecture to guarantee strict type safety and predictable correctness, and to support building custom design systems.
To ensure we get this foundational layer correct, the API will remain experimental, and you can expect breaking changes.
Runtime Optimizations
Keyed SideEffect Overload
SideEffect now supports key arguments, which lets you fire one-shot side effects whenever specific keys change. This can lead to better performance compared to using a LaunchedEffect or DisposableEffect when you don't need the coroutine or dispose block. SideEffect is up to 90% faster than LaunchedEffect and around 20% faster than DisposableEffect. Note that SideEffect runs its effect before DisposableEffect and LaunchedEffect, so use caution if migrating existing effects to this API, especially for LaunchedEffects that rely on being dispatched to start after the current frame is completed.
@Composable
fun AnalyticsTracker(userId: String, screenName: String) {
SideEffect(key1 = userId, key2 = screenName) {
analytics.logScreenView(userId, screenName)
}
}
Animation
DeferredTargetAnimation has graduated out of experimental status.
Interactive Two-Stage Transitions
New composables: DeferredAnimatedContent and DeferredAnimatedVisibility allow creating delightful two-stage transitions, e.g. for predictive back gesture tracking.
Manual animation control: During a transition's deferred phase, animated properties (like scale or offset) can now be manually manipulated in real-time (e.g., tracking a swipe gesture).
Seamless handoff: Once the deferred phase ends, the transition engine takes over and performs a seamless handoff, including velocity transfer, to the automatic transition.
Shared element support: A new permitTransformDuringDeferredTransition flag in SharedContentConfig controls whether shared elements visually transform along with their parent containers during the deferred transition phase.
val state = remember { DeferredTransitionState(initialScreen) }
val transition = rememberDeferredTransition(state)
if (predictiveBackInProgress) {
state.defer(targetScreen)
} else {
state.animateTo(targetScreen)
}
transition.DeferredAnimatedContent(
targetState = targetScreen,
mutableTransformSpec = {
MutableContentTransform {
// Manually manipulate properties during the deferred phase
initialContentTransform { scale = swipeProgress }
}
}
) { screen ->
ScreenContent(screen)
}
Below are two demos of use cases where a gesture-driven animation is handed off to a triggered animation:
Text, Input & Platform Integrations
Editable Text Formatting
New APIs offer rich-text formatting for editable text in BasicTextField. You can now programmatically apply and manipulate inline character and paragraph formatting using SpanStyle and ParagraphStyle via the new addStyle() method inside a TextFieldBuffer scope (such as inside textFieldState.edit { ... } or an InputTransformation). Additionally, TextFieldBuffer provides getSpanStyles() and getParagraphStyles() APIs that return TrackedRange objects, allowing you to read, update, or remove applied styles. To complement formatting creation, TextFieldState now exposes a read-only textStyles property for querying active styles across ranges, while TextFieldBuffer provides originalTextStyles to inspect formatting state prior to an edit. Text formatting and custom annotations are persisted across configuration changes.
val state = rememberTextFieldState("Formatted text in Compose 1.12")
// Apply bold and color styles to a range of text
state.edit {
addStyle(
SpanStyle(fontWeight = FontWeight.Bold, color = Color.Blue),
start = 0,
end = 9
)
}
// Query active styles from TextFieldState
val currentStyles = state.textStyles
Text Selection
A new SelectionState API provides programmatic control and observability over text selection within a SelectionContainer. Hoisting a SelectionState object via rememberSelectionState() and passing into SelectionContainer exposes selectedTexts as a reactive list of AnnotatedStrings and provides methods like selectAll(), clear(), select(TextRange), and extendSelectionByWord().
Additionally, use getSelectableTexts() to retrieve all selectable text items in layout order and select text across composables in the SelectionContainer using a global range.
@Composable
fun ProgrammaticSelectionExample() {
val selectionState = rememberSelectionState()
Column {
Button(
onClick = { selectionState.selectAll() },
modifier = Modifier.disableSelectionClearOnTap()
) {
Text("Select All")
}
SelectionContainer(state = selectionState) {
Text("Text content to be selected programmatically.")
}
}
}
Credential Manager Integration
Compose text fields now natively integrate with Android's Credential Manager (API 34+) via the Autofill framework (below API 34 is handled by androidx.credentialslibrary). By attaching the new credentialRequest semantics property with CredentialRequestData, text inputs can prompt passkeys, saved credentials, or sign-in requests directly within the user input flow.
@Composable
fun LoginField(textFieldState: TextFieldState) {
val credentialData = remember {
CredentialRequestData(
// Specify Credential Manager request options
)
}
BasicTextField(
state = textFieldState,
modifier = Modifier.semantics {
credentialRequest = credentialData
}
)
}
Other notable changes:
- Support for font variation settings in downloadable fonts.
- Enabled auto-scrolling when dragging text selection beyond the viewport in
SelectionContainer. - Added support for automatic interaction sounds (clicks and focus navigation) to Compose components, with a new
SoundEffectOnInteractioncomposable to allow opt-out. Note that as a consequence of this change, semantics click listeners must now be called from the main thread, which may affect a small number of test cases. KeyboardTypenow includesDate,Time,DateTime, andSignedDecimal.BasicSecureTextFieldnow usesTextObfuscationMode.Systemby default, whileRevealLastTypedserves as an absolute override.
Layout Enhancements
Named Areas in Grid Layout
Building complex 2D layouts is now easier with named areas in the @Experimental Grid component. Rather than managing numeric column and row indices across items, you can define semantic regions in your GridConfigurationScope and position composables by area name.
@OptIn(ExperimentalGridApi::class)
@Composable
fun DashboardLayout() {
Grid(
config = {
area("header", row = 0, column = 0, rowSpan = 1, columnSpan = 2)
area("sidebar", row = 1, column = 0)
area("content", row = 1, column = 1)
gap(16.dp)
}
) {
HeaderSection(modifier = Modifier.gridItem(areaId = "header"))
NavigationSidebar(modifier = Modifier.gridItem(areaId = "sidebar"))
MainContentView(modifier = Modifier.gridItem(areaId = "content"))
}
}
For more information, see the documentation.
Performance
As with every release, we continue to invest in Compose's performance to ensure that the framework helps you to build beautiful, performant apps. In this release we've focused on improving startup performance and are now seeing Time to Initial Display (the time it takes for an app to produce its first frame) that is comparable to Views in our benchmarks.
Testing & Tooling Upgrades
Test Synchronization
Compose 1.12 introduces new test APIs designed to reduce test execution times and eliminate flakiness during state sampling:
-
hasPendingWork:Passively checks if the UI has pending work without advancing the clock, which is ideal for manual animation loops. -
runWithoutImplicitWait:Temporarily disables implicit synchronization when stepping through manual clock frames (e.g. animation tests). -
@Test fun testAnimationStateFast() { composeTestRule.mainClock.autoAdvance = false while (composeTestRule.hasPendingWork()) { composeTestRule.mainClock.advanceTimeByFrame() composeTestRule.waitForIdle() composeTestRule.runOnUiThread { composeTestRule.runWithoutImplicitWait { // This is most effective when querying multiple nodes in a single frame. // It prevents the redundant synchronization overhead that would // otherwise occur on every individual query. val box1 = composeTestRule.onNodeWithTag("Box1").fetchSemanticsNode() val box2 = composeTestRule.onNodeWithTag("Box2").fetchSemanticsNode() assertThat(box1.boundsInRoot.right).isAtMost(box2.boundsInRoot.left) } } } }Other notable changes:
- The
captureToImageAPI now allows you to capture a popup or dialog together with its anchor in a single bitmap. - Added
onRootWithViewInteractionto scope Compose semantic searches to specific Android Views. This simplifies testing hybrid UIs, such as RecyclerViews, without requiring unique test tags in production code. @PreviewWrapperannotations can now be applied to custom@MultiPreviewclasses, enabling reusable preview setups (such as custom themes) across multiple components.
Happy Composing!
Compose 1.12 makes app development easier and more expressive than ever, with mesh gradients, wide color gamut support, downloadable variable fonts, Credential Manager integration, and faster testing tools. As always, we value your input, so please share your feedback on these changes or what you'd like to see next on our issue tracker. Happy composing!
- The
12 Aug 2026 4:00pm GMT
04 Jul 2026
Planet Maemo
Reticulum is interesting
It all started innocently enough: sometime last summer, I ran into the blog post Start your own Internet Resiliency Club on Hacker News.
…communicate with each other across a few kilometers without any centralized infrastructure using cheap, low-power, unlicensed LoRa radios and open source Meshtastic text messaging software.
The idea of a local, infrastructure-free communications mesh sounded useful, especially as we were about to sail into the Pacific.
Meshtastic
While conflicts and natural disasters are hopefully far away, on the smaller atolls there is no cellular network. With Meshtastic we could communicate over LoRa.

Over the hurricane season, the Meshtastic setup became quite extensive. Our boat has a Meshtastic node, plus a mast-mounted solar repeater. We both have Meshtastic cards that we carry with us. With these we can communicate with text messages over quite a long distance. And we get telemetry and alerts from the boat.
In Cartagena, Colombia we could hear the boat pretty much across the city. And since some of our buddy boats also run Meshtastic, we've even had conversations while offshore.
While the existing Meshtastic setup is serving us well, there is always room for improvement and new ideas.
Reticulum
Reticulum is a project that seeks to take this to a whole new level. It is a whole decentralized networking stack that allows anything from instant messaging and voice calls to full-on SSH sessions to be carried over a multitude of different interfaces. You can transport Reticulum over LoRa, Bluetooth, and also over regular TCP/IP networks. And if authorities didn't take a dim view on encryption in ham radio, it would also work over our HF radio. With store-and-forward mechanisms it can deal with intermittent connectivity.
Because your identity is portable, your connectivity can be fluid. You can be sitting at a desk connected to a fiber backbone one moment, and walking through a field connected only to a long-range LoRa mesh the next. To the rest of the network, nothing has changed. Your friends do not need to update your contact info. The messages they send do not bounce back. The network senses the shift in the medium and reroutes the flow of data automatically.
You are no longer a stationary node in a fixed grid. You are a wanderer in a fluid medium.
- The Zen of Reticulum
As it stands now, Reticulum is still quite an early system with rudimentary and tech-heavy user interfaces. But that seems to be about to change: the Columba app for Android seems about as user-friendly as Meshtastic or something like Signal. There's a lot of potential in that once it reaches a stable version.
Distributed development over Reticulum
In the meanwhile, there is one aspect of Reticulum we developers can benefit from immediately: Distributed development. With it, any rngit node running on Reticulum can be your "GitHub". Git history, issue tracking, release distribution is already there.
I recently switched my various programming projects over. We have rngit running on the boat NAS, and VPS running a mirror behind more consistent connectivity. And for now I also mirror the work periodically to GitHub for backwards compatibility.
Reticulum for software
What I think is worthwhile to explore is having machines interface with Reticulum. Just like we can tell our boat to switch lights on via a Meshtastic message, we should be able to do the same with Reticulum. And maybe there should be a NomadNet "site" for the boat showing status of the various systems.
Going further, maybe boats could share chart data, depth soundings, weather information with each other over this. The promise of VDES, but built from the grassroots perspective.
And maybe things like NoFlo should be able to communicate over Reticulum? Reticulum implementations exist for multiple programming languages, but for this we'd need a JavaScript port.
There's still a lot to study and to think about. Watch this space. Last time I noted that something is interesting, it took me to a ten year rabbit hole.
04 Jul 2026 12:00am GMT
26 Jan 2026
Planet Maemo
Igalia Multimedia contributions in 2025
Now that 2025 is over, it's time to look back and feel proud of the path we've walked. Last year has been really exciting in terms of contributions to GStreamer and WebKit for the Igalia Multimedia team.
With more than 459 contributions along the year, we've been one of the top contributors to the GStreamer project, in areas like Vulkan Video, GstValidate, VA, GStreamer Editing Services, WebRTC or H.266 support.
In Vulkan Video we've worked on the VP9 video decoder, and cooperated with other contributors to push the AV1 decoder as well. There's now an H.264 base class for video encoding that is designed to support general hardware-accelerated processing.
GStreaming Editing Services, the framework to build video editing applications, has gained time remapping support, which now allows to include fast/slow motion effects in the videos. Video transformations (scaling, cropping, rounded corners, etc) are now hardware-accelerated thanks to the addition of new Skia-based GStreamer elements and integration with OpenGL. Buffer pool tuning and pipeline improvements have helped to optimize memory usage and performance, enabling the edition of 4K video at 60 frames per second. Much of this work to improve and ensure quality in GStreamer Editing Services has also brought improvements in the GstValidate testing framework, which will be useful for other parts of GStreamer.
Regarding H.266 (VVC), full playback support (with decoders such as vvdec and avdec_h266, demuxers and muxers for Matroska, MP4 and TS, and parsers for the vvc1 and vvi1 formats) is now available in GStreamer 1.26 thanks to Igalia's work. This allows user applications such as the WebKitGTK web browser to leverage the hardware accelerated decoding provided by VAAPI to play H.266 video using GStreamer.
Igalia has also been one of the top contributors to GStreamer Rust, with 43 contributions. Most of the commits there have been related to Vulkan Video.
In addition to GStreamer, the team also has a strong presence in WebKit, where we leverage our GStreamer knowledge to implement many features of the web engine related to multimedia. From the 1739 contributions to the WebKit project done last year by Igalia, the Multimedia team has made 323 of them. Nearly one third of those have been related to generic multimedia playback, and the rest have been on areas such as WebRTC, MediaStream, MSE, WebAudio, a new Quirks system to provide adaptations for specific hardware multimedia platforms at runtime, WebCodecs or MediaRecorder.
We're happy about what we've achieved along the year and look forward to maintaining this success and bringing even more exciting features and contributions in 2026.
26 Jan 2026 9:34am GMT
05 Dec 2025
Planet Maemo
Meow: Process log text files as if you could make cat speak
Some years ago I had mentioned some command line tools I used to analyze and find useful information on GStreamer logs. I've been using them consistently along all these years, but some weeks ago I thought about unifying them in a single tool that could provide more flexibility in the mid term, and also as an excuse to unrust my Rust knowledge a bit. That's how I wrote Meow, a tool to make cat speak (that is, to provide meaningful information).
The idea is that you can cat a file through meow and apply the filters, like this:
cat /tmp/log.txt | meow appsinknewsample n:V0 n:video ht: \
ft:-0:00:21.466607596 's:#([A-za-z][A-Za-z]*/)*#'
which means "select those lines that contain appsinknewsample (with case insensitive matching), but don't contain V0 nor video (that is, by exclusion, only that contain audio, probably because we've analyzed both and realized that we should focus on audio for our specific problem), highlight the different thread ids, only show those lines with timestamp lower than 21.46 sec, and change strings like Source/WebCore/platform/graphics/gstreamer/mse/AppendPipeline.cpp to become just AppendPipeline.cpp", to get an output as shown in this terminal screenshot:

Cool, isn't it? After all, I'm convinced that the answer to any GStreamer bug is always hidden in the logs (or will be, as soon as I add "just a couple of log lines more, bro"
0
0 
05 Dec 2025 11:16am GMT
18 Sep 2022
Planet Openmoko
Harald "LaF0rge" Welte: Deployment of future community TDMoIP hub
I've mentioned some of my various retronetworking projects in some past blog posts. One of those projects is Osmocom Community TDM over IP (OCTOI). During the past 5 or so months, we have been using a number of GPS-synchronized open source icE1usb interconnected by a new, efficient but strill transparent TDMoIP protocol in order to run a distributed TDM/PDH network. This network is currently only used to provide ISDN services to retronetworking enthusiasts, but other uses like frame relay have also been validated.
So far, the central hub of this OCTOI network has been operating in the basement of my home, behind a consumer-grade DOCSIS cable modem connection. Given that TDMoIP is relatively sensitive to packet loss, this has been sub-optimal.
Luckily some of my old friends at noris.net have agreed to host a new OCTOI hub free of charge in one of their ultra-reliable co-location data centres. I'm already hosting some other machines there for 20+ years, and noris.net is a good fit given that they were - in their early days as an ISP - the driving force in the early 90s behind one of the Linux kernel ISDN stracks called u-isdn. So after many decades, ISDN returns to them in a very different way.
Side note: In case you're curious, a reconstructed partial release history of the u-isdn code can be found on gitea.osmocom.org
But I digress. So today, there was the installation of this new OCTOI hub setup. It has been prepared for several weeks in advance, and the hub contains two circuit boards designed entirely only for this use case. The most difficult challenge was the fact that this data centre has no existing GPS RF distribution, and the roof is ~ 100m of CAT5 cable (no fiber!) away from the roof. So we faced the challenge of passing the 1PPS (1 pulse per second) signal reliably through several steps of lightning/over-voltage protection into the icE1usb whose internal GPS-DO serves as a grandmaster clock for the TDM network.
The equipment deployed in this installation currently contains:
-
a rather beefy Supermicro 2U server with EPYC 7113P CPU and 4x PCIe, two of which are populated with Digium TE820 cards resulting in a total of 16 E1 ports
-
an icE1usb with RS422 interface board connected via 100m RS422 to an Ericsson GPS03 receiver. There's two layers of of over-voltage protection on the RS422 (each with gas discharge tubes and TVS) and two stages of over-voltage protection in the coaxial cable between antenna and GPS receiver.
-
a Livingston Portmaster3 RAS server
-
a Cisco AS5400 RAS server
For more details, see this wiki page and this ticket
Now that the physical deployment has been made, the next steps will be to migrate all the TDMoIP links from the existing user base over to the new hub. We hope the reliability and performance will be much better than behind DOCSIS.
In any case, this new setup for sure has a lot of capacity to connect many more more users to this network. At this point we can still only offer E1 PRI interfaces. I expect that at some point during the coming winter the project for remote TDMoIP BRI (S/T, S0-Bus) connectivity will become available.
Acknowledgements
I'd like to thank anyone helping this effort, specifically * Sylvain "tnt" Munaut for his work on the RS422 interface board (+ gateware/firmware) * noris.net for sponsoring the co-location * sysmocom for sponsoring the EPYC server hardware
18 Sep 2022 10:00pm GMT
08 Sep 2022
Planet Openmoko
Harald "LaF0rge" Welte: Progress on the ITU-T V5 access network front
Almost one year after my post regarding first steps towards a V5 implementation, some friends and I were finally able to visit Wobcom, a small German city carrier and pick up a lot of decommissioned POTS/ISDN/PDH/SDH equipment, primarily V5 access networks.
This means that a number of retronetworking enthusiasts now have a chance to play with Siemens Fastlink, Nokia EKSOS and DeTeWe ALIAN access networks/multiplexers.
My primary interest is in Nokia EKSOS, which looks like an rather easy, low-complexity target. As one of the first steps, I took PCB photographs of the various modules/cards in the shelf, take note of the main chip designations and started to search for the related data sheets.
The results can be found in the Osmocom retronetworking wiki, with https://osmocom.org/projects/retronetworking/wiki/Nokia_EKSOS being the main entry page, and sub-pages about
In short: Unsurprisingly, a lot of Infineon analog and digital ICs for the POTS and ISDN ports, as well as a number of Motorola M68k based QUICC32 microprocessors and several unknown ASICs.
So with V5 hardware at my disposal, I've slowly re-started my efforts to implement the LE (local exchange) side of the V5 protocol stack, with the goal of eventually being able to interface those V5 AN with the Osmocom Community TDM over IP network. Once that is in place, we should also be able to offer real ISDN Uk0 (BRI) and POTS lines at retrocomputing events or hacker camps in the coming years.
08 Sep 2022 10:00pm GMT
Harald "LaF0rge" Welte: Clock sync trouble with Digium cards and timing cables
If you have ever worked with Digium (now part of Sangoma) digital telephony interface cards such as the TE110/410/420/820 (single to octal E1/T1/J1 PRI cards), you will probably have seen that they always have a timing connector, where the timing information can be passed from one card to another.
In PDH/ISDN (or even SDH) networks, it is very important to have a synchronized clock across the network. If the clocks are drifting, there will be underruns or overruns, with associated phase jumps that are particularly dangerous when analog modem calls are transported.
In traditional ISDN use cases, the clock is always provided by the network operator, and any customer/user side equipment is expected to synchronize to that clock.
So this Digium timing cable is needed in applications where you have more PRI lines than possible with one card, but only a subset of your lines (spans) are connected to the public operator. The timing cable should make sure that the clock received on one port from the public operator should be used as transmit bit-clock on all of the other ports, no matter on which card.
Unfortunately this decades-old Digium timing cable approach seems to suffer from some problems.
bursty bit clock changes until link is up
The first problem is that downstream port transmit bit clock was jumping around in bursts every two or so seconds. You can see an oscillogram of the E1 master signal (yellow) received by one TE820 card and the transmit of the slave ports on the other card at https://people.osmocom.org/laforge/photos/te820_timingcable_problem.mp4
As you can see, for some seconds the two clocks seem to be in perfect lock/sync, but in between there are periods of immense clock drift.
What I'd have expected is the behavior that can be seen at https://people.osmocom.org/laforge/photos/te820_notimingcable_loopback.mp4 - which shows a similar setup but without the use of a timing cable: Both the master clock input and the clock output were connected on the same TE820 card.
As I found out much later, this problem only occurs until any of the downstream/slave ports is fully OK/GREEN.
This is surprising, as any other E1 equipment I've seen always transmits at a constant bit clock irrespective whether there's any signal in the opposite direction, and irrespective of whether any other ports are up/aligned or not.
But ok, once you adjust your expectations to this Digium peculiarity, you can actually proceed.
clock drift between master and slave cards
Once any of the spans of a slave card on the timing bus are fully aligned, the transmit bit clocks of all of its ports appear to be in sync/lock - yay - but unfortunately only at the very first glance.
When looking at it for more than a few seconds, one can see a slow, continuous drift of the slave bit clocks compared to the master :(
Some initial measurements show that the clock of the slave card of the timing cable is drifting at about 12.5 ppb (parts per billion) when compared against the master clock reference.
This is rather disappointing, given that the whole point of a timing cable is to ensure you have one reference clock with all signals locked to it.
The work-around
If you are willing to sacrifice one port (span) of each card, you can work around that slow-clock-drift issue by connecting an external loopback cable. So the master card is configured to use the clock provided by the upstream provider. Its other ports (spans) will transmit at the exact recovered clock rate with no drift. You can use any of those ports to provide the clock reference to a port on the slave card using an external loopback cable.
In this setup, your slave card[s] will have perfect bit clock sync/lock.
Its just rather sad that you need to sacrifice ports just for achieving proper clock sync - something that the timing connectors and cables claim to do, but in reality don't achieve, at least not in my setup with the most modern and high-end octal-port PCIe cards (TE820).
08 Sep 2022 10:00pm GMT
.png)









