<link rel="stylesheet" href="//fonts.googleapis.com/css?family=Open+Sans%3A300%2C400%2C600%2C700">Where can i get something 3d printed veruswealth.net - Best 3d Printer Machine Brands, Companies, Price, Service

Men in Nursing: The Challenges of Men in a Female-Dominated Profession

  • March 23, 2019

For men in nursing, this rewarding career choice can often be strewn with challenges and obstacles. But, the challenges are starting to recede. Men are beginning to face fewer challenges in this field as time goes on, but there are a few things that still stand in the way.

Lack of Fitting Uniforms

Most websites selling nurse uniforms cater to women being that nursing is a historically female-dominated field. As such, male nurses find it difficult to find scrubs that are large enough and tailored toward the male anatomy. They need to be more narrow in the chest area, have longer torsos, broader shoulders, and a longer inseam to ensure a great fit.

How This is Changing – Fortunately for male nurses, there are more and more companies and websites dedicated to having at least a selection of male nurse scrubs. Murse World, in particular, is an exclusively male-oriented online scrub store. You’ll find different fits, styles, and patterns all in one place tailored specifically for men.

Lack of Male Role Models

male nurse role model

While it’s normal to see a male in the doctor’s role, having a male nurse for a role model may be more difficult to achieve. Although there is nothing wrong with a male nurse having a female nurse role model, there may be things that a male nurse can’t comfortably address with a female like they would a male.

How This is Changing – More and more males are beginning to go into nursing as a profession. For example, the number of male nurses who went to school for a Doctorate or Master’s degree in nursing jumped from 1,000 to 4,000 in a few years.

Perceived as Less Compassionate

When people think of compassionate nurses, the female nurse in a white nursing uniform is always one of the top images that come to mind. Male nurses typically get the view of being rough, not as caring, and more irritable. This less compassionate view can come from both the male nurse’s collegues and from the patients that the male nurses care for.

How This is Changing – As more men continue to enter the nursing workforce, both nurses and patients are starting to realize that this stereotype simply isn’t true. Male nurses go into this field because they care about their patients and their outcomes.

Role Expectations

male nurse

Many male nurses find themselves a victim of role expectations in the workplace. For example, many of their female colleagues ask them to lift heavy objects or heavy patients simply because they have the role expectation that their male colleagues are stronger and more muscled because they’re male. On the other side of the coin, outsiders see male nurses as more feminine for their career choice.

How This is Changing – People’s role expectations are slowly starting to evolve as more male nurses take on roles throughout the hospital. People are starting to realize that they shouldn’t try to enforce typical gender stereotypes on their colleagues, and see them as a nurse first and foremost.

Differing Communication Styles

Males and females have a different communication style. This means that male nurses used to have to conform and learn the female communication style because they took a career in a female-dominated industry. This can make professional interactions difficult, especially in stressful environments like in an emergency room or in labor and delivery.

How This is Changing – Hospitals and medical facilities are starting to shift their communication styles to be more professional between colleagues. This removes some of the gender-based communication, and it can make it easier for male and female nurses to interact and understand each other.

Barred from Obstetrics

male nurse obstetrics

Most obstetrics patients don’t have a problem with having a male doctor present during their birth. However, they seem to have a big problem with male nurses, and this is why hospitals barred male nurses from obstetrics for many, many years. There was no discussion about it, and male nurses knew that they simply weren’t allowed in labor and delivery.

How This is Changing – While this is still an ongoing issue, male nurses are no longer barred from labor and delivery. Some patients may request a female nurse, but male nurses do also work in this department with great success.

Shortage of Male Nurses

The healthcare industry’s need for nurses and the number of nurses available is quickly becoming wider and wider. Male nurses are less common, and the demand for them is going up as well. This means that there isn’t a shortage of jobs available, and the demand continues to rise.

How This is Changing – The number of males going into the nursing field continues on a steady climb. Numbers are up from years ago, and more males are starting to take interest due to guaranteed jobs and high job security.

See Also: Top 7 Jobs That Pay Well

Men in nursing do face a variety of unique challenges and hurdles that they have to overcome. However, each day brings more awareness to the fact that male nurses can be a valuable part of any hospital team, and that they’re an excellent way to add diversity into a hospital staff.

The post Men in Nursing: The Challenges of Men in a Female-Dominated Profession appeared first on Dumb Little Man.

Internet Access While Traveling: Tips for Keeping Your Data Safe

  • March 22, 2019

Are you tired of unreliable and painfully slow internet when traveling? Do you worry about your security?

It does feel like an endless battle waiting for apps to respond and pages to load. Imagine spending your precious traveling time looking at a blank screen instead of enjoying the beautiful environment.

To ensure you enjoy your internet access without worrying about your security while traveling, we have put together some really helpful tips.

Ensure you stick to secure sites

First, check the website if it’s secure. You can check if a site is secure by going through the security information using a trusted browser like Firefox or Chrome. You’ll know a site’s connection is safe when there’s a green lock on the left side of the URL.

It is important that you try avoiding entering sensitive information on non-secured sites, especially when using a public network. This includes entering credit card numbers, passwords, and other personal details.

See Also: 8 Easy Steps To Your Browser Security And Privacy

Avoid using apps

internet connection while tracvelling

App security is less stringent when compared to browser security. In case you are using apps from popular brands like Paypal, you will definitely be okay.

However, it is important that you try avoiding entering sensitive information into apps from not so popular companies. This is crucial, especially when using a similar password for various websites.

Switch off file sharing

Ensure that your files are secure.

In most cases, when you are using your laptop on the home network, you normally share folders with your parents, siblings or friends. This is okay as long as you remember to turn it off when connecting to a public Wi-Fi.

If you forget to turn off file sharing, every person who connects to the same Wi-Fi can view your files.

The most recent computers are smart and capable of automatically turning off file sharing when you connect to public Wi-Fi. However, it is advisable that you always double check.

Update your anti-virus

It is important that you never connect your devices to any free Wi-Fi network without an updated antivirus. Most smartphones and laptops these days come with built-in software like the Windows Defender. However, you are still advised to step up and download software like Avast, which is capable of giving you an extra layer of protection.

Use VPN

traveling with internet access

You can consider using a VPN, which will act as your private internet bodyguard. VPN will hide your IP address and encrypt your connection to ensure everything you send over the internet is hidden.

VPN is cheap and accessible. You should never have an excuse for failing to use one.

See Also: How to Set Up a VPN

Conclusion

The above measures will not make you bulletproof. However, they will help in reducing your chance of being targeted and improve your internet access while traveling.

The post Internet Access While Traveling: Tips for Keeping Your Data Safe appeared first on Dumb Little Man.

The Most Important Habits To Develop Before 30

  • March 22, 2019

You’re reading The Most Important Habits To Develop Before 30, originally posted on Pick the Brain | Motivation and Self Improvement. If you’re enjoying this, please visit our site for more inspirational articles.

.important habits

Looking at successful people, we tend to assume that they are cut from a different cloth. But once you factor in incremental progress, the image becomes less fuzzy around the edges. Whoever you think they are, and however you think they got there, what truly defines them is a set of important habits.

If you are in your mid-twenties, let me cut you a lot of suspense – the quality of your life, and perhaps even the length of it, will greatly depend on the routines you’re about to adopt.

Results and consequences, no matter how small, tend to accumulate year after year, so you better set your trajectory straight. I use 30 in my title in order to illustrate the importance of getting a head start. The sooner you begin to pay attention, the better. But let me give you a reassuring tap on the shoulder as well – even if you are pass the age of 30, there is still a chance to turn everything around if you just commit to a set of healthy habits.

I’ll walk you through a list of behaviors, trying to sell you the idea of small but incremental progress. The goal is to put everything on autopilot, making sure that you won’t fall off the wagon.

Develop a morning routine

Routines, according to Professor Jordan Peterson from the University of Toronto, will keep you grounded and therefore sane. There are a lot of people who would vouch for the morning routine, and although most of them share a different idea when it comes to structure, they certainly agree on the importance of it as a habit.

The morning routine is the first labor contraction towards developing a successful life. It is one of the most important habits. But there are several boxes to check – first of all, you have to make your bed. This, according to Admiral William H. McRaven, will give you a sense of accomplishment, pride, and élan to move forward throughout the day and tackle different challenges.

Then have some time for yourself, whether it is with a cup of coffee or a freshly brewed tea, and write in your journal.

Eat a healthy breakfast

Often times, people start to invest in personal development without first tackling the chemistry. But you must know this – failure to eat a proper breakfast will inevitably lead to a slower metabolism, hormonal imbalances, anxiety, and lack of energy. The problems will accumulate over the long run.

Meditate daily

I first stumbled across meditation after struggling with chronic OCD that I’ve been dragging along since my early childhood. Not knowing how to start was a big obstacle, and I had all of the usual stereotypes about the practice. But I’ve downloaded a couple of apps, and decided to persist with one of them.  This changed my life forever.

Meditation will teach you how to disentangle from your negative thoughts and emotions, and this is one of the most important skills in life.

Exercise within your zone of comfort

I’m the co-founder of a holistic fitness platform, but I’m not here to repeat popular mantras. Forget about pushing hard, and going beyond your limits – steady progress will outrank ambitious routines nine ways to Sunday. The key is to make it enjoyable, bearable, and part of your daily routine.

And you don’t have to work your butt off, trust me – even the benefits of Zumba will make a lot of difference on the long run.

Forget about processed sugar

The occasional cake won’t be a problem. However, habitual consumption of sugar and overly refined carbs, will definitely come at a price. Problems will manifest even before you reach your senior years. Unfortunately, in real life, ominous music rarely plays before things suddenly fall apart. Sugar consumption will bring about a garden variety of problems. Make sure to eliminate it from your diet as soon as you can.

Read at least one book per month

We always schedule reading for sometimes next week. But time goes by, and all of the sudden we cannot remember the last title that we’ve red.  If you are generally unsure where to start, here is a long list of books recommended by author Tim Ferriss, as he tries to catalogue the pieces of writing that influenced billionaires and some of the most interesting people in the world.

If however, you are not quite comfortable with reading, you can start with audio books or even podcasts. You need something in the long form that makes you think.


Slavko Desik is the editor in chief at Lifestyle Updated, and the co-creator of a very popular fitness platform. He loves to write about personal development, exercise, and a more holistic approach to physical and mental health.

You’ve read The Most Important Habits To Develop Before 30, originally posted on Pick the Brain | Motivation and Self Improvement. If you’ve enjoyed this, please visit our site for more inspirational articles.

Let's Encrypt Everything

  • March 22, 2019

I’ll admit I was late to the HTTPS party.

But post Snowden, and particularly after the result of the last election here in the US, it’s clear that everything on the web should be encrypted by default.

Why?

  1. You have an unalienable right to privacy, both in the real world and online. And without HTTPS you have zero online privacy – from anyone else on your WiFi, from your network provider, from website operators, from large companies, from the government.

  2. The performance penalty of HTTPS is gone, in fact, HTTPS arguably performs better than HTTP on modern devices.

  3. Using HTTPS means nobody can tamper with the content in your web browser. This was a bit of an abstract concern five years ago, but these days, there are more and more instances of upstream providers actively mucking with the data that passes through their pipes. For example, if Comcast detects you have a copyright strike, they’ll insert banners into your web contentall your web content! And that’s what the good guy scenario looks like – or at least a corporation trying to follow the rules. Imagine what it looks like when someone, or some large company, decides the rules don’t apply to them?

So, how do you as an end user "use" encryption on the web? Mostly, you lobby for the websites you use regularly to adopt it. And it’s working. In the last year, the use of HTTPS by default on websites has doubled.

Browsers can help, too. By January 2017, Google Chrome will show this alert in the UI when a login or credit card form is displayed on an unencrypted connection:

Additionally, Google is throwing their considerable weight behind this effort by ranking non-encrypted websites lower in search results.

But there’s another essential part required for encryption to work on any websites – the HTTPS certificate. Historically these certificates have been issued by certificate authorities, and they were at least $30 per year per website, sometimes hundreds of dollars per year. Without that required cash each year, without the SSL certificate that you must re-purchase every year in perpetuity – you can’t encrypt anything.

That is, until Let’s Encrypt arrived on the scene.

Let’s Encrypt is a 501.3(c)(3) non-profit organization supported by the Linux Foundation. They’ve been in beta for about a year now, and to my knowledge they are the only reliable, official free source of SSL certificates that has ever existed.

However, because Let’s Encrypt is a non-profit organization, not owned by any company that must make a profit from each SSL certificate they issue, they need our support:

As a company, we’ve donated a Discourse hosted support community, and a cash amount that represents how much we would have paid in a year to one of the existing for-profit certificate authorities to set up HTTPS for all the Discourse websites we host.

I urge you to do the same:

  • Estimate how much you would have paid for any free SSL certificates you obtained from Let’s Encrypt, and please donate that amount to Let’s Encrypt.

  • If you work for a large company, urge them to sponsor Let’s Encrypt as a fundamental cornerstone of a safe web.

If you believe in an unalienable right to privacy on the Internet for every citizen in every nation, please support Let’s Encrypt.

[advertisement] Find a better job the Stack Overflow way – what you need when you need it, no spam, and no scams.

SVG Circle Decomposition To Paths

  • March 22, 2019

SVG Circle Decomposition To Paths

SVG Circle Decomposition To Paths

Bryan Rasmussen

2019-03-22T13:00:08+01:00
2019-03-22T16:35:00+00:00

This article starts with a confession: I like to hand-code SVG. It’s not always the case but often enough it could seem peculiar to people who do not share my predilection. There are a good number of benefits in being able to write SVG by hand, such as optimizing SVGs in ways a tool can’t (turning a path into a simpler path or shape), or by simply understanding how libraries like D3 or Greensock work.

With that said, I’d like to look more closely at circular shapes in SVG and things we can do with them when we move past a basic circle. Why circles? Well, I love circles. They’re my favorite shape.

First off (hopefully you’ve seen a basic circle in SVG before), here’s a pen that shows one:

See the Pen circle by Bryan Rasmussen.

A lot of things can be done with a circle: it can be animated and it can have different colors applied to it. Still, there are two very nice things that you cannot have a circle do in SVG 1.1: You cannot make another graphical element move along the circle’s path (using the animateMotion element) and you cannot have shape a text along a circle’s path (this will only be allowed after SVG 2.0 is released).

Turning Our Circle Into A Path

There is a little online tool that can help you create paths out of circles (you can try it out here), but we’re going to do be creating everything from scratch so we can find out what’s really going on behind the scenes.

To make a circular path, we’re going to actually make two arcs, i.e. semicircles that complete the circle in one path. As you’ve probably noticed in the SVG above, the attributes CX, CY, and R respectively define where the circle is drawn along the X and Y axis, while R defines the radius of the circle. The CX and CY create the center of the circle, so the circle is drawn around that point.

Replicating that circle could look like this:

<path
    d="
      M (CX - R), CY
      a R,R 0 1,0 (R * 2),0
      a R,R 0 1,0 -(R * 2),0
    "
/>

Note that CX is the same as the cx attribute of the circle; the same goes for CY and the cy attribute of the circle, as well as R and the r attribute of the circle. The small a character is used to define a segment of an elliptical arc. You can use an optional Z (or z) to close the path.

The lowercase letter a denotes the beginning of an elliptical arc drawn relatively to the current position — or in our specific case:

<path
  d="
    M 25, 50
    a 25,25 0 1,1 50,0
    a 25,25 0 1,1 -50,0
  "
/>

You can see the magic happening in this pen:

See the Pen circle from path by Bryan Rasmussen.

Hidden underneath the path is a circle with a red fill. As you play around with values of the path, you’ll see that circle as long as the path totally covers the circle (the path itself is a circle of the same size), and we’ll know that we’re doing things right.

One thing you should also know is that as long as you are drawing relative arcs, you don’t need to repeat the a command for each arc you draw. When your first 7 inputs are done for your arc, the second 7 inputs will be taken for the next arc.

You can try this out with the pen above by removing the second a in the path:

a 25,25 0 1,1 50,0

25,25 0 1,1 -50,0

This may look the same, but I prefer to leave it in until I am ready to finish a drawing, and this also helps me to keep track of where I am.

How This Path Works

First, we move to an absolutely positioned X,Y coordinate in the image. It does not draw anything there — it just moves there. Remember that for a circle element CX, CY denotes the center of the circle; but as it happens in the elliptical arc, the true CX and CY of the arc will be calculated from the other properties of that arc.

In other words, if we want our CX to be at 50 and our radius is 25, then we need to move to 50 - 25 (if we are drawing from left to right, of course). This means that our first arc is drawn from 25 X, 50 Y which results to our first arc being 25,25 0 1,0 50,0.

Let’s break down what the value 25,25 0 1,0 50,0 of our arc actually means:

  • 25: The relative X radius of the arc;
  • 25: The relative Y radius of the arc;
  • 0 1,0: I’m not going to talk about the three middle values (rotation, large-arc-flag, and the sweep-flag properties) because they are not very important in the context of the current example as long as they are the same for both arcs;
  • 50: The ending X coordinate (relative) of the arc;
  • 0: The ending Y coordinate (relative) of the arc.

The second arc is a 25,25 0 1,0 -50,0. Keep in mind that this arc will start drawing from wherever the last arc stopped drawing. Of course, the X and Y radius are the same (25), but the ending X coordinate is -50 of where the current one is.

Obviously this circle could have been drawn in many different ways. This process of turning a circle into a path is known as decomposition. In the SVG 2 spec decomposition of a circle will be done with 4 arcs, however, the method it recommends is not possible to use yet, as it currently depends on a feature named segment-completing close path which has not yet been specified.

In order to show you that we can draw the circle in a lot of ways, I have prepared a little pen with various examples:

See the Pen all circles by Bryan Rasmussen.

If you take a closer look, you’ll see our original circle along with five different examples of how to draw paths on top of that circle. Each path has a child desc element describing the use of CX, CY and R values to build the circle. The first example is the one we discussed here while three others use variations that should be comprehensible from reading the code; the last examples uses four semicircular arcs instead of two, replicating somewhat the process described in the SVG 2 spec linked above.

The circles are layered on top of each other using SVG’s natural z-indexing of placing elements that come later in the markup on top of the ones that come earlier.

If you click on the circular paths in the pen, the first click will print out how the path is structured to the console and add a class to the element so that you will see the stroke color of how the circle is drawn (you can see that the first circle is drawn with a starting wedge from the stroke). The second click will remove the circle so you have the ability to interact with the circle below.

Each circle has a different fill color; the actual circle element is yellow and will say “You clicked on the circle” to the console whenever it is clicked on. You can also, of course, simply read the code as the desc elements are quite straightforward.

Going From A Path To A Circle

I suppose you’ve noticed that while there are many different ways to draw the circle, the paths used still look pretty similar. Often — especially in SVGs output from a drawing program — circles will be represented by paths. This is probably due to optimization of the graphics program code; once you have the code to draw a path you can draw anything, so just use that. This can lead to somewhat bloated SVGs that are hard to reason about.

Recommended reading: “Tips For Creating And Exporting Better SVGs For The Web” by Sara Soueidan

Let’s take the following SVG from Wikipedia as an example. When you look at the code for that file, you will see that it has a lot of editor cruft once you’ve run it through Jake Archibald’s SVGOMG! (which you can read more about here). You’ll end up with something like the following file which has been pretty optimized, but the circles in the document are still rendered as paths:

See the Pen Wikipedia Screw Head Clutch Type A by Bryan Rasmussen.

So, let’s see if we can figure out what those circles should be if they were actual circle elements given what we know about how paths work. The first path in the document is obviously not a circle while the next two are (showing just the d attribute):

M39 20a19 19 0 1 1-38 0 19 19 0 1 1 38 0z
M25 20a5 5 0 1 1-10 0 5 5 0 1 1 10 0z

So remembering that the second a can be left out, let’s rewrite these to make a little more sense. (The first path is the big circle.)

M39 20
a19 19 0 1 1-38 0
a19 19 0 1 1 38 0z

Those arcs are then obviously the following:

aR R 0 1 1 - (R * 2) 0
aR R 0 1 1 (R * 2) 0

This means that our circle radius is 19, but what are our CX and CY values? I think our M39 is actually CX + R, which means that CX is 20 and CY is 20 too.

Let’s say you add in a circle after all the paths like this:

<circle
 fill="none"
 stroke-width="1.99975"
 stroke="red"
 r="19"
 cx="20"
 cy="20"
/>

You will see that is correct, and that the red stroked circle covers exactly the large circle. The second circle path reformulated looks like this:

M25 20
a5 5 0 1 1-10 0 
5 5 0 1 1 10 0z

Obviously, the radius is 5, and I bet our CX and CY values are the same as before: - 20.

Note: If CX = 20, then CX + R = 25. The circle is sitting inside the bigger one at the center, so obviously it should have the same CX and CY values.

Add the following circle at the end of the paths:

<circle
 fill="yellow"
 r="5"
 cx="20"
 cy="20"
/>

You can now see that this is correct by taking a look at the following pen:

See the Pen Wikipedia Screw Head Clutch Type A_ with example circles by Bryan Rasmussen.

Now that we know what the circles should be, we can remove those unneeded paths and actually create the circles — as you can see here:

See the Pen Wikipedia Screw Head Clutch Type A optimized by Bryan Rasmussen.

Using Our Circular Path For Wrapping Text

So now that we have our circles in paths, we can wrap text on those paths. Below is a pen with the same paths as our previous “All Circles” pen, but with text wrapped on the path. Whenever you click on a path, that path will be deleted and the text will be wrapped on the next available path, like so:

See the Pen all circles wrapped Text by Bryan Rasmussen.

Looking at the different paths, you’ll see tiny differences between each one (more on that in a bit), but first there is a little cross-browser incompatibility to be seen — especially noticeable in the first path:

Firefox Developer
Chrome
Microsoft Edge

The reason why the starting “S” of “Smashing” is sitting at that funny angle in the Firefox solution is that it is where we actually started drawing our path at (due to the v-R command we used). This is more obvious in the Chrome version where you can clearly see the first pie-shaped wedge of our circle that we drew:

Chrome does not follow all the wedges, so this is the result when you change the text to be “Smashing Magazine”.

The reason is that Chrome has a bug regarding inheritance of the textLength attribute declared on the parent text element. If you want them both to look the same, put the textLength attribute on the textPath element as well as the text. Why? Because it turns out that Firefox Developer has the same bug if the textLength attribute is not specified on the text element (this has been the case for some years now).

Microsoft Edge has a totally different bug; it can’t handle whitespace in between the Text and the child TextPath element. Once you have removed whitespace, and put the textLength attribute on both the text and textPath elements, they will all look relatively the same (with small variations due to differences in default fonts and so forth). So, three different bugs on three different browsers — this is why people often prefer to work with libraries!

The following pen shows how the problems can be fixed:

See the Pen all circles wrapped Text fixed TextLength by Bryan Rasmussen.

I’ve also removed the various fill colors because it makes it easier to see the text wrapping. Removing the fill colors means that my little function to allow you to cycle through the paths and see how they look won’t work unless I add a pointer-events="all" attribute, so I’ve added those as well.

Note: You can read more about the reasons for that in “Managing SVG Interaction With The Pointer Events Property” explained by Tiffany B. Brown.

We’ve already discussed the wrapping of the multiarc path, so let’s now look at the others. Since we have one path we are wrapping on, the text will always move in the same direction.

ImagePathExplanation
M CX, CY
a R, R 0 1,0 -(R * 2), 0
a R, R 0 1,0 R * 2, 0
and uses the translate function to move +R on the X axis.
The starting position for our textPath (since we have not specified it in any way) is determined by our first ending arc -(R * 2), given the radius that the arc itself has.
M (CX + R), CY
a R,R 0 1,0 -(R * 2),0
a R,R 0 1,0 (R * 2),0
Same applies as the previous path.
M CX CY
m -R, 0
a R,R 0 1,0 (R * 2),0
a R,R 0 1,0 -(R * 2),0
Since we are ending at (R * 2 ) in our first arc, we will obviously be starting at the opposite position. In other words, this one starts where our previous two paths ended.
M (CX - R), CY
a R,R 0 1,1 (R * 2),0
a R,R 0 1,1 -(R * 2),0
This starts in the same position as the last one due to (R * 2), but it is running clockwise because we have set the sweep-flag property (marked in yellow) to 1.

We‘ve seen how to wrap text on a single path in a circle. Let’s now take a look at how we can break up that path into two paths and the benefits you can get from that.

Breaking Our Paths Into Parts

There are a lot of things you can do with the text in your path, i.e. achieving stylistic effects with tspan elements, setting the offset of the text, or animating the text. Basically, whatever you do will be constrained by the path itself. But by breaking up our multiarc paths into single arc paths, we can play around with the direction of our text, the z-indexing of different parts of our text, and achieving more complex animations.

First, we are going to want to use another SVG image to show some of the effects. I will be using the diamond from the article on pointer events which I mentioned earlier. First, let’s show what it will look like with a single path circular text laid on top of it.

Let’s assume that our circle is CX 295, CY 200, R 175. Now, following the Circular path method, we now see the following:

M (CX - R), CY
a R,R 0 1,1 (R * 2),0
a R,R 0 1,1 -(R * 2),0

See the Pen SVG Amethyst by Bryan Rasmussen.

I’m not going to talk about the path or the text size, fill or stroke color. We should all understand that by now, and be able to make it be whatever we want it to be. But by looking at the text, we can see some downsides or limitations right away:

  • The text all runs in one direction;
  • It might be nice to have some of the text go behind the amethyst, especially where it says MAGAZINE. In order to make the ‘M’ and ‘E’ line up on the circle, the ‘A’ has to be on the side lower point of the amethyst, which feels sort of unbalanced in another way. (I feel like the ‘A’ should be precisely positioned and pointing down at that point.)

If we want to fix these issues, we need to split our single path into two. In the following pen, I have separated the path into two paths, (and placed them into the defs area of the SVG for our textPaths to reference):

See the Pen SVG Amethyst two paths by Bryan Rasmussen.

Again, assuming our CX is 295, CY 200, R 175, then the two paths are in the format of the following (for the top semicircular path):

M (CX - R), CY
a R,R 0 1,1 (R * 2),0

And the following for the bottom:

M (CX + R), CY
a R,R 0 1,1 -(R * 2),0

However, we still have circular text that moves all in the same direction. To fix that for everything but Edge, all you have to do is to add the side="right" attribute to the text element that holds the ‘MAGAZINE’ textPath.

Making The Text Go Another Direction

If we want to support as many browsers as we can, we have to alter the path and not rely on the side attribute which is not fully supported. What we can do is to copy our top semicircle path, but change the sweep from 1 to 0:

Before:

M 120, 200
a 175,175 0 1,1 350,0

After:

M 120, 200
a 175,175 0 1,0 350,0

But our text is now drawn on the inner circle defined by the sweep and it won’t look so nice in different browsers. This means that we’re going to have to move the position of our path to align with the ‘S’ of ‘Smashing’, make the ending X of the path greater, and set some offset to the text. As you can see, there is also a little text difference between Firefox and the others which we can improve by increasing the textLength attribute on the text element, as well as removing whitespace from the textPath (since Firefox evidently thinks whitespace is meaningful).

The solution:

See the Pen SVG Amethyst two paths fixed by Bryan Rasmussen.

Change The Z-Index Of Part Of Our Circular Text

Finally, we want to make our text goes both in front and behind the amethyst. Well, that’s easy. Remember that SVG’s z-indexing of element is based by where they are in the markup? So if we have two elements, element 1 will be drawn behind element 2. Next, all we have to do is to move a text element up in our SVG markup so it is drawn before the amethyst.

You can see the result below in which parts of the word ‘MAGAZINE’ are hidden by the lower point of the amethyst.

See the Pen SVG Amethyst two paths z-index by Bryan Rasmussen.

If you take a look at the markup, you can see that the lower semicircle of text has been moved to be before the path that draws the amethyst.

Animating The Parts Of Our Circle

So now we have the ability to make circular text by completely controlling the directionality of the parts of our text by putting the text into two semicircles. This can, of course, also be exploited to make animations of the text. Making cross-browser SVG animations is really the subject of another article (or a lot more articles). These examples will only work in Chrome and Firefox because of using the SMIL-animations syntax instead of CSS keyframes or tools like Greensock. But it gives a good indicator of the effects you can achieve by animating the decomposed circle.

Take the following pen:

See the Pen SVG Amethyst two paths animated by Bryan Rasmussen.

Please press the ‘Rerun’ button on the codepen to see the animation in action. The two parts of our circular text begin animating at the same time, but have a different duration so they end at different times. Because we are animating the textLength attribute, we have put two animate directives under each text — one for the text element (so Firefox will work) and one for the textpath element (so Chrome will work).

Conclusion

In this article, we’ve seen how to turn a circle into a path and back again, in order to better understand when we need to optimize a path and when not. We’ve seen how turning the circle into a path frees us up to placing the text on the circular path, but also how to further split the circular path into semicircles and gain fuller control over directionality and animation of the component parts of our circular text.

Further Reading on SmashingMag:

Smashing Editorial
(dm, ra, yk, il)

How Bipolar Disorder Affects Relationships

  • March 22, 2019

Bipolar disorder is a psychiatric condition which can cause several changes in a person’s mood. People who are suffering from bipolar disorder fall in two categories — high moods (mania) and extremely low moods (hypomania).

During the manic phase, people lose their common sense to judge anything, while they completely withdraw from everything and everyone when in a hypomanic state. These changes in mood can seriously affect relationships since it contributes to how they interact with others.

Bipolar Disorder and Relationships

If you have bipolar disorder, then your mood swings can cause unusual changes in your behaviors. This can make it more challenging to go on dates or even marry.

Scott Haltzman (Clinical Assistant Professor in the Brown University) tells that this disorder can seriously create problems in any relationship. In his book Secrets of Happily Married Men and The Secrets of Happily Married Women, he also tells that when people get into a relationship, they want stability. It’s not something you can easily get when you have bipolar disorder.

Dating When You Have Bipolar Disorder

bipolar dating

If you are suffering from bipolar disorder, you may feel nervous whenever you are starting a new relationship.

But, here’s the thing:

There is no need to introduce your psychiatric problems on the first date. You should find the right time to tell your partner that you have bipolar disorder.

Revealing that you are suffering from bipolar is not an auspicious beginning to your relationship. Dr. Haltzman also says that when you feel that there is a strong mutual attraction between you and your partner, that’s the point where you should clear the actual problem.

Dating someone with psychiatric problems can be challenging because you can’t control your partner’s mood swings. If you want to succeed in your relationship, you should concentrate on your partner’s treatment and focus on communication.

How Bipolar Disorder Affects Married Life

Work stress, money issues, and other factors can put a strain on your married life. Stress caused by one’s day-to-day, however, can turn to problems of epic proportions when your partner has bipolar disorder.

Research shows that this is the main reason why 90% of marriages fail.

Like most people, those with bipolar disorders have many good qualities but they may show undesirable behaviors as well. They may show love and affections and be cold and distant at the same time.

These unpredictable behaviors may be very difficult and challenging for all married people. In such conditions, you should cooperate with your partner and make your married life stronger than ever.

How Does Someone Develop Bipolar Disorder

Doctors do not know the exact causes of bipolar disorder. However, experts consider a few factors that predispose one to develop the condition.

Family genes and abnormal brain structures are two factors often believed to cause bipolar disorder. Scientist and researchers are still trying to find out more about it.

How Bipolar Disorder Can be Treated

Educate yourself: Educate and prepare yourself before you start a relationship with a person who has bipolar disorder. Do your research to know how you can deal with and understand this psychiatric problem.

Teach your partner: Teach your partner how to react during different mood swings and what they can do to overcome them.

You have to be patient: When your partner’s mood swings interfere with your romantic or dating plans, you should remind yourself that it’s the condition and not your partner. Sometimes, taking a quick break helps.

Keep an open communication: You should be open with your partner and communicate openly.

Take proper medication: Help your partner to take medicine as prescribed. Show your support and help make sure no doctor appointments are missed.

Manage stress in different ways: You can ask your partner for a long walk or to eat healthily. Exercise regularly and be more active. A change in lifestyle can go a long way.

bipolar relationships

Regular therapy: Regular therapy is an essential component when it comes to addressing psychiatric problems.

Child school support: Children who are suffering from this mental illness can be treated by the school support system. School psychologists, counselors, and other staff should participate in helping children succeed in school.

The Difference Between Bipolar Disorder and Depression

Bipolar disorder and depression are different from each other. Knowing how they are different can help you plan the right approach.

Bipolar Disorder

Bipolar disorder refers to the opposite ends of the emotional spectrum. A person may be depressed for a long period of time and show low energy, anxiety, and emptiness. When his energy is high, he may experience racing thoughts and feelings of power that can last for several days or months.

Depression

Depression is deeper than sadness. If you have this condition, you may feel helpless, hopeless, and worthless. You may loss interest in things you used to enjoy. You’ll experience changes in appetite, sleep problems, and even suicidal thoughts or actions.

See Also: 15 Symptoms You Are Depressed (Even When You Think You Aren’t)

Author: Kaitlin Adam

The post How Bipolar Disorder Affects Relationships appeared first on Dumb Little Man.

What is Blazor and what is Razor Components?

  • March 22, 2019

I’ve blogged a little about Blazor, showing examples like Compiling C# to WASM with Mono and Blazor then Debugging .NET Source with Remote Debugging in Chrome DevTools as well as very early on asking questions like .NET and WebAssembly – Is this the future of the front-end?

Let’s back up and level-set.

What is Blazor?

Blazor is a single-page app framework for building interactive client-side Web apps with .NET. Blazor uses open web standards without plugins or code transpilation. Blazor works in all modern web browsers, including mobile browsers.

You write C# in case of JavaScript, and you can use most of the .NET ecosystem of open source libraries. For the most part, if it’s .NET Standard, it’ll run in the browser. (Of course if you called a Windows API or a Linux specific API and it didn’t exist in the client-side browser S world, it’s not gonna work, but you get the idea).

The .NET code runs inside the context of WebAssembly. You’re running “a .NET” inside your browser on the client-side with no plugins, no Silverlight, Java, Flash, just open web standards.

WebAssembly is a compact bytecode format optimized for fast download and maximum execution speed.

Here’s a great diagram from the Blazor docs.

Blazor runs inside your browser, no plugins needed

Here’s where it could get a little confusing. Blazor is the client-side hosting model for Razor Components. I can write Razor Components. I can host them on the server or host them on the client with Blazor.

You may have written Razor in the past in .cshtml files, or more recently in .razor files. You can create and share components using Razor – which is a mix of standard C# and standard HTML, and you can host these Razor Components on either the client or the server.

In this diagram from the docs you can see that the Razor Components are running on the Server and SignalR (over Web Sockets, etc) is remoting them and updating the DOM on the client. This doesn’t require Web Assembly on the client, the .NET code runs in the .NET Core CLR (Common Language Runtime) and has full compatibility – you can do anything you’d like as you are not longer limited by the browser’s sandbox.

Here's Razor Components running on the server

Per the docs:

Razor Components decouples component rendering logic from how UI updates are applied. ASP.NET Core Razor Components in .NET Core 3.0 adds support for hosting Razor Components on the server in an ASP.NET Core app. UI updates are handled over a SignalR connection.

Here’s the canonical “click a button update some HTML” example.

@page "/counter"


<h1>Counter</h1>

<p>Current count: @currentCount</p>

<button class="btn btn-primary" onclick="@IncrementCount">Click me</button>

@functions
int currentCount = 0;

void IncrementCount()

currentCount++;

You can see this running entirely in the browser, with the C# .NET code running on the client side. .NET DLLs (assemblies) are downloaded and executed by the CLR that’s been compiled into WASM and running entirely in the context of the browser.

Note also that I’m stopped at a BREAKPOINT in C# code, except the code is running in the browser and mapped back into JS/WASM world.

Debugging Razor Components on the Client Side

But if I host my app on the server as hosted Razor Components, the C# code runs entirely on the Server-side and the client-side DOM is updated over a SignalR link. Here I’ve clicked the button on the client side and hit the breakpoint on the server-side in Visual Studio. No there’s no POST and no POST-back. This isn’t WebForms – It’s Razor Components. It’s a SPA app written in C#, not JavaScript, and I can change the locations of the running logic, while the UI remains always standard HTML and CSS.

Debugging Razor Components on the Server Side

It’s a pretty exciting time on the open web. There’s a lot of great work happening in this space and I’m very interesting to see how frameworks like Razor Components/Blazor and Phoenix LiveView change (or don’t) how we write apps for the web.


Sponsor: Manage GitHub Pull Requests right from the IDE with the latest JetBrains Rider. An integrated performance profiler on Windows comes to the rescue as well.


© 2018 Scott Hanselman. All rights reserved.

An Inferno on the Head of a Pin

  • March 22, 2019

Today’s processors contain billions of heat-generating transistors in an ever shrinking space. The power budget might go from:

  • 1000 watts on a specialized server
  • 100 watts on desktops
  • 30 watts on laptops
  • 5 watts on tablets
  • 1 or 2 watts on a phone
  • 100 milliwatts on an embedded system

That’s three four orders of magnitude. Modern CPU design is the delicate art of placing an inferno on the head of a pin.

Look at the original 1993 Pentium compared to the 20th anniversary Pentium:

Intel Pentium 66 1993
Pentium
66 Mhz
16kb L1
3.2 million transistors
Intel Pentium G3258 20th Anniversary Edition 2014
Pentium G3258
3.2 Ghz × 2 cores
128kb L1, 512kb L2, 3MB L3
1.4 billion transistors

I remember cooling the early CPUs with simple heatsinks; no fan. Those days are long gone.

A roomy desktop computer affords cooling opportunities (and thus a watt budget) that a laptop or tablet could only dream of. How often will you be at peak load? For most computers, the answer is "rarely". The smaller the space, the higher the required performance, the more … challenging your situation gets.

Sometimes, I build servers.

Inspired by Google and their use of cheap, commodity x86 hardware to scale on top of the open source Linux OS, I also built our own servers. When I get stressed out, when I feel the world weighing heavy on my shoulders and I don’t know where to turn … I build servers. It’s therapeutic.

Servers are one of those situations where you may be at full CPU load more often than not. I prefer to build 1U servers which is the smallest rack mountable unit, at 1.75" total height.

You get plenty of cores on a die these days, so I build single CPU servers. One reason is price; the other reason is that clock speed declines proportionally to the number of cores on a die (this is for the Broadwell Xeon V4 series):

coresGHz
E5-163043.7$406
E5-165063.6$617
E5-168083.4$1723
E5-2680122.4$1745
E5-2690142.6$2090
E5-2697182.3$2702

Yes, there are server CPUs with even more cores, but if you have to ask how much they cost, you definitely can’t afford them … and they’re clocked even slower. What we do is serviced better by a smaller number of super fast cores than a larger number of slow cores, anyway.

With that in mind, consider these two Intel Xeon server CPUs:

As you can see from the official Intel product pages for each processor, they both have a TDP heat budget of 140 watts. I’m scanning the specs, thinking maybe this is an OK tradeoff.

Unfortunately, here’s what I actually measured with my trusty Kill-a-Watt for each server build as I performed my standard stability testing, with completely identical parts except for the CPU:

  • E5-1630: 40w idle, 170w mprime
  • E5-1650: 55w idle, 250w mprime

I am here to tell you that Intel’s TDP figure of 140 watts for the 6 core version of this CPU is a terrible, scurrilous lie!

This caused a bit of a problem for me as our standard 1U server build now overheats, alarms, and throttles with the 6 core CPU — whereas the 4 core CPU was just fine. Hey Intel! From my home in California, I stab at thee!

But, you know..

Better Heatsink

The 1.75" maximum height of the 1U server form factor doesn’t leave a lot of room for creative cooling of a CPU. But you can switch from an Aluminum cooler to a Copper one.

Copper is significantly more expensive, plus heavier and harder to work with, so it’s generally easier to throw an ever-larger mass of aluminum at the cooling problem when you can. But when space is a constraint, as it is with a 1U server, copper dissipates more heat in the same form factor.

The famous "Ninja" CPU cooler came in identical copper and aluminum versions so we can compare apples to apples:

  • Aluminum Ninja — 24C rise over ambient
  • Copper Ninja — 17C rise over ambient

You can scale the load and the resulting watts of heat by spinning up MPrime threads for the exact number of cores you want to "activate", so that’s how I tested:

  • Aluminum heatsink — stable at 170w (mprime threads=4), but heat warnings with 190w (mprime threads=5)
  • Copper heatsink — stable at 190w (mprime threads=5) but heat warnings with 230w (mprime threads=6)

Each run has to be overnight to be considered successful. This helped, noticeably. But we need more.

Better Thermal Interface

When it comes to server builds, I stick with the pre-applied grey thermal interface pad that comes on the heatsinks. But out of boredom and a desire to experiment, I …

  • Removed the copper heatsink.
  • Used isopropyl alcohol to clean both CPU and heatsink.
  • Applied fancy "Ceramique" thermal compound I have on hand, using an X shape pattern.

I wasn’t expecting any change at all, but to my surprise with the new TIM applied it took 5x longer to reach throttle temps with mprime threads=6. Before, it would thermally throttle within a minute of launching the test, and after it took ~10 minutes to reach that same throttle temp. The difference was noticeable.

That’s a surprisingly good outcome, and it tells us the default grey goop that comes pre-installed on heatsinks is … not great. Per this 2011 test, the difference between worst and best thermal compounds is 4.3°C.

But as Dan once bravely noted while testing Vegemite as a thermal interface material:

If your PC’s so marginal that a CPU running three or four degrees Celsius warmer will crash it [or, for modern CPUs, cause the processor to auto-throttle itself and substantially reduce system performance], the solution is not to try to edge away from the precipice with better thermal compound. It’s to make a big change to the cooling system, or just lower the darn clock speed.

An improved thermal interface just gets you there faster (or slower); it doesn’t address the underlying problem. So we’re not done here.

Ducted Airflow

Most, but not all, of the SuperMicro cases I’ve used have included a basic fan duct / shroud that lays across the central fans and the system. Given that the case fans are pretty much directly in front of the CPU anyway, I’ve included the shroud in the builds out of a sense of completeness more than any conviction that it was doing anything for the cooling performance.

This particular server case, though, did not include a fan duct. I didn’t think much about it at the time, but considering the heat stress this 6-core CPU and its 250 watt heat generation was putting on our 1U build, I decided I should build a quick duct out of card stock and test it out.

(I know, I know, it’s a super janky duct! But I was prototyping!)

Sure enough, this duct, combined with the previous heatsink and TIM changes, enabled the server to remain stable overnight with a full MPrime run of 12 threads.

I think we’ve certainly demonstrated the surprising (to me, at least) value of fan shrouds. But before we get too excited, let’s consider one last thing.

Define "CPU Load"

Sometimes you get so involved with solving the problem at hand that you forget to consider whether you are, in fact, solving the right problem.

In these tests, we defined 100% CPU load using MPrime. Some people claim MPrime is more of a power virus than a real load test, because it exerts so much heat pressure on the CPUs. I initially dismissed these claims since I’ve used MPrime (and its Windows cousin, Prime95) for almost 20 years to test CPU stability, and it’s never let me down.

But I did more research and I found that MPrime, since 2011, uses AVX2 instructions extensively on newer Intel CPUs:

The newer versions of Prime load in a way that they are only safe to run at near stock settings. The server processors actually downclock when AVX2 is detected to retain their TDP rating. On the desktop we’re free to play and the thing most people don’t know is how much current these routines can generate. It can be lethal for a CPU to see that level of current for prolonged periods.

That’s why most stress test programs alternate between different data pattern types. Depending on how effective the rotation is, and how well that pattern causes issues for the system timing margin, it will, or will not, catch potential for instability. So it’s wise not to hang one’s hat on a single test type.

This explains why I saw such a large discrepancy between other CPU load programs like BurnP6 and MPrime.

MPrime does an amazing job of generating the type of CPU load that causes maximum heat pressure. But unless your servers regularly chew through zillions of especially power-hungry AVX2 instructions this may be completely unrepresentative of any real world load your server would actually see.

Your Own Personal Inferno

Was this overkill? Probably. Even with the aluminum heatsink, no change to thermal interface material, and zero ducting, we’d probably see no throttling under normal use in our server rack. But I wanted to be sure. Completely sure.

Is this extreme? Putting 140 TDP of CPU heat in a 1U server? Not really. Nick at Stack Overflow told me they just put two 22 core, 145W TDP Xeon 2699v4 CPUs and four 300W TDP GPUs in a single Dell C4130 1U server. I’d sure hate to be in the room when those fans spin up. I’m also a little afraid to find out what happens if you run MPrime plus full GPU load on that box.

Servers are an admittedly rare example of big CPU performance heat and size tradeoffs, one of the few left. It is fun to play at the extremes, but the SoC inside your phone makes the same tradeoffs on a smaller scale. Tiny infernos in our pockets, each and every one.

[advertisement] At Stack Overflow, we put developers first. We already help you find answers to your tough coding questions; now let us help you find your next job.

How To Build An Endless Runner Game In Virtual Reality (Part 3)

  • March 22, 2019

How To Build An Endless Runner Game In Virtual Reality (Part 3)

How To Build An Endless Runner Game In Virtual Reality (Part 3)

Alvin Wan

2019-03-20T13:00:35+01:00
2019-03-21T23:35:50+00:00

And so our journey continues. In this final part of my series on how to build an endless runner VR game, I’ll show you how you can synchronize the game state between two devices which will move you one step closer to building a multiplayer game. I’ll specifically introduce MirrorVR which is responsible for handling the mediating server in client-to-client communication.

Note: This game can be played with or without a VR headset. You can view a demo of the final product at ergo-3.glitch.me.

To get started, you will need the following.

  • Internet access (specifically to glitch.com);
  • A Glitch project completed from part 2 of this tutorial. You can start from the part 2 finished product by navigating to https://glitch.com/edit/#!/ergo-2 and clicking “Remix to edit”;
  • A virtual reality headset (optional, recommended). (I use Google Cardboard, which is offered at $15 a piece.)

Step 1: Display Score

The game as-is functions at a bare minimum, where the player is given a challenge: avoid the obstacles. However, outside of object collisions, the game does not provide feedback to the player regarding progress in the game. To remedy this, you will implement the score display in this step. The score will be large text object placed in our virtual reality world, as opposed to an interface glued to the user’s field of view.

In virtual reality generally, the user interface is best integrated into the world rather than stuck to the user’s head.

Score display
Score display (Large preview)

Start by adding the object to index.html. Add a text mixin, which will be reused for other text elements:

<a-assets>
  ...
  <a-mixin id="text" text="
     font:exo2bold;
     anchor:center;
     align:center;"></a-mixin>
  ...
</a-assets>

Next, add a text element to the platform, right before the player:

<!-- Score -->
<a-text id="score" value="" mixin="text" height="40" width="40" position="0 1.2 -3" opacity="0.75"></a-text>

<!-- Player -->
...

This adds a text entity to the virtual reality scene. The text is not currently visible, because its value is set to empty. However, you will now populate the text entity dynamically, using JavaScript. Navigate to assets/ergo.js. After the collisions section, add a score section, and define a number of global variables:

  • score: the current game score.
  • countedTrees: IDs of all trees that are included in the score. (This is because collision tests may trigger multiple times for the same tree.)
  • scoreDisplay: reference to the DOM object, corresponding to a text object in the virtual reality world.
/*********
 * SCORE *
 *********/

var score;
var countedTrees;
var scoreDisplay;

Next, define a setup function to initialize our global variables. In the same vein, define a teardown function.

...
var scoreDisplay;

function setupScore() 
  score = 0;
  countedTrees = new Set();
  scoreDisplay = document.getElementById('score');


function teardownScore() 
  scoreDisplay.setAttribute('value', '');

In the Game section, update gameOver, startGame, and window.onload to include score setup and teardown.

/********
 * GAME *
 ********/

function gameOver() 
    ...
    teardownScore();


function startGame() 
    ...
    setupScore();
    addTreesRandomlyLoop();


window.onload = function() 
    setupScore();
    ...

Define a function that increments the score for a particular tree. This function will check against countedTrees to ensure that the tree is not double counted.

function addScoreForTree(tree_id) 
  if (countedTrees.has(tree_id)) return;
  score += 1;
  countedTrees.add(tree_id);

Additionally, add a utility to update the score display using the global variable.

function updateScoreDisplay() 
  scoreDisplay.setAttribute('value', score);

Update the collision testing accordingly in order to invoke this score-incrementing function whenever an obstacle has passed the player. Still in assets/ergo.js, navigate to the collisions section. Add the following check and update.

AFRAME.registerComponent('player', 
  tick: function() 
    document.querySelectorAll('.tree').forEach(function(tree) 
      ...
      if (position.z > POSITION_Z_LINE_END) 
        addScoreForTree(tree_id);
        updateScoreDisplay();
      
    )
  
)

Finally, update the score display as soon as the game starts. Navigate to the Game section, and add updateScoreDisplay(); to startGame:

function startGame() 
  ...
  setupScore();
  updateScoreDisplay();
  ...

Ensure that assets/ergo.js and index.html match the corresponding source code files. Then, navigate to your preview. You should see the following:

Score display (Large preview)

This concludes the score display. Next, we will add proper start and Game Over menus, so that the player can replay the game as desired.

Step 2: Add Start Menu

Now that the user can keep track of the progress, you will add finishing touches to complete the game experience. In this step, you will add a Start menu and a Game Over menu, letting the user start and restart games.

Let’s begin with the Start menu where the player clicks a “Start” button to begin the game. For the second half of this step, you will add a Game Over menu, with a “Restart” button:

Start and game over menus (Large preview)

Navigate to index.html in your editor. Then, find the Mixins section. Here, append the title mixin, which defines styles for particularly large text. We use the same font as before, align text to the center, and define a size appropriate for the type of text. (Note below that anchor is where a text object is anchored to its position.)

<a-assets>
   ...
   <a-mixin id="title" text="
       font:exo2bold;
       height:40;
       width:40;
       opacity:0.75;
       anchor:center;
       align:center;"></a-mixin>
</a-assets>

Next, add a second mixin for secondary headings. This text is slightly smaller but is otherwise identical to the title.

<a-assets>
   ...
   <a-mixin id="heading" text="
       font:exo2bold;
       height:10;
       width:10;
       opacity:0.75;
       anchor:center;
       align:center;"></a-mixin>
</a-assets>

For the third and final mixin, define properties for descriptive text — even smaller than secondary headings.

<a-assets>
   ...
   <a-mixin id="copy" text="
       font:exo2bold;
       height:5;
       width:5;
       opacity:0.75;
       anchor:center;
       align:center;"></a-mixin>
</a-assets>

With all text styles defined, you will now define the in-world text objects. Add a new Menus section beneath the Score section, with an empty container for the Start menu:

<!-- Score -->
...

<!-- Menus -->
<a-entity id="menu-container">
  <a-entity id="start-menu" position="0 1.1 -3">
  </a-entity>
</a-entity>

Inside the start menu container, define the title and a container for all non-title text:

...
  <a-entity id="start-menu" ...>
    <a-entity id="start-copy" position="0 1 0">
    </a-entity>
    <a-text value="ERGO" mixin="title"></a-text>
  </a-entity>
</a-entity>

Inside the container for non-title text, add instructions for playing the game:

<a-entity id="start-copy"...>
  <a-text value="Turn left and right to move your player, and avoid the trees!" mixin="copy"></a-text>
</a-entity>

To complete the Start menu, add a button that reads “Start”:

<a-entity id="start-copy"...>
  ...
  <a-text value="Start" position="0 0.75 0" mixin="heading"></a-text>
  <a-box id="start-button" position="0 0.65 -0.05" width="1.5" height="0.6" depth="0.1"></a-box>
</a-entity>

Double-check that your Start menu HTML code matches the following:

<!-- Menus -->
<a-entity id="menu-container">
  <a-entity id="start-menu" position="0 1.1 -3">
    <a-entity id="start-copy" position="0 1 0">
      <a-text value="Turn left and right to move your player, and avoid the trees!" mixin="copy"></a-text>
      <a-text value="Start" position="0 0.75 0" mixin="heading"></a-text>
      <a-box id="start-button" position="0 0.65 -0.05" width="1.5" height="0.6" depth="0.1"></a-box>
    </a-entity>
    <a-text value="ERGO" mixin="title"></a-text>
  </a-entity>
</a-entity>

Navigate to your preview, and you will see the following Start menu:


Image of Start menu
Start menu (Large preview)

Still in the Menus section (directly beneath the start menu), add the game-over menu using the same mixins:

<!-- Menus -->
<a-entity id="menu-container">
  ...
  <a-entity id="game-over" position="0 1.1 -3">
    <a-text value="?" mixin="heading" id="game-score" position="0 1.7 0"></a-text>
    <a-text value="Score" mixin="copy" position="0 1.2 0"></a-text>
    <a-entity id="game-over-copy">
      <a-text value="Restart" mixin="heading" position="0 0.7 0"></a-text>
      <a-box id="restart-button" position="0 0.6 -0.05" width="2" height="0.6" depth="0.1"></a-box>
    </a-entity>
    <a-text value="Game Over" mixin="title"></a-text>
  </a-entity>
</a-entity>

Navigate to your JavaScript file, assets/ergo.js. Create a new Menus section before the Game section. Additionally, define three empty functions: setupAllMenus, hideAllMenus, and showGameOverMenu.

/********
 * MENU *
 ********/

function setupAllMenus() 


function hideAllMenus() 


function showGameOverMenu() 


/********
 * GAME *
 ********/

Next, update the Game section in three places. In gameOver, show the Game Over menu:

function gameOver() 
    ...
    showGameOverMenu();

```

In `startGame`, hide all menus:

```
function startGame() 
    ...
    hideAllMenus();

Next, in window.onload, remove the direct invocation to startGame and instead call setupAllMenus. Update your listener to match the following:

window.onload = function() 
  setupAllMenus();
  setupScore();
  setupTrees();

Navigate back to the Menu section. Save references to various DOM objects:

/********
 * MENU *
 ********/

var menuStart;
var menuGameOver;
var menuContainer;
var isGameRunning = false;
var startButton;
var restartButton;

function setupAllMenus() 
  menuStart     = document.getElementById('start-menu');
  menuGameOver  = document.getElementById('game-over');
  menuContainer = document.getElementById('menu-container');
  startButton   = document.getElementById('start-button');
  restartButton = document.getElementById('restart-button');

Next, bind both the “Start” and “Restart” buttons to startGame:

function setupAllMenus() 
  ...
  startButton.addEventListener('click', startGame);
  restartButton.addEventListener('click', startGame);

Define showStartMenu and invoke it from setupAllMenus:

function setupAllMenus() 
  ...
  showStartMenu();


function hideAllMenus() 


function showGameOverMenu() 


function showStartMenu() 

To populate the three empty functions, you will need a few helper functions. Define the following two functions, which accepts a DOM element representing an A-Frame VR entity and shows or hides it. Define both functions above showAllMenus:

...
var restartButton;

function hideEntity(el) 
  el.setAttribute('visible', false);


function showEntity(el) 
  el.setAttribute('visible', true);


function showAllMenus() {
...

First populate hideAllMenus. You will remove the objects from sight, then remove click listeners for both menus:

function hideAllMenus() 
  hideEntity(menuContainer);
  startButton.classList.remove('clickable');
  restartButton.classList.remove('clickable');

Second, populate showGameOverMenu. Here, restore the container for both menus, as well as the Game Over menu and the ‘Restart’ button’s click listener. However, remove the ‘Start’ button’s click listener, and hide the ‘Start’ menu.

function showGameOverMenu() 
  showEntity(menuContainer);
  hideEntity(menuStart);
  showEntity(menuGameOver);
  startButton.classList.remove('clickable');
  restartButton.classList.add('clickable');

Third, populate showStartMenu. Here, reverse all changes that the showGameOverMenu effected.

function showStartMenu() 
  showEntity(menuContainer);
  hideEntity(menuGameOver);
  showEntity(menuStart);
  startButton.classList.add('clickable');
  restartButton.classList.remove('clickable');

Double-check that your code matches the corresponding source files. Then, navigate to your preview, and you will observe the following behavior:

Start and Game Over menus (Large preview)

This concludes the Start and Game Over menus.

Congratulations! You now have a fully functioning game with a proper start and proper end. However, we have one more step left in this tutorial: We need to synchronize the game state between different player devices. This will move us one step closer towards multiplayer games.

Step 3: Synchronizing Game State With MirrorVR

In a previous tutorial, you learned how to send real-time information across sockets, to facilitate one-way communication between a server and a client. In this step, you will build on top of a fully-fledged product of that tutorial, MirrorVR, which handles the mediating server in client-to-client communication.

Note: You can learn more about MirrorVR here.

Navigate to index.html. Here, we will load MirrorVR and add a component to the camera, indicating that it should mirror a mobile device’s view where applicable. Import the socket.io dependency and MirrorVR 0.2.3.

<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.1.1/socket.io.js"></script>
<script src="https://cdn.jsdelivr.net/gh/alvinwan/[email protected]/dist/mirrorvr.min.js"></script>

Next, add a component, camera-listener, to the camera:

<a-camera camera-listener ...>

Navigate to assets/ergo.js. In this step, the mobile device will send commands, and the desktop device will only mirror the mobile device.

To facilitate this, you need a utility to distinguish between desktop and mobile devices. At the end of your file, add a mobileCheck function after shuffle:

/**
 * Checks for mobile and tablet platforms.
 */
function mobileCheck() window.opera);
  return check;
;

First, we will synchronize the game start. In startGame, of the Game section, add a mirrorVR notification at the end.

function startGame() 
  ...
  if (mobileCheck()) 
    mirrorVR.notify('startGame', )
  

The mobile client now sends notifications about a game starting. You will now implement the desktop’s response.

In the window load listener, invoke a setupMirrorVR function:

window.onload = function() 
  ...
  setupMirrorVR();

Define a new section above the Game section for the MirrorVR setup:

/************
 * MirrorVR *
 ************/

function setupMirrorVR() 
  mirrorVR.init();

Next, add keyword arguments to the initialization function for mirrorVR. Specifically, we will define the handler for game start notifications. We will additionally specify a room ID; this ensures that anyone loading your application is immediately synchronized.

function setupMirrorVR() 
  mirrorVR.init(
    roomId: 'ergo',
    state: 
      startGame: 
        onNotify: function(data) 
          hideAllMenus();
          setupScore();
          updateScoreDisplay();
        
      ,
    
  );

Repeat the same synchronization process for Game Over. In gameOver in the Game section, add a check for mobile devices and send a notification accordingly:

function gameOver() 
  ...
  if (mobileCheck()) 
    mirrorVR.notify('gameOver', );
  

Navigate to the MirrorVR section and update the keyword arguments with a gameOver listener:

function setupMirrorVR() 
  mirrorVR.init(
    state: 
      startGame: ...
      ,
      gameOver: 
        onNotify: function(data) 
          gameOver();
        
      ,
    
  ) 

Next, repeat the same synchronization process for the addition of trees. Navigate to addTreesRandomly in the Trees section. Keep track of which lanes receive new trees. Then, directly before the return directive, and send a notification accordingly:

function addTreesRandomly(...) 
  ...
  var numberOfTreesAdded ...
  var position_indices = [];
  trees.forEach(function (tree) 
    if (...) 
      ...
      position_indices.push(tree.position_index);
    
  );

  if (mobileCheck()) 
    mirrorVR.notify('addTrees', position_indices);
  
  return ...

Navigate to the MirrorVR section, and update the keyword arguments to mirrorVR.init with a new listener for trees:

function setupMirrorVR() 
  mirrorVR.init(
    state: 
      ...
      gameOver: ...
      ,
      addTrees: 
        onNotify: function(position_indices) 
          position_indices.forEach(addTreeTo)
        
      ,
    
  ) 

Finally, we synchronize the game score. In updateScoreDisplay from the Score section, send a notification when applicable:

function updateScoreDisplay() 
  ...
  if (mobileCheck()) 
    mirrorVR.notify('score', score);
  

Update the mirrorVR initialization for the last time, with a listener for score changes:

function setupMirrorVR() 
  mirrorVR.init(
    state: 
      addTrees: 
      ,
      score: 
        onNotify: function(data) 
          score = data;
          updateScoreDisplay();
        
      
    
  );

Double-check that your code matches the appropriate source code files for this step. Then, navigate to your desktop preview. Additionally, open up the same URL on your mobile device. As soon as your mobile device loads the webpage, your desktop should immediately start mirroring the mobile device’s game.

Here is a demo. Notice that the desktop cursor is not moving, indicating the mobile device is controlling the desktop preview.

Final result of the endless runner game with MirrorVR game state synchronization (Large preview)

This concludes your augmented project with mirrorVR.

This third step introduced a few basic game state synchronization steps; to make this more robust, you could add more sanity checks and more points of synchronization.

Conclusion

In this tutorial, you added finishing touches to your endless runner game and implemented real-time synchronization of a desktop client with a mobile client, effectively mirroring the mobile device’s screen on your desktop. This concludes the series on building an endless runner game in virtual reality. Along with A-Frame VR techniques, you’ve picked up 3D modeling, client-to-client communication, and other widely applicable concepts.

Next steps can include:

  • More Advanced Modeling
    This means more realistic 3D models, potentially created in a third-party software and imported. For example, (MagicaVoxel) makes creating voxel art simple, and (Blender) is a complete 3D modeling solution.
  • More Complexity
    More complex games, such as a real-time strategy game, could leverage a third-party engine for increased efficiency. This may mean sidestepping A-Frame and webVR entirely, instead publishing a compiled (Unity3d) game.

Other avenues include multiplayer support and richer graphics. With the conclusion of this tutorial series, you now have a framework to explore further.

Smashing Editorial
(rb, ra, il)

How To Make A Speech Synthesis Editor

  • March 21, 2019

How To Make A Speech Synthesis Editor

How To Make A Speech Synthesis Editor

Knut Melvær

2019-03-21T13:00:16+01:00
2019-03-21T17:34:48+00:00

When Steve Jobs unveiled the Macintosh in 1984, it said “Hello” to us from the stage. Even at that point, speech synthesis wasn’t really a new technology: Bell Labs developed the vocoder as early as in the late 30s, and the concept of a voice assistant computer made it into people’s awareness when Stanley Kubrick made the vocoder the voice of HAL9000 in 2001: A Space Odyssey (1968).

It wasn’t before the introduction of Apple’s Siri, Amazon Echo, and Google Assistant in the mid 2015s that voice interfaces actually found their way into a broader public’s homes, wrists, and pockets. We’re still in an adoption phase, but it seems that these voice assistants are here to stay.

In other words, the web isn’t just passive text on a screen anymore. Web editors and UX designers have to get accustomed to making content and services that should be spoken out loud.

We’re already moving fast towards using content management systems that let us work with our content headlessly and through APIs. The final piece is to make editorial interfaces that make it easier to tailor content for voice. So let’s do just that!

What Is SSML

While web browsers use W3C’s specification for HyperText Markup Language (HTML) to visually render documents, most voice assistants use Speech Synthesis Markup Language (SSML) when generating speech.

A minimal example using the root element <speak>, and the paragraph (<p>) and sentence (<s>) tags:

<speak>
  <p>
    <s>This is the first sentence of the paragraph.</s>
    <s>Here’s another sentence.</s>
  </p>
</speak>
Press play to listen to the snippet:

Where SSML gets existing is when we introduce tags for <emphasis> and <prosody> (pitch):

<speak>
  <p>
    <s>Put some <emphasis strength="strong">extra weight on these words</emphasis></s>
    <s>And say <prosody pitch="high" rate="fast">this a bit higher and faster</prosody>!</s>
  </p>
</speak>
Press play to listen to the snippet:

SSML has more features, but this is enough to get a feel for the basics. Now, let’s take a closer look at the editor that we will use to make the speech synthesis editing interface.

The Editor For Portable Text

To make this editor, we’ll use the editor for Portable Text that features in Sanity.io. Portable Text is a JSON specification for rich text editing, that can be serialized into any markup language, such as SSML. This means you can easily use the same text snippet in multiple places using different markup languages.


Sanity.io’s default editor for Portable Text
Sanity.io’s default editor for Portable Text (Large preview)

Installing Sanity

Sanity.io is a platform for structured content that comes with an open-source editing environment built with React.js. It takes two minutes to get it all up and running.

Type npm i -g @sanity/cli && sanity init into your terminal, and follow the instructions. Choose “empty”, when you’re prompted for a project template.

If you don’t want to follow this tutorial and make this editor from scratch, you can also clone this tutorial’s code and follow the instructions in README.md.

When the editor is downloaded, you run sanity start in the project folder to start it up. It will start a development server that use Hot Module Reloading to update changes as you edit its files.

How To Configure Schemas In Sanity Studio

Creating The Editor Files

We’ll start by making a folder called ssml-editor in the /schemas folder. In that folder, we’ll put some empty files:

/ssml-tutorial/schemas/ssml-editor
                        ├── alias.js
                        ├── emphasis.js
                        ├── annotations.js
                        ├── preview.js
                        ├── prosody.js
                        ├── sayAs.js
                        ├── blocksToSSML.js
                        ├── speech.js
                        ├── SSMLeditor.css
                        └── SSMLeditor.js

Now we can add content schemas in these files. Content schemas are what defines the data structure for the rich text, and what Sanity Studio uses to generate the editorial interface. They are simple JavaScript objects that mostly require just a name and a type.

We can also add a title and a description to make a bit nicer for editors. For example, this is a schema for a simple text field for a title:

export default 
  name: 'title',
  type: 'string',
  title: 'Title',
  description: 'Titles should be short and descriptive'


Sanity Studio with a title field and an editor for Portable Text
The studio with our title field and the default editor (Large preview)

Portable Text is built on the idea of rich text as data. This is powerful because it lets you query your rich text, and convert it into pretty much any markup you want.

It is an array of objects called “blocks” which you can think of as the “paragraphs”. In a block, there is an array of children spans. Each block can have a style and a set of mark definitions, which describe data structures distributed on the children spans.

Sanity.io comes with an editor that can read and write to Portable Text, and is activated by placing the block type inside an array field, like this:

// speech.js
export default 
  name: 'speech',
  type: 'array',
  title: 'SSML Editor',
  of: [
     type: 'block' 
  ]

An array can be of multiple types. For an SSML-editor, those could be blocks for audio files, but that falls outside of the scope of this tutorial.

The last thing we want to do is to add a content type where this editor can be used. Most assistants use a simple content model of “intents” and “fulfillments”:

  • Intents
    Usually a list of strings used by the AI model to delineate what the user wants to get done.
  • Fulfillments
    This happens when an “intent” is identified. A fulfillment often is — or at least — comes with some sort of response.

So let’s make a simple content type called fulfillment that use the speech synthesis editor. Make a new file called fulfillment.js and save it in the /schema folder:

// fulfillment.js
export default 
  name: 'fulfillment',
  type: 'document',
  title: 'Fulfillment',
  of: [
    
      name: 'title',
      type: 'string',
      title: 'Title',
      description: 'Titles should be short and descriptive'
    ,
    
      name: 'response',
      type: 'speech'
    
  ]

Save the file, and open schema.js. Add it to your studio like this:

// schema.js
import createSchema from 'part:@sanity/base/schema-creator'
import schemaTypes from 'all:part:@sanity/base/schema-type'
import fullfillment from './fullfillment'
import speech from './speech'

export default createSchema(
  name: 'default',
  types: schemaTypes.concat([
    fullfillment,
    speech,
  ])
)

If you now run sanity start in your command line interface within the project’s root folder, the studio will start up locally, and you’ll be able to add entries for fulfillments. You can keep the studio running while we go on, as it will auto-reload with new changes when you save the files.

Adding SSML To The Editor

By default, the block type will give you a standard editor for visually oriented rich text with heading styles, decorator styles for emphasis and strong, annotations for links, and lists. Now we want to override those with the audial concepts found in SSML.

We begin with defining the different content structures, with helpful descriptions for the editors, that we will add to the block in SSMLeditorSchema.js as configurations for annotations. Those are “emphasis”, “alias”, “prosody”, and “say as”.

Emphasis

We begin with “emphasis”, which controls how much weight is put on the marked text. We define it as a string with a list of predefined values that the user can choose from:

// emphasis.js
export default 
  name: 'emphasis',
  type: 'object',
  title: 'Emphasis',
  description:
    'The strength of the emphasis put on the contained text',
  fields: [
    
      name: 'level',
      type: 'string',
      options: 
        list: [
           value: 'strong', title: 'Strong' ,
           value: 'moderate', title: 'Moderate' ,
           value: 'none', title: 'None' ,
           value: 'reduced', title: 'Reduced' 
        ]
      
    
  ]

Alias

Sometimes the written and the spoken term differ. For instance, you want to use the abbreviation of a phrase in a written text, but have the whole phrase read aloud. For example:

<s>This is a <sub alias="Speech Synthesis Markup Language">SSML</sub> tutorial</s>
Press play to listen to the snippet:

The input field for the alias is a simple string:

// alias.js
export default 
  name: 'alias',
  type: 'object',
  title: 'Alias (sub)',
  description:
    'Replaces the contained text for pronunciation. This allows a document to contain both a spoken and written form.',
  fields: [
    
      name: 'text',
      type: 'string',
      title: 'Replacement text',
    
  ]

Prosody

With the prosody property we can control different aspects how text should be spoken, like pitch, rate, and volume. The markup for this can look like this:

<s>Say this with an <prosody pitch="x-low">extra low pitch</prosody>, and this <prosody rate="fast" volume="loud">loudly with a fast rate</prosody></s>
Press play to listen to the snippet:

This input will have three fields with predefined string options:

// prosody.js
export default 
  name: 'prosody',
  type: 'object',
  title: 'Prosody',
  description: 'Control of the pitch, speaking rate, and volume',
  fields: [
    
      name: 'pitch',
      type: 'string',
      title: 'Pitch',
      description: 'The baseline pitch for the contained text',
      options: 
        list: [
           value: 'x-low', title: 'Extra low' ,
           value: 'low', title: 'Low' ,
           value: 'medium', title: 'Medium' ,
           value: 'high', title: 'High' ,
           value: 'x-high', title: 'Extra high' ,
           value: 'default', title: 'Default' 
        ]
      
    ,
    
      name: 'rate',
      type: 'string',
      title: 'Rate',
      description:
        'A change in the speaking rate for the contained text',
      options: 
        list: [
           value: 'x-slow', title: 'Extra slow' ,
           value: 'slow', title: 'Slow' ,
           value: 'medium', title: 'Medium' ,
           value: 'fast', title: 'Fast' ,
           value: 'x-fast', title: 'Extra fast' ,
           value: 'default', title: 'Default' 
        ]
      
    ,
    
      name: 'volume',
      type: 'string',
      title: 'Volume',
      description: 'The volume for the contained text.',
      options: 
        list: [
           value: 'silent', title: 'Silent' ,
           value: 'x-soft', title: 'Extra soft' ,
           value: 'medium', title: 'Medium' ,
           value: 'loud', title: 'Loud' ,
           value: 'x-loud', title: 'Extra loud' ,
           value: 'default', title: 'Default' 
        ]
      
    
  ]

Say As

The last one we want to include is <say-as>. This tag lets us exercise a bit more control over how certain information is pronounced. We can even use it to bleep out words if you need to redact something in voice interfaces. That’s @!%&© useful!

<s>Do I have to <say-as interpret-as="expletive">frakking</say-as> <say-as interpret-as="verbatim">spell</say-as> it out for you!?</s>
Press play to listen to the snippet:

// sayAs.js
export default 
  name: 'sayAs',
  type: 'object',
  title: 'Say as...',
  description: 'Lets you indicate information about the type of text construct that is contained within the element. It also helps specify the level of detail for rendering
  the contained text.',
  fields: [
    
      name: 'interpretAs',
      type: 'string',
      title: 'Interpret as...',
      options: 
        list: [
           value: 'cardinal', title: 'Cardinal numbers' ,
          
            value: 'ordinal',
            title: 'Ordinal numbers (1st, 2nd, 3th...)'
          ,
           value: 'characters', title: 'Spell out characters' ,
           value: 'fraction', title: 'Say numbers as fractions' ,
           value: 'expletive', title: 'Blip out this word' ,
          
            value: 'unit',
            title: 'Adapt unit to singular or plural'
          ,
          
            value: 'verbatim',
            title: 'Spell out letter by letter (verbatim)'
          ,
           value: 'date', title: 'Say as a date' ,
           value: 'telephone', title: 'Say as a telephone number' 
        ]
      
    ,
    
      name: 'date',
      type: 'object',
      title: 'Date',
      fields: [
        
          name: 'format',
          type: 'string',
          description: 'The format attribute is a sequence of date field character codes. Supported field character codes in format are y, m, d for year, month, and day (of the month) respectively. If the field code appears once for year, month, or day then the number of digits expected are 4, 2, and 2 respectively. If the field code is repeated then the number of expected digits is the number of times the code is repeated. Fields in the date text may be separated by punctuation and/or spaces.'
        ,
        
          name: 'detail',
          type: 'number',
          validation: Rule =>
            Rule.required()
              .min(0)
              .max(2),
          description: 'The detail attribute controls the spoken form of the date. For detail='1' only the day fields and one of month or year fields are required, although both may be supplied'
        
      ]
    
  ]

Now we can import these in an annotations.js file, which makes things a bit tidier.

// annotations.js
export default as alias from './alias'
export default as emphasis from './emphasis'
export default as prosody from './prosody'
export default as sayAs from './sayAs'

Now we can import these annotation types into our main schemas:

// schema.js
import createSchema from "part:@sanity/base/schema-creator"
import schemaTypes from "all:part:@sanity/base/schema-type"
import fulfillment from './fulfillment'
import speech from './ssml-editor/speech'
import 
  alias,
  emphasis,
  prosody,
  sayAs
 from './annotations'

export default createSchema(
  name: "default",
  types: schemaTypes.concat([
    fulfillment,
    speech,
    alias,
    emphasis,
    prosody,
    sayAs
  ])
)

Finally, we can now add these to the editor like this:

// speech.js
export default 
  name: 'speech',
  type: 'array',
  title: 'SSML Editor',
  of: [
    
      type: 'block',
      styles: [],
      lists: [],
      marks: 
        decorators: [],
        annotations: [
          type: 'alias',
          type: 'emphasis',
          type: 'prosody',
          type: 'sayAs'
        ]
      
    
  ]

Notice that we also added empty arrays to styles, and decorators. This disables the default styles and decorators (like bold and emphasis) since they don’t make that much sense in this specific case.

Customizing The Look And Feel

Now we have the functionality in place, but since we haven’t specified any icons, each annotation will use the default icon, which makes the editor hard to actually use for authors. So let’s fix that!

With the editor for Portable Text it’s possible to inject React components both for the icons and for how the marked text should be rendered. Here, we’ll just let some emoji do the work for us, but you could obviously go far with this, making them dynamic and so on. For prosody we’ll even make the icon change depending on the volume selected. Note that I omitted the fields in these snippets for brevity, you shouldn’t remove them in your local files.

// alias.js
import React from 'react'

export default 
  name: 'alias',
  type: 'object',
  title: 'Alias (sub)',
  description: 'Replaces the contained text for pronunciation. This allows a document to contain both a spoken and written form.',
  fields: [
    /* all the fields */
  ],
  blockEditor: 
    icon: () => '🔤',
    render: ( children ) => <span>children 🔤</span>,
  ,
;
// emphasis.js
import React from 'react'

export default 
  name: 'emphasis',
  type: 'object',
  title: 'Emphasis',
  description: 'The strength of the emphasis put on the contained text',
  fields: [
    /* all the fields */
  ],
  blockEditor: 
    icon: () => '🗯',
    render: ( children ) => <span>children 🗯</span>,
  ,
;

// prosody.js
import React from 'react'

export default 
  name: 'prosody',
  type: 'object',
  title: 'Prosody',
  description: 'Control of the pitch, speaking rate, and volume',
  fields: [
    /* all the fields */
  ],
  blockEditor: 
    icon: () => '🔊',
    render: ( children, volume ) => (
      <span>
        children ['x-loud', 'loud'].includes(volume) ? '🔊' : '🔈'
      </span>
    ),
  ,
;
// sayAs.js
import React from 'react'

export default 
  name: 'sayAs',
  type: 'object',
  title: 'Say as...',
  description: 'Lets you indicate information about the type of text construct that is contained within the element. It also helps specify the level of detail for rendering the contained text.',
  fields: [
    /* all the fields */
  ],
  blockEditor: 
    icon: () => '🗣',
    render: props => <span>props.children 🗣</span>,
  ,
;


The customized SSML editor
The editor with our custom SSML marks (Large preview)

Now you have an editor for editing text that can be used by voice assistants. But wouldn’t it be kinda useful if editors also could preview how the text actually will sound like?

Adding A Preview Button Using Google’s Text-to-Speech

Native speech synthesis support is actually on its way for browsers. But in this tutorial, we’ll use Google’s Text-to-Speech API which supports SSML. Building this preview functionality will also be a demonstration of how you serialize Portable Text into SSML in whatever service you want to use this for.

Wrapping The Editor In A React Component

We begin with opening the SSMLeditor.js file and add the following code:

// SSMLeditor.js
import React,  Fragment  from 'react';
import  BlockEditor  from 'part:@sanity/form-builder';

export default function SSMLeditor(props) 
  return (
    <Fragment>
      <BlockEditor ...props />
    </Fragment>
  );

We have now wrapped the editor in our own React component. All the props it needs, including the data it contains, are passed down in real-time. To actually use this component, you have to import it into your speech.js file:

// speech.js
import React from 'react'
import SSMLeditor from './SSMLeditor.js'

export default 
  name: 'speech',
  type: 'array',
  title: 'SSML Editor',
  inputComponent: SSMLeditor,
  of: [
    
      type: 'block',
      styles: [],
      lists: [],
      marks: 
        decorators: [],
        annotations: [
           type: 'alias' ,
           type: 'emphasis' ,
           type: 'prosody' ,
           type: 'sayAs' ,
        ],
      ,
    ,
  ],

When you save this and the studio reloads, it should look pretty much exactly the same, but that’s because we haven’t started tweaking the editor yet.

Convert Portable Text To SSML

The editor will save the content as Portable Text, an array of objects in JSON that makes it easy to convert rich text into whatever format you need it to be. When you convert Portable Text into another syntax or format, we call that “serialization”. Hence, “serializers” are the recipes for how the rich text should be converted. In this section, we will add serializers for speech synthesis.

You have already made the blocksToSSML.js file. Now we’ll need to add our first dependency. Begin by running the terminal command npm init -y inside the ssml-editor folder. This will add a package.json where the editor’s dependencies will be listed.

Once that’s done, you can run npm install @sanity/block-content-to-html to get a library that makes it easier to serialize Portable Text. We’re using the HTML-library because SSML has the same XML syntax with tags and attributes.

This is a bunch of code, so do feel free to copy-paste it. I’ll explain the pattern right below the snippet:

// blocksToSSML.js
import blocksToHTML,  h  from '@sanity/block-content-to-html'

const serializers = 
  marks: 
    prosody: ( children, mark:  rate, pitch, volume  ) =>
      h('prosody',  attrs:  rate, pitch, volume  , children),
    alias: ( children, mark:  text  ) =>
      h('sub',  attrs:  alias: text  , children),
    sayAs: ( children, mark:  interpretAs  ) =>
      h('say-as',  attrs:  'interpret-as': interpretAs  , children),
    break: ( children, mark:  time, strength  ) =>
      h('break',  attrs:  time: '$timems', strength  , children),
    emphasis: ( children, mark:  level  ) =>
      h('emphasis',  attrs:  level  , children)
  


export const blocksToSSML = blocks => blocksToHTML( blocks, serializers )

This code will export a function that takes the array of blocks and loop through them. Whenever a block contains a mark, it will look for a serializer for the type. If you have marked some text to have emphasis, it this function from the serializers object:

emphasis: ( children, mark:  level  ) =>
      h('emphasis',  attrs:  level  , children)

Maybe you recognize the parameter from where we defined the schema? The h() function lets us defined an HTML element, that is, here we “cheat” and makes it return an SSML element called <emphasis>. We also give it the attribute level if that is defined, and place the children elements within it — which in most cases will be the text you have marked up with emphasis.


    "_type": "block",
    "_key": "f2c4cf1ab4e0",
    "style": "normal",
    "markDefs": [
        
            "_type": "emphasis",
            "_key": "99b28ed3fa58",
            "level": "strong"
        
    ],
    "children": [
        
            "_type": "span",
            "_key": "f2c4cf1ab4e01",
            "text": "Say this strongly!",
            "marks": [
                "99b28ed3fa58"
            ]
        
    ]

That is how the above structure in Portable Text gets serialized to this SSML:

<emphasis level="strong">Say this strongly</emphasis>

If you want support for more SSML tags, you can add more annotations in the schema, and add the annotation types to the marks section in the serializers.

Now we have a function that returns SSML markup from our marked up rich text. The last part is to make a button that lets us send this markup to a text-to-speech service.

Adding A Preview Button That Speaks Back To You

Ideally, we should have used the browser’s speech synthesis capabilities in the Web API. That way, we would have gotten away with less code and dependencies.

As of early 2019, however, native browser support for speech synthesis is still in its early stages. It looks like support for SSML is on the way, and there is proof of concepts of client-side JavaScript implementations for it.

Chances are that you are going to use this content with a voice assistant anyways. Both Google Assistant and Amazon Echo (Alexa) support SSML as responses in a fulfillment. In this tutorial, we will use Google’s text-to-speech API, which also sounds good and support several languages.

Start by obtaining an API key by signing up for Google Cloud Platform (it will be free for the first 1 million characters you process). Once you’re signed up, you can make a new API key on this page.

Now you can open your PreviewButton.js file, and add this code to it:

// PreviewButton.js
import React from 'react'
import Button from 'part:@sanity/components/buttons/default'
import  blocksToSSML  from './blocksToSSML'

// You should be careful with sharing this key
// I put it here to keep the code simple
const API_KEY = '<yourAPIkey>'
const GOOGLE_TEXT_TO_SPEECH_URL = 'https://texttospeech.googleapis.com/v1beta1/text:synthesize?key=' + API_KEY

const speak = async blocks => 
  // Serialize blocks to SSML
  const ssml = blocksToSSML(blocks)
  // Prepare the Google Text-to-Speech configuration
  const body = JSON.stringify(
    input:  ssml ,
    // Select the language code and voice name (A-F)
    voice:  languageCode: 'en-US', name: 'en-US-Wavenet-A' ,
    // Use MP3 in order to play in browser
    audioConfig:  audioEncoding: 'MP3' 
  )
  // Send the SSML string to the API
  const res = await fetch(GOOGLE_TEXT_TO_SPEECH_URL, 
    method: 'POST',
    body
  ).then(res => res.json())
  // Play the returned audio with the Browser’s Audo API
  const audio = new Audio('data:audio/wav;base64,' + res.audioContent)
  audio.play()


export default function PreviewButton (props) 
  return <Button style= marginTop: '1em'  onClick=() => speak(props.blocks)>Speak text</Button>

I’ve kept this preview button code to a minimal to make it easier to follow this tutorial. Of course, you could build it out by adding state to show if the preview is processing or make it possible to preview with the different voices that Google’s API supports.

Add the button to SSMLeditor.js:

// SSMLeditor.js
import React,  Fragment  from 'react';
import  BlockEditor  from 'part:@sanity/form-builder';
import PreviewButton from './PreviewButton';

export default function SSMLeditor(props) 
  return (
    <Fragment>
      <BlockEditor ...props />
      <PreviewButton blocks=props.value />
    </Fragment>
  );

Now you should be able to mark up your text with the different annotations, and hear the result when pushing “Speak text”. Cool, isn’t it?

You’ve Created A Speech Synthesis Editor, And Now What?

If you have followed this tutorial, you have been through how you can use the editor for Portable Text in Sanity Studio to make custom annotations and customize the editor. You can use these skills for all sorts of things, not only to make a speech synthesis editor. You have also been through how to serialize Portable Text into the syntax you need. Obviously, this is also handy if you’re building frontends in React or Vue. You can even use these skills to generate Markdown from Portable Text.

We haven’t covered how you actually use this together with a voice assistant. If you want to try, you can use much of the same logic as with the preview button in a serverless function, and set it as the API endpoint for a fulfillment using webhooks, e.g. with Dialogflow.

If you’d like me to write a tutorial on how to use the speech synthesis editor with a voice assistant, feel free to give me a hint on Twitter or share in the comments section below.

Further Reading on SmashingMag:

Smashing Editorial
(dm, ra, yk, il)