Surfin' Safari

Web Inspector Keyboard Shortcuts

Posted by Nikita Vasilyev on Tuesday, October 6th, 2015 at 7:00 am

Web Inspector is loaded with tools to make web development easier and more effective. To help you become more efficient, Web Inspector includes keyboard shortcuts for many commonly used actions. These shortcuts will help you streamline your interactions and reduce the effort needed to complete your work.

Keyboard Shortcuts

You can learn about the shortcuts by hovering over interface elements and examining their tooltips. We’ve compiled a handy reference list of useful shortcuts to make working with Web Inspector more effortless.

Debugger Shortcuts

Debugger Shortcuts

⌘Y Disable breakpoints
⌃⌘Y or ⌘\ Continue script execution
F6 or ⌘’ Step over
F7 or ⌘; Step in
F8 or ⇧⌘; Step out

These shortcuts work from any Web Inspector panel, not just Debugger. One exception is ⌘Y — when editing code, ⌘Y comments out a line.



⌘⇧F — global search, it searches in all resources.
⌘F — local search, search within currently selected panel (e.g. a resource or console).

CSS selectors and XPath can be used for both global and local search.

Hide an Element

The H key hides the currently selected element by assigning “visibility: hidden” to it. Unlike deleting a node (by pressing delete key or setting “display: none”), setting “visibility: hidden” doesn’t move any other elements on the web page.

Select Next and Previous Tabs

Much like ⌘⇧] and ⌘⇧[ select next and previous tabs respectively in Safari, ⌘⇧] and ⌘⇧[ select next and previous tabs in WebKit Inspector when it’s undocked.

Toggle Split Console

The Esc key focuses on the quick console. Once it’s focused, pressing Esc toggles the split console.

Console Filters

Several filters can be selected by clicking while holding key.

Clear console

Clear console

Console can be cleared not only by clicking on the trash icon or by executing console.clear(), but also by pressing ⌘K or ⌃L.


All these keyboard shortcuts are available in both Safari 8 and 9. Please message @webkit on Twitter about keyboard shortcuts you like, don’t like or want to be implemented. If it doesn’t fit into 140 characters, file a bug report.

Scroll Snapping with CSS Snap Points

Posted by Wenson Hsieh on Tuesday, September 15th, 2015 at 7:00 am

Many of you, as web developers, are trying to accomplish sophisticated scrolling effects such as paginated scrolling. WebKit now supports paginated scrolling through CSS Snap Points. Though some JavaScript scrolling libraries already allow you to implement paginated scrolling on your websites and web views, these approaches have important weaknesses – first, they run JavaScript on every frame of a scrolling animation, which WebKit’s fast scrolling infrastructure cannot optimize. Second, the current event model standard does not expose trackpad scrolling phases, making it difficult for you to get optimal paged scrolling using JavaScript. Fortunately, with scroll snapping, you won’t need information about scrolling phases to implement great paginated scrolling.

New CSS Properties

Scroll snapping allows you to define special points in the content of a scrollable container using CSS. When a user scrolls in this container, the scroll offset will come to rest at one of these special offsets. To motivate snap scrolling, consider this horizontally scrolling gallery:

Notice how scrolling can leave us at an awkward position where we see a sliver of one image and only part of the next. Let’s look at how we can use the new scroll-snap-* properties to achieve paginated scrolling. Note that the examples shown in this post are all available here.

Scroll Snap Type

By default, scroll-snap-type on the parent scrolling container is none, which indicates that the container opts out of any scroll snapping behavior. To opt in, we set the value to be mandatory, which guarantees that the visual viewport of the container will rest on a snap point when there are no active scrolling operations. Also note that another value for scroll-snap-type is proximity, which indicates that scrolling may come to rest on a snap point only if scrolling takes us near the snap point. Currently, WebKit only supports mandatory snapping.

Scroll Snap Points-X and -Y

The scroll-snap-points-x and scroll-snap-points-y properties determine how to place snap points along the horizontal and vertical axes of the scroll snap container, respectively. The values here are none (the default) or repeat(<length>), where <length> indicates any type of expression indicating a length. These include raw pixel values, viewport units, percentages (relative to the container’s padding box) and even calc() expressions. Please note that negative repeat() values are handled as parsing errors, and any value that comes out to less than 1px is clamped to 1px. A repeat() value indicates that snap points should be placed at regular intervals along the x or y axes, respectively. Using the above two properties, here is an example of mandatory scroll snapping with horizontal snap points at every 100% of the container width using repeat(100%). If you’re on a browser that supports scroll snapping, you can also check it out here.

Each snap point is located at the top left corner of each image. Scroll snapping makes each snap point come to rest at the top left corner of the container, marked by the blue cross.

Scroll Snap Coordinate

If our images are different sizes, we won’t be able to use repeat(), since the interval will vary from image to image. Instead, we can use scroll-snap-coordinate on a child element to generate one or more snap points relative to each child element. This property accepts either “none” or a space-separated list of coordinates. Each coordinate is of the form <length> <length>, representing the x and y positions of the coordinate, respectively. Just like before, each <length> is generalized to match any generic length value, and percentage-based length values use the dimensions of the child element’s border-box. The following example places a snap coordinate at each child element’s top left corner, allowing us to snap to unevenly spaced elements. Click here to try it out.

Scroll Snap Destination

Another way to improve the gallery would be to center each image relative to the container. Up until now, we’ve been aligning each snap point to the top left corner of the parent container. However, by changing scroll-snap-destination, we’re able to manipulate where snap points animate to. The destination value is a single position consisting of two space-separated <length>s. Percentage-based values are computed relative to the padding box of the scroll snapping container. We use this property in conjunction with scroll-snap-coordinate on each child element to animate each child element to the center of the container. Click here to see it in action.

Notice that the position of the blue cross is now in the center of the container, since we set the destination to 50% 50%. The snap points are also now in the center of each child element, so the center of each child element now comes to rest at the center of the container.

Additional Details

Our scroll snapping implementation supports 2D snapping as well, when scroll snap points in both axes are active or when elements are positioned in a grid-like fashion in a container. Scrolling diagonally animates the scroll offset on a curved path to its destination. Check it out here.

We also support scroll snapping on containers to which CSS transformations have been applied. Here is a link to this last example.

Standards Compliance and Future Work

We currently only support the most complete part of the snap points specification: 2D scroll snapping to child elements that are aligned on a grid. This means you may find more snap points than expected if your 2D scrolling container contains scroll snapping elements of uneven sizes, such as in a masonry grid layout.

The CSS Scroll Snap Points specification is still a work in progress. We are considering the CSS Scroll Snapping Change Proposal here. Once a consensus is reached we will either implement the alternative scrolling model, or implement the proximity type for scroll snapping, tracked by bug 135994.

Since the specification is still in flux, we have prefixed our implementation with -webkit in keeping with the W3C requirements for features that have not completed the standardization process. We intend to unprefix our scroll snapping properties once the specification approaches its final state.

The spec also does not explicitly state how to handle unreachable snap points. Our current implementation clamps all x and y offsets of each snap point to the min and max scroll offsets of the scroll snapping container. Furthermore, to prevent content from being unreachable, we emit snap points at the min and max scroll offsets of the container as well.

You can use scroll snapping on the nightlies, as well as Safari on OS X El Capitan and iOS 9. We hope you’ve enjoyed reading about scroll snapping, and we’re excited to see what you create with it!

Introducing the Rendering Frames Timeline

Posted by Matt Baker on Tuesday, September 8th, 2015 at 7:00 am

Being able to measure performance and identify bottlenecks is essential for web developers of modern web applications. Delivering a smooth user experience can be a challenge as complex scripts, dependencies on third-party JavaScript frameworks, and high performance animations become the norm. Most of today’s devices have displays which refresh at 60 Hz, which means our apps need to update at a consistent 60 frames per second.

Web Inspector includes a new tool to help locate and resolve rendering performance issues. In addition to the familiar event-based timelines, the Timelines tab now includes a Rendering Frames mode, a new timeline presenting a frame-based, task-specific view of existing profiler/instrumentation data.

Web Inspector Rendering Frames Timeline

The Event Loop

Each frame in the timeline represents a single cycle of the browser’s event processing loop, broken down into the tasks that executed during that time. During a single cycle of the event loop, the browser must:

  1. Handle events and timers
  2. Run JavaScript
  3. Perform style calculations and layout
  4. Paint and composite the page

If one or more of these tasks takes an excessive amount of time, frame rates can drop and page performance can suffer. To maintain a consistent 60 FPS frame rate, the time budget for each frame is about 16.667 ms, or 1 / 60 of a second.


Frame data is displayed in three panels: an overview graphrecords table, and summary chart. The graph panel (at the top of the view) shows a high-level overview of rendering times and long-running tasks. The records table (bottom half) shows details about what happened in every single frame, and the summary chart gives the aggregate time for the frame records currently selected in the timeline.

The Rendering Frames timeline plots discrete frames along the x-axis; each frame’s height corresponds to how long it took to render (lower is better). This is in contrast to other timelines in the Web Inspector, which plot events over time on the x-axis and don’t have a y-axis.

Rendering Frames Overview Graph

When there’s no script activity or layout changes to render, WebKit’s event loop remains idle. By default, the Rendering Frames timeline automatically hides these idle periods so you can easily compare periods of activity.

The above example shows a profile taken from a page that performed some work once per second with a call to setInterval. According to the Start Time column, each frame is about one second apart, and the interval between frames – idle time – is not shown.

Identifying Performance Problems

When web content has poor rendering performance, this shows up as tall bars in the timeline or frames with large values in the Total Time table column. Filters, which are discussed in the next section, can also aid in highlighting just those frames which consistently fail to meet our 60 FPS goal. The next step is determining what script or layouts are eating up our frame budget:

Filtering Data

When rendering up to 60 frames per second, complex web content can quickly generate large recordings that obscure infrequent, but annoying, frame drops. The Rendering Frames timeline adds two new filter types to help reduce the noise: a task filter and frame duration filter. Both filter options are located in the Timelines sidebar panel.

As with all timeline views, only the range of data selected in the timeline overview graph is shown in the records table.

Frame Filtering Options

All selected frames are shown in the overview graph and data grid by default. In the example below, this includes many frames well within the ~16 ms budget, and quite a few that are little more than a blip on the graph:

Unfiltered Frame Data

Duration filters are useful for hiding frames that don’t take a significant time to render. Below is the same data after filtering out frames which took less than 15 ms to render:

Filtered Frame Data

Filtered frames are de-emphasized in the overview graph, and hidden in the timeline records table. Applying filters reduces the amount of profile data which must be investigated. Data can be filtered even further by applying task filters:

Frame Task Filters

Final Thoughts

Knowing how your site spends its time is crucial for delivering smoothly rendered content and a snappy user experience. The Rendering Frames timeline is a great tool for identifying performance issues, and this is just the beginning. There are many enhancements and features planned for this and other areas of the Web Inspector, and we’re interested in hearing your feedback.

The Rendering Frames timeline is available to use in the Safari 9 beta, and the latest features and enhancements can be found in WebKit Nightly Builds. Help us make Web Inspector even better by sending us feedback on Twitter (@xeenon, @jonathandavis, @matt_a_baker), or by filing a bug.

Styles Sidebar Refinements in Web Inspector

Posted by Devin Rousso on Monday, August 31st, 2015 at 7:00 am

For many users, the most important part of a webpage is how it looks. If a website doesn’t appeal to our senses we quickly move on. This is why design is so critical when making and debugging websites. It’s why we worked hard to improve the interface with thoughtful enhancements to help developers work more efficiently and effectively when they are designing webpages using Web Inspector.

Finding the Right Style

Due to the cascading nature of CSS, it can often be difficult to find styles in the Styles sidebar. You can always look at the Computed panel to see the list of styles that are applied to the selected element, but that won’t help you find the source of that style. To resolve this, we have added go-to arrows that will appear next to computed styles. When clicked you are switched to the Rules panel and the corresponding style is highlighted.

Computed to rules

If instead you want to see the list of declarations for a particular property or value, we have added a filter bar to the bottom of the Styles sidebar that will hide any style that does not match the filter term. As an example, you could filter for “background-color” and only the section that contain a “background-color” property will remain visible.

Filter bar

As an added bonus, the filter will also search selectors in the Rules panel, so you can search for a style specified by a particular selector, such as “#title h1”.

Filter selector

Intelligent Editing

There are over 200 different CSS properties, not even counting the prefixed ones, that developers have to take into account when designing websites and that can often get overwhelming. Did I remember to add all the prefixes for this property? Does this value exist for this property? Does this property even exist? These are all common questions that arise when editing CSS. In order to assist in answering these questions, Web Inspector now displays warning icons whenever a property has something wrong with it, whether that be from missing a prefix to not even existing. Simply hover over the icon to find out exactly what is wrong with the property and brief explanation of how to fix it.

Warning icon

In some cases, the fix is simple enough you can click on the warning icon and Web Inspector will automatically fix the issue, either by replacing or removing the problematic text.

Tabbing in the Styles sidebar has been functionally redesigned to provide an easier way to switch what text, whether that be a property name or value, is currently selected. In addition, Shift-Tabbing will perform the exact same selection change except in reverse, selecting the previous match. Once the last section of text is selected, tabbing again will highlight the next rule’s selector and start the entire process over in the next rule. This process wraps around from the last style to the first, allowing the user to completely tab through every editable property name and value in the Styles sidebar. While tabbing, any missing semicolons will also automatically be added for you.

Match Preview

Even if you know exactly what styles you want to apply to a particular element, finding the right selector to use for that style can be challenging. Previously, there was no way to tell if a selector would apply to an element other than actually creating a test style so that you could visually confirm the changes. Now, simply hovering over a selector will cause all matching nodes on the page to be highlighted.

Selector hovering

In addition, editing selectors will also apply highlights to all nodes matching the current text in the selector area. This way, you can preview what nodes will receive the styling without actually committing the selector.


What do you think? These enhancements are available in the WebKit Nightly Builds. Is there a feature you’d like to see added to the Styles sidebar? Let us know! Please feel free to submit any feedback through Twitter (@xeenon, @jonathandavis) or by filing a bug.

Type Profiling and Code Coverage Profiling for JavaScript

Posted by Saam Barati on Monday, August 24th, 2015 at 7:00 am

Web Inspector now has two great tools designed to make debugging JavaScript programs easier: the Code Coverage Profiler and the Type Profiler. The Code Coverage Profiler visually displays exactly which sections of your JavaScript program have executed. The Type Profiler visually annotates important variables with the collected set of type information for those variables. Both tools make understanding and debugging your JavaScript programs in Web Inspector better than ever.

Code Coverage Profiling

Understanding how a program works can be complex and complicated. Being able to grasp how a program works requires understanding which parts of the program execute based on a set of input data. In web applications, we’re often interested in knowing which parts of a program execute based on a user’s interaction with the web page. Web Inspector now has a way to show you exactly which parts of your program have run, and which haven’t.

Code Coverage Profiler

Knowing exactly which functions have executed, and even which if-then branches inside those functions have executed, is imperative in understanding the inner workings of your program’s control flow. Knowing when something has or has not executed gives you important insight into finding bugs and understanding how a program’s different pieces fit together. Web Inspector’s Code Coverage Profiler will tell you which portions of your program have executed up to basic block granularity.

One subtle detail you can see while watching this video is that the comments describing left and right are swapped. The Code Coverage Profiler a great tool for finding subtle mistakes in your program and your program’s documentation.

Type Profiling

One of the coolest new features in Web Inspector is the JavaScript Type Profiler. All values in JavaScript have a type, but, just by reading a JavaScript file or function, it’s hard to know what type everything will be. We created the Type Profiler because we no longer want to read JavaScript programs without knowing what type important variables have.

JavaScript is a dynamically typed language. This means that JavaScript programs are written without any type declarations. Any variable in any JavaScript program can have any type; expressions can have any type; any function can return any type; the list goes on. For example, the following is a legal JavaScript program:

let x = 20;
x = ["Hello", "World", "I am the Type Profiler"];
const identity = (x) => { return x; };
identity([1, 2, 3]);
identity("Saam Barati");

This is in contrast to a statically typed language like Swift or Haskell. Statically typed languages prevent you from mixing mismatched types. The compiler won’t let you run a program that fails its type checker. JavaScript doesn’t have this restriction. As long as a program doesn’t have a syntax error, it will run. For example, in a statically typed language, you can’t assign a number to a variable of type Array. JavaScript has no such restrictions.

This trivial example of a type mismatch seems easy enough to prevent. However, as your JavaScript program grows larger and the number of classes increases, it becomes untenable to keep track of all possible types and prevent their misuse. Even though JavaScript is not statically typed, variables in a JavaScript program are often intended to only have a particular type (this tendency towards monomorphism is a big part of why JIT compilers are successful at optimizing JavaScript programs). For example, consider this program:

function add(a, b) {
    return a + b;

The above function may have been written with the intention that a and b are both numbers. When a and b are both numbers, this function will behave as expected. For example:

add(10, 20) === 30

However, it begins to do weird things if it’s called with parameters that have an unintended type. For example:

add(10, "20") === "1020"
add([1, 2], undefined) === "[1,2]undefined"

The resulting values are weird and unexpected, but also totally valid according to the JavaScript spec. Even though the intention of the programmer is that the add function is to only be called with numbers, there is no easy and straightforward way to enforce this. Because type safety is hard to enforce, JavaScript programs often have bugs due to unexpected types leaking into places they were not meant to be. Web Inspector’s Type Profiler makes finding and debugging such problems easier than ever.

Sometimes type-related bugs are easy to diagnose, a mismatched type may throw a runtime TypeError. At other times, though, such problems won’t throw a TypeError and will result in subtle bugs that are difficult to diagnose and may only arise in rare situations.

Web Inspector’s Type Profiler helps you to find these subtle bugs. When using Web Inspector with the Type Profiler enabled, your JavaScript program will have type annotations next to important variables and function return types.

This allows you to visually inspect types in your JavaScript program without needing to insert console.log statements or pause in the debugger. console.log and the debugger are important debugging tools, but the Type Profiler is great for finding type-related bugs retroactively. The Type Profiler does what its name indicates: it profiles the types of values in your program. This means the information it shows you is only ever the types that have flowed through your program. It neither shows you the types something could be (which could be anything in JavaScript, this wouldn’t be helpful) nor does it infer what type something would be based on the information it already has.

The Type Profiler updates in real time. As new information is introduced in your program, the Type Profiler updates its annotations. As you see in the video, when the announceAnimal function is called a second time, the type displayed for the animal parameter updates from Dog to Animal. This happens because the Type Profiler tracks the inheritance chain for values it profiles. The Type Profiler also shows other aggregate types in an intelligent way. As you use the Type Profiler, you’ll see that the types it shows you are both intuitive and helpful.

The Type Profiler is also a great form of documentation. Using the Type Profiler will help you familiarize yourself with new code. Seeing type annotations while reading unfamiliar code makes understanding that code much easier. It also helps you better understand how your own code behaves.

You can see in the above image that the Type Profiler will display type names as String? and Number?. The Type Profiler is great at displaying optional parameters. Any <type> mixed with Undefined or Null will show up as <type>?. The Type Profiler will also recognize when a cohesive type name would be deceptive. This may happen when many unrelated types are assigned to the same variable, passed as an argument, or returned from a function. In this situation, the Type Profiler will display the type name as (many). When encountering (many), just hovering the type token will give you information about all the types that constitute that (many).

A Note on Compilation

Implementing a type profiler in a naive way would not be terribly difficult. You could imagine a naive implementation that works by rewriting JavaScript source code using abstract syntax tree transformations and wrapping the necessary language constructs with the required type profiling code. However, such an implementation would suffer from being unusably slow. It would probably incur a 20x slowdown if not more. When developing the Type Profiler, we set out to optimize its overhead as much as possible. On average, the Type Profiler will slow down JavaScript code by 2x.

This means that the Type Profiler is deeply integrated with JavaScriptCore’s JIT compilation infrastructure. Here is a quick overview of how JavaScriptCore’s (JSC) compiler pipeline works.

JSC first parses JavaScript text into an abstract syntax tree (AST). From this AST, JSC generates bytecode. This bytecode is a way of representing JavaScript operations at a lower level. The bytecode is not at such a low level that the original program is difficult to decipher from the bytecode. JSC will then interpret this bytecode and collect profiling information inside JSC’s Low Level Interpreter (LLInt). If a particular function has executed enough times, JSC will do a straightforward compilation of this bytecode into machine code inside JSC’s Baseline JIT. If this function continues to execute many times, JSC will compile it using its optimizing DFG and FTL JITs.

Let’s walk through an example of a JavaScript function and look at its corresponding bytecode representation:

    function add(a, b) { 
        return a + b 

JSC bytecode:

    function add: 10 m_instructions; 3 parameter(s); 1 variable(s)
    [ 0] enter             
    [ 1] get_scope       loc0
    [ 3] add             loc1, arg1, arg2
    [ 8] ret             loc1

This bytecode is straightforward. It says add the two parameters of the function together, store them in loc1, then return loc1. To see how the Type Profiler changes JSC’s bytecode, let’s see the same function’s bytecode with the Type Profiler enabled. (Comments added inline to describe what the Type Profiler is doing.)

    function add: 40 m_instructions; 3 parameter(s); 1 variable(s)
    [ 0] enter             
    [ 1] get_scope         loc0

    // Profile the parameters upon entering the function.
    [ 3] op_profile_type   arg1 
    [ 9] op_profile_type   arg2

    // Profile the operands to the add expression.
    [15] op_profile_type   arg1
    [21] op_profile_type   arg2
    [27] add               loc1, arg1, arg2

    // Profile the return statement to gather return type information for this function.
    [32] op_profile_type   loc1
    [38] ret               loc1

Because the type profiling machinery is compiled into JSC’s bytecode, JSC is then able to utilize its multi-tiered compiler infrastructure to optimize the overhead of type profiling. JSC’s DFG JIT is able to successfully optimize JavaScript code in large part due to the tendency that most JavaScript code is written with specific types in mind. Because of this, the DFG JIT can speculatively transform its IR based on gathered profiling information. Then, when executing this speculative code, if it finds that an assumption is broken at runtime, the DFG will OSR exit back into JSC’s baseline JIT. The bytecode operation op_profile_type is a very expensive operation and it appears with high frequency when the Type Profiler is enabled. When we transform this bytecode operation into DFG IR, we’re often able to optimize away the overhead of ProfileType by completely removing it from the executing code. We’re able to do this because by the time we decide to DFG compile the bytecode stream, it is likely that the types which were profiled in the Baseline JIT and the LLInt are the same types that the DFG will speculatively compile against. For example, if an op_profile_type operation has already observed a value with type Integer, it does not need to observe this type again because doing so adds no new information. If the DFG speculates that the type of a node is an Integer, and the type profiling information already associates this node with an Integer, then the DFG will remove the ProfileType node altogether. If this assumption is ever broken at runtime, the DFG will exit into the baseline JIT where this new type information will be recorded. As an example, here is the same add function represented using DFG IR at the time when bytecode IR is translated into DFG IR.

0:  SetArgument(this)
1:  SetArgument(arg1)
2:  SetArgument(arg2)
3:  JSConstant(JS|PureInt, Undefined)
4:  MovHint(@3, loc0)
5:  SetLocal(@3, loc0)
6:  JSConstant(JS|PureInt, Weak:Cell: 0x10f458ca0 Function)
7:  JSConstant(JS|PureInt, Weak:Cell: 0x10f443800 GlobalScopeObject)
8:  MovHint(@7, loc0)
9:  SetLocal(@7, loc0)
10: GetLocal(JS|MustGen|PureInt, arg1)
11: ProfileType(@10)
12: GetLocal(JS|MustGen|PureInt, arg2)
13: ProfileType(@12)
14: ProfileType(@10)
15: ProfileType(@12)
16: ValueAdd(@10, @12, JS|MustGen|PureInt)
17: MovHint(@16, loc1)
18: SetLocal(@16, loc1)
19: ProfileType(@16)
20: Return(@16)

There are still a lot of ProfileType operations in this IR. And these operations have the potential to be really expensive. However, the DFG speculates that the arguments to this function are integers. Because of this, the DFG IR at the time of generating machine code is able to remove all ProfileType operations under the speculative assumption that this function has integer arguments.

1:  SetArgument(arg1)
2:  SetArgument(arg2)
3:  JSConstant(JS|PureInt, Undefined)
4:  MovHint(@3, loc0)
7:  JSConstant(JS|PureInt, Weak:Cell: 0x10f443800 GlobalScopeObject)
8:  MovHint(@7, loc0)
10: GetLocal(@1, arg1)
12: GetLocal(@2, arg2)
16: ArithAdd(Int32:@10, Int32:@12)
17: MovHint(@16, loc1)
20: Return(@16)

Without optimizations like these, it would be unusably slow to use the Type Profiler while debugging your code.

If you’re interested in learning more about the Type Profiler, Code Coverage Profiler, or contributing to JSC or WebKit, please get in touch: @saambarati. There is a lot of interesting work still to be done and I’d be happy to point you in the right direction. You can also get in touch with @jonathandavis with any other questions and comments.