{"id":465764,"date":"2025-07-03T09:07:46","date_gmt":"2025-07-03T09:07:46","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=465764"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=465764","title":{"rendered":"<span>Virtual Mirror Library \u2014 \u0411\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430 \u0432\u0438\u0440\u0442\u0443\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u043c\u0430\u043a\u0438\u044f\u0436\u0430 \u0438 \u043e\u043d\u043b\u0430\u0439\u043d \u043f\u0440\u0438\u043c\u0435\u0440\u043a\u0438 \u0430\u043a\u0441\u0435\u0441\u0441\u0443\u0430\u0440\u043e\u0432<\/span>"},"content":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<p>\u041f\u0440\u0438\u0432\u0435\u0442! \u042f \u0410\u043d\u044f, \u0438 \u043e\u0447\u0435\u043d\u044c \u043b\u044e\u0431\u043b\u044e \u043f\u0438\u0441\u0430\u0442\u044c \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u044b\u0435 \u0438\u043d\u0442\u0435\u0440\u0435\u043d\u043e\u0441\u0442\u0438 \u043f\u043e\u0434 E-commerce.<\/p>\n<p>\u0420\u0430\u043d\u0435\u0435 \u044f \u0443\u0436\u0435 \u043f\u0438\u0441\u0430\u043b\u0430 \u043e \u0442\u043e\u043c, \u043a\u0430\u043a \u0441\u043e\u0437\u0434\u0430\u043b\u0430 POC \u043c\u043e\u0434\u0443\u043b\u044f <a href=\"https:\/\/habr.com\/ru\/articles\/825622\/\" rel=\"noopener noreferrer nofollow\">\u0432\u0438\u0437\u0443\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u043f\u043e\u0438\u0441\u043a\u0430<\/a>, \u0441\u0435\u0433\u043e\u0434\u043d\u044f \u0445\u043e\u0447\u0443 \u043f\u043e\u0434\u0435\u043b\u0438\u0442\u044c\u0441\u044f \u0441\u0432\u043e\u0435\u0439 \u043d\u0430\u0440\u0430\u0431\u043e\u0442\u043a\u043e\u0439 \u0432\u0438\u0440\u0442\u0443\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u0437\u0435\u0440\u043a\u0430\u043b\u0430.<\/p>\n<p>\u0411\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0443 \u043d\u0430\u043f\u0438\u0441\u0430\u043b\u0430 \u0435\u0449\u0435 \u0433\u043e\u0434-\u043f\u043e\u043b\u0442\u043e\u0440\u0430 \u043d\u0430\u0437\u0430\u0434, \u043d\u0430 \u0442\u043e \u0432\u0440\u0435\u043c\u044f \u0431\u044b\u043b\u043e \u043c\u0430\u043b\u043e \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438 \u043d\u0430 \u044d\u0442\u0443 \u0442\u0435\u043c\u0443, \u043d\u043e \u0437\u0430\u0442\u043e \u0431\u043e\u043b\u044c\u0448\u043e\u0435 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u043f\u0440\u0435\u0434\u043b\u043e\u0436\u0435\u043d\u0438\u0439 \u043e \u043f\u043e\u043a\u0443\u043f\u043a\u0435 \u0433\u043e\u0442\u043e\u0432\u044b\u0445 \u043c\u043e\u0434\u0443\u043b\u0435\u0439. \u041c\u043d\u0435, \u043a\u0430\u043a \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0443, \u0441\u0442\u0430\u043b\u043e \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u043e, \u0430 \u043a\u0430\u043a \u0436\u0435 \u044d\u0442\u043e \u0432\u0441\u0435 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442, \u0438 \u043d\u0430\u0447\u0430\u043b\u0430 \u043f\u043e\u0433\u0440\u0443\u0436\u0430\u0442\u044c\u0441\u044f \u0434\u0435\u0442\u0430\u043b\u044c\u043d\u0435\u0435 \u0432 \u044d\u0442\u0443 \u0442\u0435\u043c\u0443.<\/p>\n<p>\u0414\u043b\u044f \u043d\u0435\u0442\u0435\u0440\u043f\u0435\u043b\u0438\u0432\u044b\u0445 &#8212; <a href=\"https:\/\/github.com\/annysmolyan\/virtual-mirror-library\" rel=\"noopener noreferrer nofollow\">\u0432\u043e\u0442 \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 Github<\/a><\/p>\n<details class=\"spoiler\">\n<summary>\u0412\u0410\u0416\u041d\u041e! \u041f\u0420\u041e\u0427\u0418\u0422\u0410\u0422\u042c \u041f\u0415\u0420\u0415\u0414 \u0414\u0410\u041b\u042c\u041d\u0415\u0419\u0428\u0418\u041c \u0427\u0422\u0415\u041d\u0418\u0415\u041c \u0421\u0422\u0410\u0422\u042c\u0418!!!!<\/summary>\n<div class=\"spoiler__content\">\n<p>1) \u041c\u043e\u0439 \u043a\u043e\u0434 \u043d\u0435 \u043f\u0440\u0435\u0442\u0435\u043d\u0434\u0443\u0435\u0442 \u043d\u0430 \u0438\u0434\u0435\u0430\u043b\u044c\u043d\u044b\u0439.<\/p>\n<p>2) \u0411\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430 <strong>\u043d\u0435 \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f<\/strong> \u0430\u0431\u0441\u043e\u043b\u044e\u0442\u043d\u043e \u0433\u043e\u0442\u043e\u0432\u044b\u043c \u043f\u0440\u043e\u0434\u0443\u043a\u0442\u043e\u043c, \u043c\u043e\u0436\u0435\u0442 \u0438\u043c\u0435\u0442\u044c \u043e\u0448\u0438\u0431\u043a\u0438 \u0432 \u0440\u0430\u0431\u043e\u0442\u0435, \u043d\u0435 \u0438\u0434\u0435\u0430\u043b\u044c\u043d\u044b\u0439 \u043d\u0430\u043a\u043b\u0430\u0434\u044b\u0432\u0430\u0435\u043c\u044b\u0439 \u044d\u0444\u0444\u0435\u043a\u0442, \u0430 \u0442\u0430\u043a \u0436\u0435 \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043d\u044e\u0430\u0441\u044b \u0432 Safari. <s>\u041a\u043e\u043c\u0443 \u043d\u0443\u0436\u0435\u043d \u0438\u0434\u0435\u0430\u043b\u044c\u043d\u043e \u0440\u0430\u0431\u043e\u0442\u0430\u044e\u0449\u0438\u0439 \u043f\u0440\u043e\u0434\u0443\u043a\u0442 &#8212; \u043c\u043e\u0436\u043d\u043e \u043a\u0443\u043f\u0438\u0442\u044c \u0437\u0430 \u043c\u043d\u043e\u0433\u043e \u0434\u0435\u043d\u0435\u0433 \ud83d\ude42<\/s><\/p>\n<p>3) \u042f \u0431\u0443\u0434\u0443 \u0440\u0430\u0434\u0430 \u043f\u043e\u0447\u0438\u0442\u0430\u0442\u044c \u0432\u0430\u0448\u0438\u00a0<strong>\u043a\u043e\u043d\u0441\u0442\u0440\u0443\u043a\u0442\u0438\u0432\u043d\u044b\u0435<\/strong>\u00a0\u0438\u0434\u0435\u0438\/\u0440\u0435\u043a\u043e\u043c\u0435\u043d\u0434\u0430\u0446\u0438\u0438\/\u043f\u0440\u0435\u0434\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0432 \u043a\u043e\u043c\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u044f\u0445.<\/p>\n<p>4) \u041a\u043e\u0434 \u043c\u043e\u0436\u0435\u0442 \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0442\u044c \u043f\u043e\u043c\u0435\u0442\u043a\u0438 \u0441 TODO &#8212; \u044d\u0442\u043e \u043d\u043e\u0440\u043c\u0430\u043b\u044c\u043d\u043e.<\/p>\n<\/div>\n<\/details>\n<h3>\u0421\u043e\u0434\u0435\u0440\u0436\u0430\u043d\u0438\u0435 \u0441\u0442\u0430\u0442\u044c\u0438<\/h3>\n<ol>\n<li>\n<p><a href=\"#intro\" rel=\"noopener noreferrer nofollow\">\u0412\u0432\u0435\u0434\u0435\u043d\u0438\u0435<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#constants\" rel=\"noopener noreferrer nofollow\">\u0421\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0430 \u043f\u0440\u043e\u0435\u043a\u0442\u0430: Constants<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#lib\" rel=\"noopener noreferrer nofollow\">\u0421\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0430 \u043f\u0440\u043e\u0435\u043a\u0442\u0430: Lib &#8212; The Core of The Library<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#utility\" rel=\"noopener noreferrer nofollow\">\u0421\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0430 \u043f\u0440\u043e\u0435\u043a\u0442\u0430: Utility<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#effect\" rel=\"noopener noreferrer nofollow\">\u0421\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0430 \u043f\u0440\u043e\u0435\u043a\u0442\u0430: Effect<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#glasses-mask\" rel=\"noopener noreferrer nofollow\">\u041a\u0430\u043a \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u043c\u0430\u0441\u043a\u0443 \u0434\u043b\u044f \u043d\u0430\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u043e\u0447\u043a\u043e\u0432<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#include\" rel=\"noopener noreferrer nofollow\">\u041a\u0430\u043a \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0443<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#sum-up\" rel=\"noopener noreferrer nofollow\">\u0417\u0430\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435<\/a><\/p>\n<\/li>\n<\/ol>\n<h2>\u0412\u0432\u0435\u0434\u0435\u043d\u0438\u0435:<\/h2>\n<p><a class=\"anchor\" name=\"intro\" id=\"intro\"><\/a><\/p>\n<p>\u042d\u0442\u0430 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f\u043c \u043f\u0440\u0438\u043c\u0435\u0440\u044f\u0442\u044c \u0440\u0430\u0437\u043b\u0438\u0447\u043d\u0443\u044e \u043a\u043e\u0441\u043c\u0435\u0442\u0438\u043a\u0443 \u0438 \u0430\u043a\u0441\u0435\u0441\u0441\u0443\u0430\u0440\u044b, \u0442\u0430\u043a \u0436\u0435, \u043a\u0430\u043a \u043e\u043d\u0438 \u0434\u0435\u043b\u0430\u043b\u0438 \u0431\u044b \u044d\u0442\u043e \u0441 \u0444\u0438\u0437\u0438\u0447\u0435\u0441\u043a\u0438\u043c \u0437\u0435\u0440\u043a\u0430\u043b\u043e\u043c.<\/p>\n<h4>\u041e\u0441\u043d\u043e\u0432\u043d\u044b\u0435 \u0444\u0443\u043d\u043a\u0446\u0438\u0438<\/h4>\n<ul>\n<li>\n<p><strong>\u041f\u043e\u0442\u043e\u043a \u0441 \u043a\u0430\u043c\u0435\u0440\u044b \u0432 \u0440\u0435\u0430\u043b\u044c\u043d\u043e\u043c \u0432\u0440\u0435\u043c\u0435\u043d\u0438<\/strong>: \u0411\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442 \u0432\u0435\u0431-\u043a\u0430\u043c\u0435\u0440\u0443 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0434\u043b\u044f \u0437\u0430\u0445\u0432\u0430\u0442\u0430 \u0432\u0438\u0434\u0435\u043e\u043f\u043e\u0442\u043e\u043a\u0430 \u043b\u0438\u0446\u0430 \u0432 \u0440\u0435\u0430\u043b\u044c\u043d\u043e\u043c \u0432\u0440\u0435\u043c\u0435\u043d\u0438, \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u044f \u0435\u043c\u0443 \u0432\u0438\u0434\u0435\u0442\u044c \u0441\u0435\u0431\u044f \u0432 \u0440\u0435\u0430\u043b\u044c\u043d\u043e\u043c \u0432\u0440\u0435\u043c\u0435\u043d\u0438, \u043f\u0440\u0438\u043c\u0435\u0440\u044f\u044f \u0440\u0430\u0437\u043b\u0438\u0447\u043d\u0443\u044e \u043a\u043e\u0441\u043c\u0435\u0442\u0438\u043a\u0443 \u0438 \u0430\u043a\u0441\u0435\u0441\u0441\u0443\u0430\u0440\u044b.<\/p>\n<\/li>\n<li>\n<p><strong>\u041f\u0440\u0438\u043c\u0435\u043d\u0435\u043d\u0438\u0435 \u044d\u0444\u0444\u0435\u043a\u0442\u043e\u0432 \u043a \u0441\u0442\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u043e\u043c\u0443 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044e<\/strong>: \u0411\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442 \u043f\u0440\u0438\u043c\u0435\u043d\u0435\u043d\u0438\u0435 \u043c\u0430\u043a\u0438\u044f\u0436\u0430 \u0438 \u0430\u043a\u0441\u0435\u0441\u0441\u0443\u0430\u0440\u043e\u0432 \u043a \u0441\u0442\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u043c \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f\u043c.<\/p>\n<\/li>\n<\/ul>\n<h4>\u0414\u043e\u0441\u0442\u0443\u043f\u043d\u044b\u0435 \u044d\u0444\u0444\u0435\u043a\u0442\u044b<\/h4>\n<p>\u0411\u043b\u0435\u0441\u043a \u0434\u043b\u044f \u0433\u0443\u0431, \u041a\u0430\u0440\u0430\u043d\u0434\u0430\u0448 \u0434\u043b\u044f \u0433\u0443\u0431, \u041f\u043e\u043c\u0430\u0434\u0430, \u041f\u043e\u043c\u0430\u0434\u0430 \u0441 \u0448\u0438\u043c\u043c\u0435\u0440\u043e\u043c, \u041c\u0430\u0442\u043e\u0432\u0430\u044f \u043f\u043e\u043c\u0430\u0434\u0430, \u0426\u0432\u0435\u0442 \u0431\u0440\u043e\u0432\u0435\u0439, \u041f\u043e\u0434\u0432\u043e\u0434\u043a\u0430 \u0434\u043b\u044f \u0433\u043b\u0430\u0437, \u0422\u0443\u0448\u044c, \u041a\u0430\u0440\u0430\u043d\u0434\u0430\u0448 \u0434\u043b\u044f \u0433\u043b\u0430\u0437 (\u041a\u0430\u044f\u043b), \u0422\u0435\u043d\u0438 \u0434\u043b\u044f \u0432\u0435\u043a \u0441\u0430\u0442\u0438\u043d\u043e\u0432\u044b\u0435, \u0422\u0435\u043d\u0438 \u0434\u043b\u044f \u0432\u0435\u043a \u043c\u0430\u0442\u043e\u0432\u044b\u0435, \u0422\u0435\u043d\u0438 \u0434\u043b\u044f \u0432\u0435\u043a \u0441 \u0448\u0438\u043c\u043c\u0435\u0440\u043e\u043c, \u0422\u043e\u043d\u0430\u043b\u044c\u043d\u044b\u0439 \u043a\u0440\u0435\u043c \u0441\u0430\u0442\u0438\u043d\u043e\u0432\u044b\u0439, \u0422\u043e\u043d\u0430\u043b\u044c\u043d\u044b\u0439 \u043a\u0440\u0435\u043c \u043c\u0430\u0442\u043e\u0432\u044b\u0439, \u041a\u043e\u043d\u0441\u0438\u043b\u0435\u0440, \u041a\u043e\u043d\u0442\u0443\u0440\/\u0411\u0440\u043e\u043d\u0437\u0435\u0440, \u041e\u0447\u043a\u0438<\/p>\n<h4>\u0421\u0438\u0441\u0442\u0435\u043c\u043d\u044b\u0435 \u0442\u0440\u0435\u0431\u043e\u0432\u0430\u043d\u0438\u044f<\/h4>\n<ul>\n<li>\n<p><strong>\u0414\u043e\u0441\u0442\u0443\u043f\u043d\u0430\u044f \u043a\u0430\u043c\u0435\u0440\u0430 (\u0434\u043b\u044f \u0440\u0435\u0436\u0438\u043c\u0430 \u043a\u0430\u043c\u0435\u0440\u044b)<\/strong>: \u0423\u0431\u0435\u0434\u0438\u0442\u0435\u0441\u044c, \u0447\u0442\u043e \u043a\u0430\u043c\u0435\u0440\u0430 \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u0430.<\/p>\n<\/li>\n<li>\n<p><strong>SSL-\u0441\u0435\u0440\u0442\u0438\u0444\u0438\u043a\u0430\u0442<\/strong>: WebRTC \u043d\u0435 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u0431\u0435\u0437 \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b\u0430 HTTPS.<\/p>\n<\/li>\n<li>\n<p><strong>\u041f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0430 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u043e\u0432<\/strong><\/p>\n<\/li>\n<\/ul>\n<div>\n<div class=\"table\">\n<table>\n<tbody>\n<tr>\n<td>\n<p align=\"left\">\u0411\u0440\u0430\u0443\u0437\u0435\u0440<\/p>\n<\/td>\n<td>\n<p align=\"left\">\u041c\u0438\u043d\u0438\u043c\u0430\u043b\u044c\u043d\u044b\u0435 \u0442\u0440\u0435\u0431\u043e\u0432\u0430\u043d\u0438\u044f \u043a \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0443<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\">Chrome<\/p>\n<\/td>\n<td>\n<p align=\"left\">52+<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\">Firefox<\/p>\n<\/td>\n<td>\n<p align=\"left\">35+<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\">Internet Explorer<\/p>\n<\/td>\n<td>\n<p align=\"left\">\u041d\/\u0414*<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\">Opera<\/p>\n<\/td>\n<td>\n<p align=\"left\">39+<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\">Safari<\/p>\n<\/td>\n<td>\n<p align=\"left\">11+<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/div>\n<\/div>\n<p><em>* Internet Explorer<\/em> \u043d\u0435 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442\u0441\u044f \u043f\u043e\u043b\u043d\u043e\u0441\u0442\u044c\u044e \u0438\u0437-\u0437\u0430 \u043e\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0438\u044f \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0438 \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u0441\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0445 \u0432\u0435\u0431-\u0444\u0443\u043d\u043a\u0446\u0438\u0439 \u0438 WebRTC. \u0420\u0435\u043a\u043e\u043c\u0435\u043d\u0434\u0443\u0435\u0442\u0441\u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0441\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0439 \u0431\u0440\u0430\u0443\u0437\u0435\u0440 \u0434\u043b\u044f \u043b\u0443\u0447\u0448\u0435\u0439 \u0441\u043e\u0432\u043c\u0435\u0441\u0442\u0438\u043c\u043e\u0441\u0442\u0438 \u0438 \u0431\u0435\u0437\u043e\u043f\u0430\u0441\u043d\u043e\u0441\u0442\u0438.<\/p>\n<h4>\u041a\u0440\u0430\u0442\u043a\u0438\u0439 \u043e\u0431\u0437\u043e\u0440 \u043f\u0440\u043e\u0435\u043a\u0442\u0430<\/h4>\n<ul>\n<li>\n<p><strong>_documentation<\/strong>: \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044f.<\/p>\n<\/li>\n<li>\n<p><strong>Constants<\/strong>: \u041e\u0441\u043d\u043e\u0432\u043d\u044b\u0435 \u043a\u043e\u043d\u0441\u0442\u0430\u043d\u0442\u044b, \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u044b\u0435 \u0434\u043b\u044f \u044d\u0442\u043e\u0439 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438.<\/p>\n<\/li>\n<li>\n<p><strong>Effect<\/strong>: \u0417\u0434\u0435\u0441\u044c \u043f\u0440\u0438\u043c\u0435\u043d\u044f\u044e\u0442\u0441\u044f \u0440\u0430\u0437\u043b\u0438\u0447\u043d\u044b\u0435 \u044d\u0444\u0444\u0435\u043a\u0442\u044b. <\/p>\n<\/li>\n<li>\n<p><strong>Lib<\/strong>: \u0418\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u0434\u043b\u044f \u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u00ab\u044f\u0434\u0440\u0430\u00bb \u044d\u0442\u043e\u0433\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0434\u043b\u044f \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0438\u044f \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432 \u043b\u0438\u0446\u0430. <\/p>\n<\/li>\n<li>\n<p><strong>Utility<\/strong>: \u0420\u0430\u0437\u043b\u0438\u0447\u043d\u044b\u0435 \u0432\u0441\u043f\u043e\u043c\u043e\u0433\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u0444\u0443\u043d\u043a\u0446\u0438\u0438.<\/p>\n<\/li>\n<li>\n<p><strong>face.png<\/strong>: \u0414\u0435\u043c\u043e-\u043b\u0438\u0446\u043e \u0434\u043b\u044f \u00ab\u0440\u0435\u0436\u0438\u043c\u0430 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f\u00bb. \u0418\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435 \u0431\u044b\u043b\u043e \u0432\u0437\u044f\u0442\u043e \u0438\u0437 \u0418\u043d\u0442\u0435\u0440\u043d\u0435\u0442\u0430 \u0438\u0437 \u043e\u0442\u043a\u0440\u044b\u0442\u044b\u0445 \u0438\u0441\u0442\u043e\u0447\u043d\u0438\u043a\u043e\u0432.<\/p>\n<\/li>\n<li>\n<p><strong>glasses.png<\/strong>: \u0414\u0435\u043c\u043e-\u043c\u0430\u0441\u043a\u0430 \u043f\u0430\u0440\u044b \u043e\u0447\u043a\u043e\u0432. \u0421\u043c\u043e\u0442\u0440\u0438\u0442\u0435 \u043d\u0438\u0436\u0435, \u043a\u0430\u043a \u0435\u0451 \u0441\u043e\u0437\u0434\u0430\u0442\u044c.<\/p>\n<\/li>\n<li>\n<p><strong>main.js<\/strong>: \u0421\u043a\u0440\u0438\u043f\u0442, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u044b\u0439 \u0434\u043b\u044f \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u043e\u0439.<\/p>\n<\/li>\n<li>\n<p><strong>main.html<\/strong>: \u0414\u0435\u043c\u043e \u0444\u0430\u0439\u043b \u044d\u0442\u043e\u0439 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438.<\/p>\n<\/li>\n<\/ul>\n<h4>\u041e\u0442\u043b\u0430\u0434\u043a\u0430 \u043d\u0430 \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u043e\u0439 \u043c\u0430\u0448\u0438\u043d\u0435<\/h4>\n<p>WebRTC \u043d\u0435 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u0431\u0435\u0437 SSL-\u0441\u0435\u0440\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u0430. \u0415\u0441\u043b\u0438 \u0432\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u043e\u0442\u043b\u0430\u0434\u0438\u0442\u044c \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0443, \u043f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430, \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u0435 \u0444\u043b\u0430\u0433 \u0432 Google Chrome. \u041e\u0442\u043a\u0440\u043e\u0439\u0442\u0435 \u0441\u0441\u044b\u043b\u043a\u0443 \u043d\u0438\u0436\u0435 \u0438 \u0432\u0432\u0435\u0434\u0438\u0442\u0435 \u0441\u0432\u043e\u0439 \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u044b\u0439 \u0434\u043e\u043c\u0435\u043d \u0432 \u043f\u043e\u043b\u0435 &#171;Insecure origins treated as secure&#187; (\u041d\u0435\u0431\u0435\u0437\u043e\u043f\u0430\u0441\u043d\u044b\u0435 \u0438\u0441\u0442\u043e\u0447\u043d\u0438\u043a\u0438, \u0440\u0430\u0441\u0441\u043c\u0430\u0442\u0440\u0438\u0432\u0430\u0435\u043c\u044b\u0435 \u043a\u0430\u043a \u0431\u0435\u0437\u043e\u043f\u0430\u0441\u043d\u044b\u0435).<\/p>\n<p>\u041f\u0440\u0438\u043c\u0435\u0440 \u0434\u043b\u044f GoogleChrome:<\/p>\n<pre><code>   chrome:\/\/flags\/#unsafely-treat-insecure-origin-as-secure<\/code><\/pre>\n<p>\u0411\u0440\u0430\u0443\u0437\u0435\u0440 Mozilla:<\/p>\n<blockquote>\n<p><code>- \u041e\u0442\u043a\u0440\u043e\u0439\u0442\u0435 \u0432 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0435 -&gt; about:config<\/code><\/p>\n<p><code>- \u0423\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \"true\" \u0434\u043b\u044f media.devices.insecure.enabled \u0438 media.getusermedia.insecure.enabled<\/code><\/p>\n<\/blockquote>\n<h2>\u0421\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0430 \u043f\u0440\u043e\u0435\u043a\u0442\u0430: Constants<\/h2>\n<p><a class=\"anchor\" name=\"constants\" id=\"constants\"><\/a><\/p>\n<p>\u0414\u043b\u044f \u043e\u0431\u043b\u0435\u0433\u0447\u0435\u043d\u0438\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u044f \u0440\u0435\u0448\u0438\u043b\u0430 \u0432\u044b\u043d\u0435\u0441\u0442\u0438 \u0432 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0439 \u0444\u0430\u0439\u043b \u043a\u043e\u043d\u0441\u0442\u0430\u043d\u0442\u044b \u0441 \u044d\u0444\u0444\u0435\u043a\u0442\u0430\u043c\u0438. \u041f\u0440\u0438 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e\u0441\u0442\u0438, \u043c\u043e\u0436\u043d\u043e \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u043a\u043e\u043d\u0441\u0442\u0430\u043d\u0442\u044b \u0438 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0430\u0442\u044c \u043f\u043e \u0442\u0440\u0435\u0431\u043e\u0432\u0430\u043d\u0438\u044e.<\/p>\n<details class=\"spoiler\">\n<summary>\u041f\u0440\u0438\u043c\u0435\u0440 \u0444\u0430\u0439\u043b\u0430 EffectConstants.js<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"javascript\">export const EFFECT_BROWS_COLOR = 'BrowsColor';export const EFFECT_LIPSTICK = 'Lipstick';export const EFFECT_MATTE_LIPSTICK = 'MatteLipstick';export const EFFECT_MASCARA = 'Mascara';export const EFFECT_EYELINER = 'Eyeliner';export const EFFECT_KAJAL = 'Kajal';export const EFFECT_EYEGLASSES = 'Eyeglasses';export const EFFECT_LIPLINER = 'LipLiner';export const EFFECT_LIP_GLOSS = 'LipGloss';export const EFFECT_LIPSTICK_SHIMMER = 'LipstickShimmer';export const EFFECT_FOUNDATION_SATIN = 'FoundationSatin';export const EFFECT_FOUNDATION_MATTE = 'FoundationMatte';export const EFFECT_CONCEALER = 'Concealer';export const EFFECT_CONTOUR = 'Contour';export const EFFECT_EYESHADOW_SATIN = 'EyeShadowSatin';export const EFFECT_EYESHADOW_MATTE = 'EyeShadowMatte';export const EFFECT_EYESHADOW_SHIMMER = 'EyeShadowShimmer';Object.defineProperty(window, 'EFFECT_EYESHADOW_SHIMMER', {     value: 'EyeShadowShimmer',     writable: false,     configurable: false });Object.defineProperty(window, 'EFFECT_EYESHADOW_MATTE', {     value: 'EyeShadowMatte',     writable: false,     configurable: false });Object.defineProperty(window, 'EFFECT_EYESHADOW_SATIN', {     value: 'EyeShadowSatin',     writable: false,     configurable: false });Object.defineProperty(window, 'EFFECT_CONTOUR', {     value: 'Contour',     writable: false,     configurable: false });Object.defineProperty(window, 'EFFECT_CONCEALER', {     value: 'Concealer',     writable: false,     configurable: false });Object.defineProperty(window, 'EFFECT_FOUNDATION_MATTE', {     value: 'FoundationMatte',     writable: false,     configurable: false });Object.defineProperty(window, 'EFFECT_FOUNDATION_SATIN', {     value: 'FoundationSatin',     writable: false,     configurable: false });Object.defineProperty(window, 'EFFECT_BROWS_COLOR', {     value: 'BrowsColor',     writable: false,     configurable: false });Object.defineProperty(window, 'EFFECT_MASCARA', {     value: 'Mascara',     writable: false,     configurable: false });Object.defineProperty(window, 'EFFECT_LIPSTICK', {     value: 'Lipstick',     writable: false,     configurable: false });Object.defineProperty(window, 'EFFECT_MATTE_LIPSTICK', {     value: 'MatteLipstick',     writable: false,     configurable: false });Object.defineProperty(window, 'EFFECT_EYELINER', {     value: 'Eyeliner',     writable: false,     configurable: false });Object.defineProperty(window, 'EFFECT_KAJAL', {     value: 'Kajal',     writable: false,     configurable: false });Object.defineProperty(window, 'EFFECT_EYEGLASSES', {     value: 'Eyeglasses',     writable: false,     configurable: false  });Object.defineProperty(window, 'EFFECT_LIPLINER', {     value: 'LipLiner',     writable: false,     configurable: false });Object.defineProperty(window, 'EFFECT_LIP_GLOSS', {     value: 'LipGloss',     writable: false,     configurable: false });Object.defineProperty(window, 'EFFECT_LIPSTICK_SHIMMER', {     value: 'LipstickShimmer',     writable: false,     configurable: false });<\/code><\/pre>\n<\/div>\n<\/details>\n<h2>\u0421\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0430 \u043f\u0440\u043e\u0435\u043a\u0442\u0430: Lib &#8212; The Core of The Library<\/h2>\n<p><a class=\"anchor\" name=\"lib\" id=\"lib\"><\/a><\/p>\n<p>\u0412 \u043e\u0441\u043d\u043e\u0432\u0435 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438 \u043b\u0435\u0436\u0438\u0442 <a href=\"https:\/\/ai.google.dev\/edge\/mediapipe\/solutions\/guide\" rel=\"noopener noreferrer nofollow\"><strong>MediaPipe<\/strong><\/a> \u2014 \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a \u043e\u0442 Google \u0434\u043b\u044f \u0440\u0430\u0441\u043f\u043e\u0437\u043d\u0430\u0432\u0430\u043d\u0438\u044f \u043b\u0438\u0446\u0430 \u0438 \u043a\u043b\u044e\u0447\u0435\u0432\u044b\u0445 \u0442\u043e\u0447\u0435\u043a. \u0421 \u0435\u0433\u043e \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430 \u0442\u043e\u0447\u043d\u043e \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u0442 \u043f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0433\u043b\u0430\u0437, \u0433\u0443\u0431, \u043d\u043e\u0441\u0430 \u0438 \u0434\u0440\u0443\u0433\u0438\u0445 \u043e\u0431\u043b\u0430\u0441\u0442\u0435\u0439, \u0447\u0442\u043e\u0431\u044b \u043d\u0430\u043b\u043e\u0436\u0438\u0442\u044c \u043c\u0430\u043a\u0438\u044f\u0436 \u0438\u043b\u0438 \u0430\u043a\u0441\u0435\u0441\u0441\u0443\u0430\u0440\u044b.<\/p>\n<p>\u041e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0438\u0435 \u0442\u043e\u0447\u0435\u043a \u043d\u0430 \u043b\u0438\u0446\u0435 \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u0432\u043e\u0442 \u0442\u0430\u043a:<\/p>\n<figure class=\"full-width\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/051\/355\/32b\/05135532bffaa027416c26354f628884.png\" alt=\"\u0432\u0437\u044f\u0442\u043e \u0441 \u043e\u0444\u0438\u0446\u0438\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u0441\u0430\u0439\u0442\u0430 https:\/\/ai.google.dev\/\" title=\"\u0432\u0437\u044f\u0442\u043e \u0441 \u043e\u0444\u0438\u0446\u0438\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u0441\u0430\u0439\u0442\u0430 https:\/\/ai.google.dev\/\" width=\"614\" height=\"814\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/051\/355\/32b\/05135532bffaa027416c26354f628884.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/051\/355\/32b\/05135532bffaa027416c26354f628884.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/p>\n<div><figcaption>\u0432\u0437\u044f\u0442\u043e \u0441 \u043e\u0444\u0438\u0446\u0438\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u0441\u0430\u0439\u0442\u0430 <a href=\"https:\/\/ai.google.dev\/\" rel=\"noopener noreferrer nofollow\">https:\/\/ai.google.dev\/<\/a><\/figcaption><\/div>\n<\/figure>\n<p>\u041a\u0430\u0436\u0434\u0430\u044f \u0442\u043e\u0447\u043a\u0430 \u0438\u043c\u0435\u0435\u0442 \u0441\u0432\u043e\u0438 \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u044b \u0432 \u043f\u043b\u043e\u0441\u043a\u043e\u0441\u0442\u0438. \u0414\u043b\u044f \u0442\u043e\u0433\u043e, \u0447\u0442\u043e\u0431\u044b \u0437\u0430\u043a\u0440\u0430\u0441\u0438\u0442\u044c \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u043d\u0443\u044e \u043e\u0431\u043b\u0430\u0441\u0442\u044c, \u044f \u0432\u044b\u0431\u0438\u0440\u0430\u043b\u0430 \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u044b, \u0437\u0430\u043c\u044b\u043a\u0430\u043b\u0430 \u0438\u0445 \u0432 \u0444\u0438\u0433\u0443\u0440\u0443, \u0430 \u043f\u043e\u0441\u043b\u0435 &#8212; \u043f\u0440\u0438\u043c\u0435\u043d\u044f\u043b\u0430 \u044d\u0444\u0444\u0435\u043a\u0442\u044b \u0441 \u043d\u0430\u043b\u043e\u0436\u0435\u043d\u0438\u0435\u043c \u0446\u0432\u0435\u0442\u0430\/\u0430\u043a\u0441\u0435\u0441\u0441\u0443\u0430\u0440\u043e\u0432. \u0422\u0443\u0442 \u043c\u043e\u0436\u043d\u043e \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c <a href=\"https:\/\/ai.google.dev\/edge\/mediapipe\/solutions\/vision\/face_landmarker\" rel=\"noopener noreferrer nofollow\">\u043f\u043e\u043b\u043d\u0443\u044e \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044e \u043f\u0440\u043e FaceLandmarks<\/a>.<\/p>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043d\u0435\u043c\u043d\u043e\u0433\u043e \u0434\u0435\u0442\u0430\u043b\u044c\u043d\u0435\u0435, \u0432 \u043f\u0440\u043e\u0435\u043a\u0442\u0435 \u044d\u0442\u043e \u0432\u0441\u0435 \u043d\u0430\u0445\u043e\u0434\u0438\u0442\u0441\u044f \u0432:<\/p>\n<pre><code>- Lib   - Mediapipe       - face_mesh       - vision_task<\/code><\/pre>\n<p>\u041d\u0430 \u043c\u043e\u043c\u0435\u043d\u0442 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438, \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442 \u0434\u0432\u0435 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438: \u0441\u0442\u0430\u0431\u0438\u043b\u044c\u043d\u0443\u044e \u0432\u0435\u0440\u0441\u0438\u044e Face Mesh \u0438 \u044d\u043a\u0441\u043f\u0435\u0440\u0438\u043c\u0435\u043d\u0442\u0430\u043b\u044c\u043d\u0443\u044e Vision Task, \u043e\u0434\u043d\u0430\u043a\u043e \u043f\u0440\u0435\u0434\u043f\u043e\u0447\u0442\u0435\u043d\u0438\u0435 \u043e\u0442\u0434\u0430\u043d\u043e \u043f\u0435\u0440\u0432\u043e\u0439 \u0438\u0437-\u0437\u0430 \u0431\u043e\u043b\u044c\u0448\u0435\u0439 \u043d\u0430\u0434\u0451\u0436\u043d\u043e\u0441\u0442\u0438. \u041a\u0430\u0436\u0434\u044b\u0439 \u0434\u0432\u0438\u0436\u043e\u043a \u0441\u043e\u043f\u0440\u043e\u0432\u043e\u0436\u0434\u0430\u0435\u0442\u0441\u044f \u0441\u0432\u043e\u0438\u043c <code>Processor<\/code> \u2014 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u043e\u043c, \u043e\u0442\u0432\u0435\u0447\u0430\u044e\u0449\u0438\u043c \u0437\u0430 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0443 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u0438\u043b\u0438 \u0432\u0438\u0434\u0435\u043e, \u043c\u0430\u0441\u0448\u0442\u0430\u0431\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0445\u043e\u043b\u0441\u0442\u0430, \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u044e HTML-\u044d\u043b\u0435\u043c\u0435\u043d\u0442\u043e\u0432 \u0438 \u0437\u0430\u043f\u0443\u0441\u043a \u0446\u0438\u043a\u043b\u0430 \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u043c\u0430\u043a\u0438\u044f\u0436\u0430 \u0432 \u0440\u0435\u0430\u043b\u044c\u043d\u043e\u043c \u0432\u0440\u0435\u043c\u0435\u043d\u0438. <\/p>\n<p><em>\u0412\u0430\u0436\u043d\u043e! \u041f\u0440\u0438 \u043f\u0435\u0440\u0435\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0438 \u043c\u0435\u0436\u0434\u0443 \u0434\u0432\u0438\u0436\u043a\u0430\u043c\u0438, \u0438\u043c\u0435\u0439\u0442\u0435 \u0432\u0432\u0438\u0434\u0443, \u0447\u0442\u043e <\/em><strong><em>FaceMesh<\/em><\/strong><em> \u043d\u0435 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442 \u0440\u0430\u0431\u043e\u0442\u0443 \u0441 \u0432\u043e\u043b\u043e\u0441\u0430\u043c\u0438 \u0438 \u043d\u0430 <\/em><strong><em>\u0441\u0435\u0433\u043e\u0434\u043d\u044f\u0448\u043d\u0438\u0439 \u0434\u0435\u043d\u044c \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0443\u0441\u0442\u0430\u0440\u0435\u0432\u0448\u0438\u043c.<\/em><\/strong><\/p>\n<details class=\"spoiler\">\n<summary>FaceMeshProcessor<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"javascript\">import FaceMeshEngine from '.\/engine\/FaceMeshEngine.js';  \/**  * Processor of FaceMesh engine  * @type {{processVideo: ((function(*, *, *): Promise&lt;boolean&gt;)|*), processImage: (function(*, *, *): Promise&lt;boolean&gt;)}}  *\/ const FaceMeshProcessor = (function () {          let isAnimating = false;         let intervalId = null;          \/**          * Validate that given object is &lt;img&gt; element.          * (can be get by document.getElementById(\"ID_STRING\"))          *          * @param imageHtmlObject          *\/         function validateImageHtmlObject(imageHtmlObject) {             if (imageHtmlObject.tagName.toLowerCase() !== 'img') {                 throw new Error(\"Can not process image. The given object doesn't represent img tag\");             }         }          \/**          * Validate that given object is &lt;video&gt; element.          * (can be get by document.getElementById(\"ID_STRING\"))          *          * @param videoHtmlObject          *\/         function validateVideoHtmlObject(videoHtmlObject) {             if (videoHtmlObject.tagName.toLowerCase() !== 'video') {                 throw new Error(\"Can not process video. The given object doesn't represent video tag\");             }         }          \/**          * Validate that given object is &lt;canvas&gt; element.          * (can be get by document.getElementById(\"ID_STRING\"))          *          * @param canvasHtmlObject          *\/         function validateCanvasHtmlObject(canvasHtmlObject) {             if (canvasHtmlObject.tagName.toLowerCase() !== 'canvas') {                 throw new Error(\"Can not process video. The given object doesn't represent canvas tag\");             }         }          \/**          * Process video with requested effect          *          * Where:          *  sourceVideoHtmlObject - &lt;video&gt; html object          *  resultCanvasHTMLObject - canvas where to show the resulting output          *  effectObject - object with effect settings (see FaceMesh documentation to get more info)          *          * @param sourceVideoHtmlObject          * @param resultCanvasHTMLObject          * @param effectObject          * @returns {Promise&lt;boolean&gt;}          *\/         async function processVideo (sourceVideoHtmlObject, resultCanvasHTMLObject, effectObject) {              validateCanvasHtmlObject(resultCanvasHTMLObject);              async function drawResults() {                 validateVideoHtmlObject(sourceVideoHtmlObject);                  let resultCanvasContext = resultCanvasHTMLObject.getContext('2d');                 let width = sourceVideoHtmlObject.clientWidth;                 let height = sourceVideoHtmlObject.clientHeight;                  \/\/ make result canvas the same size as source video                 resultCanvasHTMLObject.width = width;                 resultCanvasHTMLObject.height = height;                  resultCanvasContext.drawImage(sourceVideoHtmlObject, 0, 0, width, height);                  await FaceMeshEngine.process(                     sourceVideoHtmlObject,                     resultCanvasHTMLObject,                     effectObject                 );             }              intervalId = setInterval(async () =&gt; {                 if (!isAnimating) {                     isAnimating = true;                     await drawResults();                     isAnimating = false;                 }             }, 10);         }          \/**          * Process image with a requested effect          *          * Where:          *  SourceImageHtmlObject - &lt;img&gt; html object          *  resultCanvasHTMLObject - canvas where to show the resulting output          *  effectObject - object with effect settings (see FaceMesh documentation to get more info)          *          * @param sourceImageHtmlObject          * @param resultCanvasHTMLObject          * @param effectObject          * @returns {Promise&lt;boolean&gt;}          *\/         async function processImage (sourceImageHtmlObject, resultCanvasHTMLObject, effectObject) {             validateImageHtmlObject(sourceImageHtmlObject);             validateCanvasHtmlObject(resultCanvasHTMLObject);              \/\/ make result canvas the same size as source image             resultCanvasHTMLObject.width = sourceImageHtmlObject.width;             resultCanvasHTMLObject.height = sourceImageHtmlObject.height;              await FaceMeshEngine.process(                 sourceImageHtmlObject,                 resultCanvasHTMLObject,                 effectObject             );         }          return { \/\/ Public Area              \/**              * Where processedElementHtmlObject can be either &lt;img&gt; or &lt;video&gt; html object              * @param processedElementHtmlObject              * @param resultCanvasHTMLObject              * @param effectObject              * @returns {Promise&lt;void&gt;}              *\/             process: async function(processedElementHtmlObject, resultCanvasHTMLObject, effectObject) {                 await this.terminate();                  if (processedElementHtmlObject.tagName.toLowerCase() === 'video') {                     await processVideo(processedElementHtmlObject, resultCanvasHTMLObject, effectObject);                 } else if (processedElementHtmlObject.tagName.toLowerCase() === 'img') {                     await processImage(processedElementHtmlObject, resultCanvasHTMLObject, effectObject);                 } else {                     this.terminate();                     throw new Error(\"Invalid source type. Can process img or video only\");                 }             },              \/**              * Stop detection              *\/             terminate: function () {                 clearInterval(intervalId);                 isAnimating = false;             }         };     } )();  export default FaceMeshProcessor; <\/code><\/pre>\n<\/div>\n<\/details>\n<details class=\"spoiler\">\n<summary>VisionTaskProcessor<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"javascript\">import VisionTaskEngine from \".\/engine\/VisionTaskEngine.js\"; import * as Constants from \"..\/..\/..\/Constants\/EffectConstants.js\";  \/**  * @type {{launch: VisionTaskProcessor.launch, terminate: VisionTaskProcessor.terminate}}  *\/ const VisionTaskProcessor = (function () {          let isAnimating = false;         let intervalId = null;          \/**          * Validate that given object is &lt;img&gt; element.          * (can be get by document.getElementById(\"ID_STRING\"))          *          * @param imageHtmlObject          *\/         function validateImageHtmlObject(imageHtmlObject) {             if (imageHtmlObject.tagName.toLowerCase() !== 'img') {                 throw new Error(\"Can not process image. The given object doesn't represent img tag\");             }         }          \/**          * Validate that given object is &lt;video&gt; element.          * (can be get by document.getElementById(\"ID_STRING\"))          *          * @param videoHtmlObject          *\/         function validateVideoHtmlObject(videoHtmlObject) {             if (videoHtmlObject.tagName.toLowerCase() !== 'video') {                 throw new Error(\"Can not process video. The given object doesn't represent video tag\");             }         }          \/**          * Validate that given object is &lt;canvas&gt; element.          * (can be get by document.getElementById(\"ID_STRING\"))          *          * @param canvasHtmlObject          *\/         function validateCanvasHtmlObject(canvasHtmlObject) {             if (canvasHtmlObject.tagName.toLowerCase() !== 'canvas') {                 throw new Error(\"Can not process video. The given object doesn't represent canvas tag\");             }         }          \/**          * Process video with requested effect          *          * Where:          *  sourceVideoElementHTMLObject - &lt;video&gt; html object          *  resultCanvasHTMLObject - canvas where to show the resulting output          *  effectObject - object with effect settings (see FaceMesh documentation to get more info)          *          * @param sourceVideoElementHTMLObject          * @param resultCanvasHTMLObject          * @param effectObject          * @returns {Promise&lt;void&gt;}          *\/         async function processVideo (sourceVideoElementHTMLObject, resultCanvasHTMLObject, effectObject) {              validateVideoHtmlObject(sourceVideoElementHTMLObject);             validateCanvasHtmlObject(resultCanvasHTMLObject);              async function drawResults() {                  let landmarksDetected = false;                  let width = sourceVideoElementHTMLObject.clientWidth;                 let height = sourceVideoElementHTMLObject.clientHeight;                  \/\/ make result canvas the same size as source image                 resultCanvasHTMLObject.width = width;                 resultCanvasHTMLObject.height = height;                  if (effectObject.effect === Constants.EFFECT_HAIR_COLOR) {                     landmarksDetected = await VisionTaskEngine.processHairVideo(                         sourceVideoElementHTMLObject,                         resultCanvasHTMLObject,                         effectObject                     );                 } else {                     await VisionTaskEngine.processFaceVideo(                         sourceVideoElementHTMLObject,                         resultCanvasHTMLObject,                         effectObject                     );                 }             }              intervalId = setInterval(async () =&gt; {                 if (!isAnimating) {                     isAnimating = true;                     await drawResults();                     isAnimating = false;                 }             }, 10);         }          \/**          * Process image with a requested effect          *          * Where:          *  sourceImageHtmlObject - &lt;img&gt; html object          *  resultCanvasHTMLObject - canvas where to show the resulting output          *  effectObject - object with effect settings (see FaceMesh documentation to get more info)          *          * @param sourceImageHtmlObject          * @param resultCanvasHTMLObject          * @param effectObject          * @returns {Promise&lt;boolean&gt;}          *\/         async function processImage (sourceImageHtmlObject, resultCanvasHTMLObject, effectObject) {             validateImageHtmlObject(sourceImageHtmlObject);             validateCanvasHtmlObject(resultCanvasHTMLObject);              \/\/ make result canvas the same size as source image             resultCanvasHTMLObject.width = sourceImageHtmlObject.width;             resultCanvasHTMLObject.height = sourceImageHtmlObject.height;              if (effectObject.effect === Constants.EFFECT_HAIR_COLOR) {                 return await VisionTaskEngine.processHairImage(                     sourceImageHtmlObject,                     resultCanvasHTMLObject,                     effectObject                 );             } else {                 return await VisionTaskEngine.processFaceImage(                     sourceImageHtmlObject,                     resultCanvasHTMLObject,                     effectObject                 );             }         }          return { \/\/ Public Area              \/**              * Where processedElementHtmlObject can be either &lt;img&gt; or &lt;video&gt; html object              * @param processedElementHtmlObject              * @param resultCanvasHTMLObject              * @param effectObject              * @returns {Promise&lt;void&gt;}              *\/             process: async function(processedElementHtmlObject, resultCanvasHTMLObject, effectObject) {                 this.terminate();                  if (processedElementHtmlObject.tagName.toLowerCase() === 'video') {                     await processVideo(processedElementHtmlObject, resultCanvasHTMLObject, effectObject);                 } else if (processedElementHtmlObject.tagName.toLowerCase() === 'img') {                     await processImage(processedElementHtmlObject, resultCanvasHTMLObject, effectObject);                 } else {                     this.terminate();                     throw new Error(\"Invalid source type. Can process img or video only\");                 }             },              \/**              * Stop detection              *\/             terminate: function () {                 clearInterval(intervalId);                 isAnimating = false;             }         };     } )();  export default VisionTaskProcessor; <\/code><\/pre>\n<\/div>\n<\/details>\n<p>\u0422.\u043a \u044f \u0431\u043e\u043b\u044c\u0448\u0435\u0435 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435 \u0443\u0434\u0435\u043b\u0438\u043b\u0430 FaceMesh, \u0438 \u043d\u0430 \u0442\u043e\u0442 \u043c\u043e\u043c\u0435\u043d\u0442 \u043e\u043d \u0431\u044b\u043b \u0431\u043e\u043b\u0435\u0435 \u0441\u0442\u0430\u0431\u0438\u043b\u044c\u043d\u044b\u0439 \u0432 \u0440\u0430\u0431\u043e\u0442\u0435, \u0442\u043e \u0437\u0434\u0435\u0441\u044c \u0438 \u0434\u0430\u043b\u0435\u0435 \u044f \u0431\u0443\u0434\u0443 \u043e\u043f\u0438\u0441\u044b\u0432\u0430\u0442\u044c \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443 \u0434\u043b\u044f FaceMesh. Vision Task \u0438\u043c\u0435\u0435\u0442 \u0430\u043d\u0430\u043b\u043e\u0433\u0438\u0447\u043d\u0443\u044e \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443. <\/p>\n<p>\u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u0435\u0449\u0435 \u0440\u0430\u0437 \u043d\u0430 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443 \u0434\u0432\u0438\u0436\u043a\u0430: <\/p>\n<pre><code>- Lib   - Mediapipe       - face_mesh         - core         - detector         - engine         Processor file.js         README.md<\/code><\/pre>\n<p>\u0421 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u043e\u0440\u043e\u043c \u043c\u044b \u0440\u0430\u0437\u043e\u0431\u0440\u0430\u043b\u0438\u0441\u044c \u0432\u044b\u0448\u0435. \u0424\u0430\u0439\u043b ReadMe \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442 \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u0442\u0435\u0445\u043d\u0438\u0447\u0435\u0441\u043a\u0438\u0435 \u0445\u0430\u0440\u0430\u043a\u0442\u0435\u0440\u0438\u0441\u0442\u0438\u043a\u0438, \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u043e \u0434\u0435\u0431\u0430\u0433\u0435, \u043f\u043e\u043b\u0435\u0437\u043d\u044b\u0435 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u0438 \u0442\u0434.<\/p>\n<ul>\n<li>\n<p><strong>Core<\/strong> &#8212; \u043e\u0442\u0432\u0435\u0447\u0430\u0435\u0442 \u0437\u0430 \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044e \u0438 \u0431\u0430\u0437\u043e\u0432\u0443\u044e \u0438\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443 \u0440\u0430\u0431\u043e\u0442\u044b \u0434\u0432\u0438\u0436\u043a\u0430. \u041e\u043d \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442 WebAssembly-\u043c\u043e\u0434\u0443\u043b\u0438, \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b\u0435 \u0434\u043b\u044f \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u0438 \u0438 \u0442\u043e\u0447\u043d\u043e\u0441\u0442\u0438 \u0440\u0430\u0441\u043f\u043e\u0437\u043d\u0430\u0432\u0430\u043d\u0438\u044f, \u0430 \u0442\u0430\u043a\u0436\u0435 \u0433\u043b\u0430\u0432\u043d\u044b\u0439 \u0444\u0430\u0439\u043b, \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u044e\u0449\u0438\u0439 \u0438 \u043d\u0430\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u044e\u0449\u0438\u0439 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0443. \u041a\u0440\u043e\u043c\u0435 \u0442\u043e\u0433\u043e, \u0441\u044e\u0434\u0430 \u0432\u0445\u043e\u0434\u044f\u0442 \u0443\u0442\u0438\u043b\u0438\u0442\u044b, \u0443\u043f\u0440\u043e\u0449\u0430\u044e\u0449\u0438\u0435 \u0440\u0430\u0431\u043e\u0442\u0443 \u0441 \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u0430\u043c\u0438 \u043b\u0438\u0446\u0430, \u043e\u0442\u0440\u0438\u0441\u043e\u0432\u043a\u043e\u0439 \u044d\u0444\u0444\u0435\u043a\u0442\u043e\u0432 \u0438 \u0434\u043e\u0441\u0442\u0443\u043f\u043e\u043c \u043a \u043a\u0430\u043c\u0435\u0440\u0435. \u042d\u0442\u043e\u0442 \u043c\u043e\u0434\u0443\u043b\u044c \u043e\u0431\u0435\u0441\u043f\u0435\u0447\u0438\u0432\u0430\u0435\u0442 \u043d\u0438\u0437\u043a\u043e\u0443\u0440\u043e\u0432\u043d\u0435\u0432\u0443\u044e \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0443.<\/p>\n<\/li>\n<li>\n<p><strong>Detector<\/strong> &#8212; \u043e\u0442\u0432\u0435\u0447\u0430\u0435\u0442 \u0437\u0430 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0438\u0435 \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442 \u043a\u043b\u044e\u0447\u0435\u0432\u044b\u0445 \u0437\u043e\u043d \u043b\u0438\u0446\u0430, \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b\u0445 \u0434\u043b\u044f \u0442\u043e\u0447\u043d\u043e\u0433\u043e \u043d\u0430\u043d\u0435\u0441\u0435\u043d\u0438\u044f \u0432\u0438\u0440\u0442\u0443\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u043c\u0430\u043a\u0438\u044f\u0436\u0430. \u041a\u0430\u0436\u0434\u044b\u0439 \u0434\u0435\u0442\u0435\u043a\u0442\u043e\u0440 \u0432 \u044d\u0442\u043e\u043c \u043c\u043e\u0434\u0443\u043b\u0435 \u2014 \u044d\u0442\u043e \u043b\u043e\u0433\u0438\u0447\u0435\u0441\u043a\u0438 \u0432\u044b\u0434\u0435\u043b\u0435\u043d\u043d\u044b\u0439 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442, \u0441\u043f\u0435\u0446\u0438\u0430\u043b\u0438\u0437\u0438\u0440\u0443\u044e\u0449\u0438\u0439\u0441\u044f \u043d\u0430 \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u043e\u0439 \u043e\u0431\u043b\u0430\u0441\u0442\u0438 \u043b\u0438\u0446\u0430, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0431\u0440\u043e\u0432\u044f\u0445, \u0433\u0443\u0431\u0430\u0445 \u0438\u043b\u0438 \u0433\u043b\u0430\u0437\u0430\u0445. \u041e\u043d \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u0442 \u043d\u0430 \u0432\u0445\u043e\u0434 landmarks, \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u043c\u044b\u0435 FaceMesh, \u0438 \u043d\u0430 \u043e\u0441\u043d\u043e\u0432\u0435 \u044d\u0442\u0438\u0445 \u0434\u0430\u043d\u043d\u044b\u0445 \u0440\u0430\u0441\u0441\u0447\u0438\u0442\u044b\u0432\u0430\u0435\u0442 \u043a\u043e\u043d\u0442\u0443\u0440 \u043d\u0443\u0436\u043d\u043e\u0439 \u043e\u0431\u043b\u0430\u0441\u0442\u0438 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0443\u0442\u0438\u043b\u0438\u0442 \u0438\u0437 <code>core<\/code>. \u042d\u0442\u043e\u0442 \u0441\u043b\u043e\u0439 \u0438\u0437\u043e\u043b\u0438\u0440\u0443\u0435\u0442 \u043b\u043e\u0433\u0438\u043a\u0443 \u0438\u0437\u0432\u043b\u0435\u0447\u0435\u043d\u0438\u044f \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442, \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u044f \u0447\u0451\u0442\u043a\u043e \u0440\u0430\u0437\u0434\u0435\u043b\u0438\u0442\u044c \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0443 \u0433\u0435\u043e\u043c\u0435\u0442\u0440\u0438\u0438 \u043b\u0438\u0446\u0430 \u0438 \u0432\u0438\u0437\u0443\u0430\u043b\u044c\u043d\u043e\u0435 \u043f\u0440\u0438\u043c\u0435\u043d\u0435\u043d\u0438\u0435 \u044d\u0444\u0444\u0435\u043a\u0442\u043e\u0432.<\/p>\n<\/li>\n<\/ul>\n<details class=\"spoiler\">\n<summary>\u041f\u0440\u0438\u043c\u0435\u0440 BrowsDetector<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"javascript\">import CoordinatesUtility from \"..\/core\/coordinates_utils.js\";  \/**  * Define brows and return object of detected coordinates  * @type {{apply: BrowsColorEffect.apply}}  *\/ const BrowsDetector = (function () {      \/**      * Return eye brow contour point coordinates      *      * @param landmarks      * @param eyebrowPoints      * @param canvas      * @returns {*}      *\/     function getEyebrowContourCoordinates(landmarks, eyebrowPoints, canvas) {         let bottomContour = eyebrowPoints.slice(1, 4);         let topContour = eyebrowPoints.slice(4).reverse();         let contour = bottomContour.concat(             [[bottomContour.slice(-1)[0][1], topContour[0][1]]],             topContour,             [[topContour.slice(-1)[0][0], bottomContour[0][1]]],         );         return contour.map(point =&gt; {             return CoordinatesUtility.getPointCoordinates(                 landmarks, point[0], canvas.width, canvas.height             );         });     }      return { \/\/ Public Area         \/**          * Detect contours coordinates of brows and return them          * @param resultCanvasElement          * @param landmarks          * @param leftEyebrowPoints          * @param rightEyebrowPoints          * @returns {{leftBrowContourCoordinates: *, rightBrowContourCoordinates: *}}          *\/         detect: function (resultCanvasElement, landmarks, leftEyebrowPoints, rightEyebrowPoints) {              let rightBrowContourCoordinates = getEyebrowContourCoordinates(                 landmarks, rightEyebrowPoints, resultCanvasElement             );              let leftBrowContourCoordinates = getEyebrowContourCoordinates(                 landmarks, leftEyebrowPoints, resultCanvasElement             );              return {                 \"rightBrowContourCoordinates\": rightBrowContourCoordinates,                 \"leftBrowContourCoordinates\": leftBrowContourCoordinates             }         }     }; })();  export default BrowsDetector; <\/code><\/pre>\n<\/div>\n<\/details>\n<ul>\n<li>\n<p><strong>Engine <\/strong>&#8212;  \u043e\u0442\u0432\u0435\u0447\u0430\u0435\u0442 \u0437\u0430 \u0440\u0430\u0441\u043f\u043e\u0437\u043d\u0430\u0432\u0430\u043d\u0438\u0435 \u043b\u0438\u0446\u0430 \u043d\u0430 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f\u0445 \u0438\u043b\u0438 \u0432\u0438\u0434\u0435\u043e \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438 MediaPipe FaceMesh \u0438 \u043d\u0430\u043d\u0435\u0441\u0435\u043d\u0438\u0435 \u0432\u044b\u0431\u0440\u0430\u043d\u043d\u044b\u0445 \u0432\u0438\u0437\u0443\u0430\u043b\u044c\u043d\u044b\u0445 \u044d\u0444\u0444\u0435\u043a\u0442\u043e\u0432, \u0442\u0430\u043a\u0438\u0445 \u043a\u0430\u043a \u043c\u0430\u043a\u0438\u044f\u0436 \u0438\u043b\u0438 \u0430\u043a\u0441\u0435\u0441\u0441\u0443\u0430\u0440\u044b. <\/p>\n<\/li>\n<\/ul>\n<p><strong>\u0412\u0410\u0416\u041d\u041e! \u041f\u0440\u0438 \u0440\u0430\u0437\u0432\u043e\u0440\u0430\u0447\u0438\u0432\u0430\u043d\u0438\u0438, \u0432 \u044d\u0442\u043e\u043c \u0444\u0430\u0439\u043b\u0435 \u043e\u0431\u0440\u0430\u0442\u0438\u0442\u0435 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435 \u043d\u0430 TODO &#8212; \u0443\u0431\u0435\u0434\u0438\u0442\u0435\u0441\u044c, \u0447\u0442\u043e \u0432\u044b \u0432\u0435\u0440\u043d\u043e \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u043b\u0438 \u043f\u0443\u0442\u0438, \u0438\u043d\u0430\u0447\u0435 \u0444\u0430\u0439\u043b\u044b \u043d\u0435 \u043f\u043e\u0434\u0433\u0440\u0443\u0437\u044f\u0442\u0441\u044f!<\/strong><\/p>\n<details class=\"spoiler\">\n<summary>\u041f\u0440\u0438\u043c\u0435\u0440 FaceMeshEngine.js<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"javascript\">\/\/ Detectors import BrowsDetector from '..\/detector\/BrowsDetector.js'; import EyesDetector from '..\/detector\/EyesDetector.js'; import LipsDetector from '..\/detector\/LipsDetector.js'; import JawDetector from '..\/detector\/JawDetector.js'; import FaceDetector from '..\/detector\/FaceDetector.js';  \/\/ Makeup effects import LipstickEffect from '..\/..\/..\/..\/Effect\/Makeup\/Lipstick.js'; import LipstickMatteEffect from '..\/..\/..\/..\/Effect\/Makeup\/LipstickMatte.js'; import BrowsColorEffect from '..\/..\/..\/..\/Effect\/Makeup\/BrowsColor.js'; import EyelinerEffect from '..\/..\/..\/..\/Effect\/Makeup\/Eyeliner.js'; import LipLinerEffect from '..\/..\/..\/..\/Effect\/Makeup\/LipLiner.js'; import LipGlossEffect from '..\/..\/..\/..\/Effect\/Makeup\/LipGloss.js'; import LipstickShimmerEffect from \"..\/..\/..\/..\/Effect\/Makeup\/LipstickShimmer.js\"; import KajalEffect from \"..\/..\/..\/..\/Effect\/Makeup\/Kajal.js\"; import MascaraEffect from \"..\/..\/..\/..\/Effect\/Makeup\/Mascara.js\"; import FoundationSatinEffect from \"..\/..\/..\/..\/Effect\/Makeup\/FoundationSatin.js\"; import FoundationMatteEffect from \"..\/..\/..\/..\/Effect\/Makeup\/FoundationMatte.js\"; import ConcealerEffect from \"..\/..\/..\/..\/Effect\/Makeup\/Concealer.js\"; import ContourEffect from \"..\/..\/..\/..\/Effect\/Makeup\/Contour.js\"; import EyeShadowSatin from \"..\/..\/..\/..\/Effect\/Makeup\/EyeShadowSatin.js\"; import EyeShadowMatte from \"..\/..\/..\/..\/Effect\/Makeup\/EyeShadowMatte.js\"; import EyeShadowShimmer from \"..\/..\/..\/..\/Effect\/Makeup\/EyeShadowShimmer.js\";  \/\/ Accessories effects import EyeGlassesEffect from '..\/..\/..\/..\/Effect\/Accessories\/EyeGlassesEffect.js';  \/\/ Constants Area import * as Constants from '..\/..\/..\/..\/Constants\/EffectConstants.js';  \/**  * FaceMeshEngine  * @type {{processVideo: ((function(*, *, *): Promise&lt;void&gt;)|*)}}  *\/ const FaceMeshEngine = (function () {          var faceMesh = null;         var initializationPromise = null; \/\/ Store the promise for initialization         var currentMode = null;          \/**          * Initialize facemesh library here          *\/         function initialize() {             if (initializationPromise) {                 return initializationPromise; \/\/ Return existing promise if initialization is already in progress             }              initializationPromise = new Promise((resolve, reject) =&gt; {                 Promise.all([                     import('..\/core\/camera_utils.js'),                     import('..\/core\/control_utils.js'),                     import('..\/core\/drawing_utils.js'),                     import('..\/core\/face-mesh.js'),                 ])                     .then(async ([                         camera_utils,                         control_utils,                         drawing_utils,                         face_mesh,                     ]) =&gt; {                          faceMesh = await new FaceMesh({                             locateFile: (file) =&gt; {                                 return `\/mirror\/Lib\/Mediapipe\/face_mesh\/core\/wasm\/${file}`; \/\/TODO correct the path of images                             }                         });                          faceMesh.setOptions({                             maxNumFaces: 1,                             refineLandmarks: true,                             minDetectionConfidence: 0.5,                             minTrackingConfidence: 0.5                         });                          resolve(); \/\/ Resolve the promise once initialization is complete                     })                     .catch(error =&gt; {                         reject(error); \/\/ Reject the promise if initialization fails                     });             });              return initializationPromise;         }          \/**          * Apply effect by a given result landmarks and effect object          * @param landmarks          * @param effectObject          * @param resultCanvasHTMLObject          *\/         function applyEffect(landmarks, effectObject, resultCanvasHTMLObject) {             let detectionData = null;              switch (effectObject.effect) {                  case Constants.EFFECT_BROWS_COLOR:                     detectionData = BrowsDetector.detect(                         resultCanvasHTMLObject,                         landmarks,                         FACEMESH_LEFT_EYEBROW,                         FACEMESH_RIGHT_EYEBROW                     );                     BrowsColorEffect.apply(resultCanvasHTMLObject, detectionData, effectObject);                     break;                  case Constants.EFFECT_LIPSTICK:                     detectionData = LipsDetector.detect(resultCanvasHTMLObject, landmarks, FACEMESH_LIPS);                     LipstickEffect.apply(resultCanvasHTMLObject, detectionData, effectObject);                     break;                  case Constants.EFFECT_LIPLINER:                     detectionData = LipsDetector.detect(resultCanvasHTMLObject, landmarks, FACEMESH_LIPS);                     LipLinerEffect.apply(resultCanvasHTMLObject, detectionData, effectObject);                     break;                  case Constants.EFFECT_LIP_GLOSS:                     detectionData = LipsDetector.detect(resultCanvasHTMLObject, landmarks, FACEMESH_LIPS);                     LipGlossEffect.apply(resultCanvasHTMLObject, detectionData, effectObject);                     break;                  case Constants.EFFECT_LIPSTICK_SHIMMER:                     detectionData = LipsDetector.detect(resultCanvasHTMLObject, landmarks, FACEMESH_LIPS);                     LipstickShimmerEffect.apply(resultCanvasHTMLObject, detectionData, effectObject);                     break;                  case Constants.EFFECT_MATTE_LIPSTICK:                     detectionData = LipsDetector.detect(resultCanvasHTMLObject, landmarks, FACEMESH_LIPS);                     LipstickMatteEffect.apply(resultCanvasHTMLObject, detectionData, effectObject);                     break;                  case Constants.EFFECT_EYELINER:                     detectionData = EyesDetector.detect(                         resultCanvasHTMLObject,                         landmarks,                         FACEMESH_TESSELATION,                         FACEMESH_LEFT_EYE,                         FACEMESH_RIGHT_EYE                     );                     EyelinerEffect.apply(resultCanvasHTMLObject, detectionData, effectObject);                     break;                  case Constants.EFFECT_EYESHADOW_SATIN:                     detectionData = EyesDetector.detect(                         resultCanvasHTMLObject,                         landmarks,                         FACEMESH_TESSELATION,                         FACEMESH_LEFT_EYE,                         FACEMESH_RIGHT_EYE                     );                     EyeShadowSatin.apply(resultCanvasHTMLObject, detectionData, effectObject);                     break;                  case Constants.EFFECT_EYESHADOW_MATTE:                     detectionData = EyesDetector.detect(                         resultCanvasHTMLObject,                         landmarks,                         FACEMESH_TESSELATION,                         FACEMESH_LEFT_EYE,                         FACEMESH_RIGHT_EYE                     );                     EyeShadowMatte.apply(resultCanvasHTMLObject, detectionData, effectObject);                     break;                  case Constants.EFFECT_EYESHADOW_SHIMMER:                     detectionData = EyesDetector.detect(                         resultCanvasHTMLObject,                         landmarks,                         FACEMESH_TESSELATION,                         FACEMESH_LEFT_EYE,                         FACEMESH_RIGHT_EYE                     );                     EyeShadowShimmer.apply(resultCanvasHTMLObject, detectionData, effectObject);                     break;                  case Constants.EFFECT_KAJAL:                     detectionData = EyesDetector.detect(                         resultCanvasHTMLObject,                         landmarks,                         FACEMESH_TESSELATION,                         FACEMESH_LEFT_EYE,                         FACEMESH_RIGHT_EYE                     );                     KajalEffect.apply(resultCanvasHTMLObject, detectionData, effectObject);                     break;                  case Constants.EFFECT_MASCARA:                     detectionData = EyesDetector.detect(                         resultCanvasHTMLObject,                         landmarks,                         FACEMESH_TESSELATION,                         FACEMESH_LEFT_EYE,                         FACEMESH_RIGHT_EYE                     );                     MascaraEffect.apply(resultCanvasHTMLObject, detectionData, effectObject);                     break;                  case Constants.EFFECT_EYEGLASSES:                     detectionData = EyesDetector.detect(                         resultCanvasHTMLObject,                         landmarks,                         FACEMESH_TESSELATION,                         FACEMESH_LEFT_EYE,                         FACEMESH_RIGHT_EYE                     );                     let jawData = JawDetector.detect(                         resultCanvasHTMLObject,                         landmarks,                         FACEMESH_FACE_OVAL                     );                     EyeGlassesEffect.apply(resultCanvasHTMLObject, detectionData, jawData, effectObject);                     break;                  case Constants.EFFECT_FOUNDATION_SATIN:                     detectionData = Object.assign(                         EyesDetector.detect(resultCanvasHTMLObject, landmarks, FACEMESH_TESSELATION, FACEMESH_LEFT_EYE, FACEMESH_RIGHT_EYE),                         FaceDetector.detect(resultCanvasHTMLObject, landmarks, FACEMESH_FACE_OVAL),                         BrowsDetector.detect(resultCanvasHTMLObject, landmarks, FACEMESH_LEFT_EYEBROW, FACEMESH_RIGHT_EYEBROW),                         LipsDetector.detect(resultCanvasHTMLObject, landmarks, FACEMESH_LIPS)                     );                      FoundationSatinEffect.apply(resultCanvasHTMLObject, detectionData, effectObject);                     break;                  case Constants.EFFECT_FOUNDATION_MATTE:                     detectionData = Object.assign(                         EyesDetector.detect(resultCanvasHTMLObject, landmarks, FACEMESH_TESSELATION, FACEMESH_LEFT_EYE, FACEMESH_RIGHT_EYE),                         FaceDetector.detect(resultCanvasHTMLObject, landmarks, FACEMESH_FACE_OVAL),                         BrowsDetector.detect(resultCanvasHTMLObject, landmarks, FACEMESH_LEFT_EYEBROW, FACEMESH_RIGHT_EYEBROW),                         LipsDetector.detect(resultCanvasHTMLObject, landmarks, FACEMESH_LIPS)                     );                      FoundationMatteEffect.apply(resultCanvasHTMLObject, detectionData, effectObject);                     break;                  case Constants.EFFECT_CONCEALER:                     detectionData = EyesDetector.detect(                         resultCanvasHTMLObject,                         landmarks,                         FACEMESH_TESSELATION,                         FACEMESH_LEFT_EYE,                         FACEMESH_RIGHT_EYE                     );                      ConcealerEffect.apply(resultCanvasHTMLObject, detectionData, effectObject);                     break;                  case Constants.EFFECT_CONTOUR:                     detectionData = FaceDetector.detect(resultCanvasHTMLObject, landmarks, FACEMESH_FACE_OVAL);                     ContourEffect.apply(resultCanvasHTMLObject, detectionData, effectObject);                     break;                  default:                     throw new Error('Unknown effect type: ' + effectObject.effect);                     break;             }         }          return { \/\/ Public Area              \/**              * Process video or image with requested effect              *              * Where:              *  processedElementHTMLObject - it can be either &lt;img&gt; html object or &lt;video&gt; html object              *  resultCanvasHTMLObject - canvas where to show the resulting output              *  effectObject - object with effect settings (see FaceMesh documentation to get more info)              *              * @param processedElementHTMLObject              * @param resultCanvasHTMLObject              * @param effectObject              * @returns {Promise&lt;boolean&gt;}              *\/             process: async function (processedElementHTMLObject, resultCanvasHTMLObject, effectObject) {                 let landmarksDetected = false;                  if (faceMesh == undefined || faceMesh == null) {                     await initialize();                 }                  if (currentMode !== processedElementHTMLObject.tagName.toLowerCase()) {                     currentMode = processedElementHTMLObject.tagName.toLowerCase();                     await faceMesh.reset();                 }                  faceMesh.onResults(async function (results) {                     let faceLandmarksPoints = results.multiFaceLandmarks[0];                      if (faceLandmarksPoints) {                         let resultCanvasContext = resultCanvasHTMLObject.getContext('2d');                          \/\/ clean result canvas and display captured image from video                         resultCanvasContext.clearRect(                             0, 0, processedElementHTMLObject.clientWidth, processedElementHTMLObject.clientHeight                         );                         resultCanvasContext.drawImage(                             results.image, 0, 0, resultCanvasHTMLObject.width, resultCanvasHTMLObject.height                         );                          await applyEffect(faceLandmarksPoints, effectObject, resultCanvasHTMLObject);                          landmarksDetected = true;                     } else {                         landmarksDetected = false;                     }                 });                  await faceMesh.send({image: processedElementHTMLObject});                  return landmarksDetected;             },         };     } )();  export default FaceMeshEngine;<\/code><\/pre>\n<\/div>\n<\/details>\n<h3>\u0421\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0430 \u043f\u0440\u043e\u0435\u043a\u0442\u0430: Utility<\/h3>\n<p><a class=\"anchor\" name=\"utility\" id=\"utility\"><\/a><\/p>\n<p>\u041f\u0430\u043f\u043a\u0430 <code>Utility<\/code> \u0432 \u043f\u0440\u043e\u0435\u043a\u0442\u0435 \u0441\u043b\u0443\u0436\u0438\u0442 \u043a\u0430\u043a \u0443\u043d\u0438\u0432\u0435\u0440\u0441\u0430\u043b\u044c\u043d\u043e\u0435 \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0435 \u0432\u0441\u043f\u043e\u043c\u043e\u0433\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u0445 \u043c\u043e\u0434\u0443\u043b\u0435\u0439, \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u044e\u0449\u0438\u0445 \u043e\u0431\u0449\u0438\u0435 \u0444\u0443\u043d\u043a\u0446\u0438\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043c\u043e\u0433\u0443\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u0432 \u043b\u044e\u0431\u043e\u043c \u043c\u0435\u0441\u0442\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f. \u0412 \u043d\u0435\u0439 \u0441\u043e\u0431\u0440\u0430\u043d\u044b \u043d\u0435\u0437\u0430\u0432\u0438\u0441\u0438\u043c\u044b\u0435 \u0443\u0442\u0438\u043b\u0438\u0442\u044b, \u0440\u0435\u0448\u0430\u044e\u0449\u0438\u0435 \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u044b\u0435 \u0437\u0430\u0434\u0430\u0447\u0438, \u043d\u0435 \u043f\u0440\u0438\u0432\u044f\u0437\u0430\u043d\u043d\u044b\u0435 \u043a \u043b\u043e\u0433\u0438\u043a\u0435 \u0434\u0432\u0438\u0436\u043a\u043e\u0432 \u0438\u043b\u0438 \u0441\u043b\u043e\u0451\u0432 \u0440\u0430\u0441\u043f\u043e\u0437\u043d\u0430\u0432\u0430\u043d\u0438\u044f \u043b\u0438\u0446\u0430.<\/p>\n<p>\u041f\u0440\u0438\u043c\u0435\u0440 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u044b:<\/p>\n<pre><code>- Utility   - ColorUtility.js   - CoordinatesUtility.js   - DrawUtility.js   - SafariUtility.js   - TextureUtility.js<\/code><\/pre>\n<ul>\n<li>\n<p><strong>ColorUtility.js &#8212; <\/strong>\u043e\u0442\u0432\u0435\u0447\u0430\u0435\u0442 \u0437\u0430 \u0440\u0430\u0431\u043e\u0442\u0443 \u0441 \u0446\u0432\u0435\u0442\u0430\u043c\u0438: \u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u043e\u0432\u0430\u043d\u0438\u0435 \u0444\u043e\u0440\u043c\u0430\u0442\u043e\u0432, \u0432\u044b\u0447\u0438\u0441\u043b\u0435\u043d\u0438\u0435 \u043f\u0440\u043e\u0437\u0440\u0430\u0447\u043d\u043e\u0441\u0442\u0438, \u043d\u0430\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u043e\u0442\u0442\u0435\u043d\u043a\u043e\u0432 \u0438 \u0434\u0440\u0443\u0433\u0438\u0435 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438, \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b\u0435 \u0434\u043b\u044f \u043a\u043e\u0440\u0440\u0435\u043a\u0442\u043d\u043e\u0433\u043e \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u0432\u0438\u0440\u0442\u0443\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u043c\u0430\u043a\u0438\u044f\u0436\u0430. \u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u043c\u043e\u0436\u0435\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u0434\u043b\u044f \u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u043e\u0432\u0430\u043d\u0438\u044f HEX \u0432 RGBA \u0438 \u0434\u0440.<\/p>\n<\/li>\n<\/ul>\n<details class=\"spoiler\">\n<summary>ColorUtility.js<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"javascript\">const ColorUtility= (function () {     return { \/\/ Public Area          \/**          * Check if the \"color\" is a valid hexadecimal color code          * @param colorValue          * @returns {boolean}          *\/         isHexColor: function (colorValue) {             const colorRegex = \/^#[0-9A-Fa-f]{6}$\/;             return colorRegex.test(colorValue);         },          \/**          * Mix color in natural way          * where factor is domination of desired color from 0 to 1          * @param desiredColor          * @param originalColor          * @param factor          * @param transparency          * @returns {{a: number, r: *, b: *, g: *}}          *\/         interpolateColors: function (desiredColor, originalColor, factor, transparency) {             let p = factor \/ 100;             return {                 r: (desiredColor.r - originalColor.r) * p + originalColor.r,                 g: (desiredColor.g - originalColor.g) * p + originalColor.g,                 b: (desiredColor.b - originalColor.b) * p + originalColor.b,                 a: Math.round(transparency * 255)             };         },          \/**          * Make color matte          * @param {{a, r: number, b: number, g: number}} color          * @returns {{a, r: number, b: number, g: number}}          *\/         toMatteColor: function (color) {             \/\/ Adjust the saturation and brightness to create a matte effect             const saturation = 0.9; \/\/ Adjust the value as needed             const brightness = 0.9; \/\/ Adjust the value as needed              const hslColor = this.rgbToHsl(color.r, color.g, color.b);              \/\/ Apply the saturation and brightness modifications             const modifiedHslColor = {                 h: hslColor.h,                 s: hslColor.s * saturation,                 l: hslColor.l * brightness,             };              \/\/ Convert the modified HSL color back to RGB color space             const modifiedRgbColor = this.hslToRgb(modifiedHslColor.h, modifiedHslColor.s, modifiedHslColor.l);              return {                 r: modifiedRgbColor.r,                 g: modifiedRgbColor.g,                 b: modifiedRgbColor.b,                 a: color.a,             };         },          \/**          * Warning! areaColor and desiredColor must represent an object: {r: number, b: number, g: number}          * Return average color between applied area (e.g. lips) and desired color          * Need it to get more natural effect          *          * @param {{a, r: number, b: number, g: number}} areaColor          * @param {{a, r: number, b: number, g: number}} desiredColor          * @returns {{r: number, b: number, g: number}}          *\/         getAverageColor: function (areaColor, desiredColor) {             return {                 r: Math.round((areaColor.r + desiredColor.r) \/ 2),                 g: Math.round((areaColor.g + desiredColor.g) \/ 2),                 b: Math.round((areaColor.b + desiredColor.b) \/ 2)             };         },          \/**          * saturationIncrease can be between 0 and 1          * @param rgbColor          * @param saturation          * @returns {*}          *\/         increaseSaturation: function (rgbColor, saturation) {             if (saturation &lt; 0 || saturation &gt; 1) {                 throw new Error('Invalid saturation value.');             }              let hslColor = this.rgbToHsl(rgbColor.r, rgbColor.g, rgbColor.b);              \/\/ Increase the saturation             hslColor.s += saturation;              \/\/ Ensure saturation is within [0, 1] range             hslColor.s = Math.max(0, Math.min(1, hslColor.s));              \/\/ Convert HSL back to RGB             return this.hslToRgb(hslColor.h, hslColor.s, hslColor.l);         },          \/**          * Convert hex color to RGB object          *          * @param hex          * @returns {{r: number, b: number, g: number}}          *\/         getRgbFromHex: function (hex) {             hex = hex.replace('#', '');              let decimal = parseInt(hex, 16); \/\/ Convert hexadecimal to decimal             let r = (decimal &gt;&gt; 16) &amp; 255; \/\/ Extract red component from decimal value             let g = (decimal &gt;&gt; 8) &amp; 255; \/\/ Extract green component from decimal value             let b = decimal &amp; 255; \/\/ Extract blue component from decimal value              \/\/don't allow 0 value, it won't be applied in mask             r = r + 1;             g = g + 1;             b = b + 1;              return {r, g, b};         },          \/**          * @param r          * @param g          * @param b          * @returns {{s: number, h: number, l: number}}          *\/         rgbToHsl: function (r, g, b) {             r \/= 255;             g \/= 255;             b \/= 255;              const max = Math.max(r, g, b);             const min = Math.min(r, g, b);             let h, s, l = (max + min) \/ 2;              if (max === min) {                 h = s = 0; \/\/ achromatic             } else {                 const d = max - min;                 s = l &gt; 0.5 ? d \/ (2 - max - min) : d \/ (max + min);                 switch (max) {                     case r:                         h = (g - b) \/ d + (g &lt; b ? 6 : 0);                         break;                     case g:                         h = (b - r) \/ d + 2;                         break;                     case b:                         h = (r - g) \/ d + 4;                         break;                 }                 h \/= 6;             }              return {                 h: h,                 s: s,                 l: l             };         },          \/**          * @param h          * @param s          * @param l          * @returns {{r: number, b: number, g: number}}          *\/         hslToRgb: function (h, s, l) {             let r, g, b;              if (s === 0) {                 r = g = b = l; \/\/ achromatic             } else {                 function hue2rgb(p, q, t) {                     if (t &lt; 0) t += 1;                     if (t &gt; 1) t -= 1;                     if (t &lt; 1 \/ 6) return p + (q - p) * 6 * t;                     if (t &lt; 1 \/ 2) return q;                     if (t &lt; 2 \/ 3) return p + (q - p) * (2 \/ 3 - t) * 6;                     return p;                 }                  const q = l &lt; 0.5 ? l * (1 + s) : l + s - l * s;                 const p = 2 * l - q;                  r = hue2rgb(p, q, h + 1 \/ 3);                 g = hue2rgb(p, q, h);                 b = hue2rgb(p, q, h - 1 \/ 3);             }              return {                 r: Math.round(r * 255),                 g: Math.round(g * 255),                 b: Math.round(b * 255),             };         },          \/**          * Return average color of provided pixels from source canvas image          * Need to know this color to avoid not natural applied effect          *          * @param coordinates          * @param canvasContext          * @returns {{r: number, b: number, g: number}}          *\/         getAveragePixelsRgbColor: function (coordinates, canvasContext) {             let averageColor = {r: 0, g: 0, b: 0};             let width = 1; \/\/ width of the area to capture (in this case, 1 pixel)             let height = 1; \/\/ height of the area to capture (in this case, 1 pixel)              for (var i = 0; i &lt; coordinates.length; i++) {                  \/\/ Capture the image data of the current pixel area                 let imageData = canvasContext.getImageData(                     coordinates[i].x,                     coordinates[i].y,                     width,                     height                 );                  averageColor.r += imageData.data[0]; \/\/ Red component of the pixel color                 averageColor.g += imageData.data[1]; \/\/ Green component of the pixel color                 averageColor.b += imageData.data[2]; \/\/ Blue component of the pixel color             }              \/\/ Divide the total color components by the number of areas to get the average color             \/\/ and round the average color components to integers             let numAreas = coordinates.length;              averageColor.r = Math.round(averageColor.r \/ numAreas);             averageColor.g = Math.round(averageColor.g \/ numAreas);             averageColor.b = Math.round(averageColor.b \/ numAreas);              return averageColor;         }     }; })();  export default ColorUtility; <\/code><\/pre>\n<\/div>\n<\/details>\n<ul>\n<li>\n<p><strong>CoordinatesUtility.js &#8212; <\/strong>\u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u0430\u043c\u0438, \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u043d\u044b\u043c\u0438 \u043e\u0442 \u0434\u0432\u0438\u0436\u043a\u043e\u0432 \u0440\u0430\u0441\u043f\u043e\u0437\u043d\u0430\u0432\u0430\u043d\u0438\u044f \u043b\u0438\u0446\u0430. <\/p>\n<\/li>\n<\/ul>\n<details class=\"spoiler\">\n<summary>CoordinatesUtility.js<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"javascript\">const CoordinatesUtility = (function () {     return { \/\/ Public Area         \/**          * Return distance in pixels          * @param point1          * @param point2          *          * where point is:          * {          *  x: number,          *  y: number          * }          * @returns {number}          *\/         getDistanceBetweenPoints: function (point1, point2) {             return Math.sqrt(Math.pow(point1.x - point2.x, 2) + Math.pow(point1.y - point2.y, 2));         },     }; })();  export default CoordinatesUtility; <\/code><\/pre>\n<\/div>\n<\/details>\n<ul>\n<li>\n<p><strong>DrawUtility.js &#8212; <\/strong>\u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u043e\u0442\u0440\u0438\u0441\u043e\u0432\u043a\u0438 \u043d\u0430 \u043a\u0430\u043d\u0432\u0430\u0441\u0435<\/p>\n<\/li>\n<\/ul>\n<details class=\"spoiler\">\n<summary>DrawUtility.js<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"javascript\">const DrawUtility = (function () {     return { \/\/ Public Area         \/**          * Draw contour by provided coordinates          *          * @param canvasContext          * @param coordinates          * @param options          *\/         drawContour: function (canvasContext, coordinates, options = {}) {             if (options.globalCompositeOperation) {                 canvasContext.globalCompositeOperation = options.globalCompositeOperation;             }              canvasContext.beginPath();              for (let i = 0; i &lt; coordinates.length; i++) {                 const point = coordinates[i];                 (i === 0) ? canvasContext.moveTo(point.x, point.y) : canvasContext.lineTo(point.x, point.y);             }              canvasContext.closePath();              if (options.fillStyle) {                 canvasContext.fillStyle = options.fillStyle;                 canvasContext.fill();             }              if (options.lineWidth) {                 canvasContext.lineWidth = options.lineWidth;             }              if (options.strokeStyle) {                 canvasContext.strokeStyle = options.strokeStyle;                 canvasContext.stroke();             }              canvasContext.globalCompositeOperation = 'source-over'; \/\/set to default value         }     }; })();  export default DrawUtility; <\/code><\/pre>\n<\/div>\n<\/details>\n<ul>\n<li>\n<p><strong>SafariUtility.js &#8212; <\/strong>\u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442 \u043d\u0430\u0431\u043e\u0440 \u0440\u0435\u0448\u0435\u043d\u0438\u0439, \u043d\u0430\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u043d\u044b\u0445 \u043d\u0430 \u043e\u0431\u0435\u0441\u043f\u0435\u0447\u0435\u043d\u0438\u0435 \u0441\u043e\u0432\u043c\u0435\u0441\u0442\u0438\u043c\u043e\u0441\u0442\u0438 \u0441 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u043e\u043c Safari, \u0432\u043a\u043b\u044e\u0447\u0430\u044f \u043c\u0435\u0442\u043e\u0434\u044b \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0438\u044f \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0430, \u0430 \u0442\u0430\u043a\u0436\u0435 \u043f\u0440\u0438\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u0440\u0430\u0437\u043c\u044b\u0442\u0438\u044f. <\/p>\n<\/li>\n<\/ul>\n<details class=\"spoiler\">\n<summary>SafariUtility.js<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"javascript\">const SafariUtility = (function () {      \/\/ Private Area     var canvas = null;     var ctx = null;     var canvas_off = null;     var ctx_off = null;      return { \/\/ Public Area         \/**          * @returns {boolean}          *\/         isSafari: function () {             return \/^((?!chrome|android).)*safari\/i.test(navigator.userAgent);         },          \/**          * Set canvas before working          * @param canvasObject          *\/         setCanvas(canvasObject){             canvas = canvasObject;             ctx = canvasObject.getContext('2d');             let w = canvasObject.width;             let h = canvasObject.height;             canvas_off = document.createElement(\"canvas\");             ctx_off = canvas_off.getContext(\"2d\");             canvas_off.width = w;             canvas_off.height = h;             ctx_off.drawImage(canvasObject, 0, 0);         },          \/**          * Recover canvas          *\/         recoverCanvas(){             let w = canvas_off.width;             let h = canvas_off.height;             canvas.width = w;             canvas.height = h;             ctx.drawImage(this.canvas_off,0,0);         },          \/**          * Gassuan blur          * @param blur          *\/         gBlur(blur) {             let sum = 0;             let delta = 5;             let alpha_left = 1 \/ (2 * Math.PI * delta * delta);             let step = blur &lt; 3 ? 1 : 2;             for (let y = -blur; y &lt;= blur; y += step) {                 for (let x = -blur; x &lt;= blur; x += step) {                     let weight = alpha_left * Math.exp(-(x * x + y * y) \/ (2 * delta * delta));                     sum += weight;                 }             }             let count = 0;             for (let y = -blur; y &lt;= blur; y += step) {                 for (let x = -blur; x &lt;= blur; x += step) {                     count++;                     ctx.globalAlpha = alpha_left * Math.exp(-(x * x + y * y) \/ (2 * delta * delta)) \/ sum * blur;                     ctx.drawImage(canvas,x,y);                 }             }             ctx.globalAlpha = 1;         },          \/**          * @param distance          *\/         mBlur(distance){             distance = distance&lt;0?0:distance;             let w = canvas.width;             let h = canvas.height;             canvas.width = w;             canvas.height = h;             ctx.clearRect(0,0,w,h);              for(let n=0;n&lt;5;n+=0.1){                 ctx.globalAlpha = 1\/(2*n+1);                 let scale = distance\/5*n;                 ctx.transform(1+scale,0,0,1+scale,0,0);                 ctx.drawImage(canvas_off, 0, 0);             }             ctx.globalAlpha = 1;             if(distance&lt;0.01){                 window.requestAnimationFrame(()=&gt;{                     this.mBlur(distance+0.0005);                 });             }         }     }; })();  export default SafariUtility; <\/code><\/pre>\n<\/div>\n<\/details>\n<ul>\n<li>\n<p><strong>TextureUtility.js &#8212; <\/strong>\u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u0442 \u0442\u0435\u043a\u0441\u0442\u0443\u0440\u044b, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u044b\u0435 \u0432 \u043c\u0430\u043a\u0438\u044f\u0436\u0435.<\/p>\n<\/li>\n<\/ul>\n<details class=\"spoiler\">\n<summary>TextureUtility.js<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"javascript\">const TextureUtility = (function () {     return { \/\/ Public Area         \/**          * Draw shimmer effect by coordinates          * where coordinates represent the array of the following objects:          *          * {          *  x: x,          *  y: y,          *  offsetX: 1,          *  offsetY: 2,          *  speedX: Math.random() * 2 - 1, \/\/ Random horizontal speed          *  speedY: Math.random() * 2 - 1, \/\/ Random vertical speed          * }          *          * @param canvas          * @param shimmerCoordinates          * @param shimmerSize          *\/         applyShimmer: function (canvas, shimmerCoordinates, shimmerSize) {             shimmerCoordinates.forEach(shimmer =&gt; {                 let canvasContext = canvas.getContext('2d');                 canvasContext.fillStyle = '#ffffff';                  canvasContext.beginPath();                 canvasContext.arc(shimmer.x, shimmer.y, shimmerSize, 0, Math.PI * 2);                 canvasContext.fill();                  \/\/ Update glitter position                 shimmer.x += shimmer.speedX;                 shimmer.y += shimmer.speedY;                  \/\/ Wrap around canvas edges                 if (shimmer.x &lt; 0 || shimmer.x &gt; canvas.width) {                     shimmer.x = shimmer.offsetX;                 }                 if (shimmer.y &lt; 0 || shimmer.y &gt; canvas.height) {                     shimmer.y = shimmer.offsetY;                 }             });         }     }; })();  export default TextureUtility; <\/code><\/pre>\n<\/div>\n<\/details>\n<h3>\u0421\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0430 \u043f\u0440\u043e\u0435\u043a\u0442\u0430: Effect<\/h3>\n<p><a class=\"anchor\" name=\"effect\" id=\"effect\"><\/a><\/p>\n<p>\u0417\u0434\u0435\u0441\u044c \u0445\u0440\u0430\u043d\u044f\u0442\u0441\u044f \u043f\u0440\u0438\u043c\u0435\u043d\u044f\u0435\u043c\u044b\u0435 \u044d\u0444\u0444\u0435\u043a\u0442\u044b. \u0414\u043b\u044f \u0443\u0434\u043e\u0431\u0441\u0442\u0432\u0430, \u044f \u0440\u0430\u0437\u0434\u0435\u043b\u0438\u043b\u0430 \u044d\u0444\u0444\u0435\u043a\u0442\u044b \u043d\u0430 \u0434\u0432\u0435 \u0433\u0440\u0443\u043f\u043f\u044b &#8212; \u0430\u043a\u0441\u0435\u0441\u0441\u0443\u0430\u0440\u044b \u0438 \u043c\u0430\u043a\u0438\u044f\u0436.<\/p>\n<p>\u0420\u0430\u0431\u043e\u0442\u0430 \u0441 \u0430\u043a\u0441\u0435\u0441\u0441\u0443\u0430\u0440\u0430\u043c\u0438 \u043d\u0430 \u0434\u0430\u043d\u043d\u044b\u0439 \u043c\u043e\u043c\u0435\u043d\u0442 \u0438\u0434\u0435\u0442 \u0447\u0435\u0440\u0435\u0437 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f 2D \u043c\u0430\u0441\u043a\u0438 \u0438 \u043d\u0430\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0435\u0435 \u043d\u0430 \u043b\u0438\u0446\u043e. \u041a\u0430\u043a \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u043c\u0430\u0441\u043a\u0438 \u043d\u0430 \u043f\u0440\u0438\u043c\u0435\u0440\u0435 \u0441 \u043e\u0447\u043a\u0430\u043c\u0438 \u0441\u043c\u043e\u0442\u0440\u0438\u0442\u0435 \u043d\u0438\u0436\u0435.<\/p>\n<p>\u042d\u0444\u0444\u0435\u043a\u0442\u044b, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043e\u0442\u043d\u043e\u0441\u044f\u0442\u0441\u044f \u043a &#171;\u043c\u0430\u043a\u0438\u044f\u0436\u0443&#187; \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442\u0441\u044f \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0445 \u0441\u043a\u0440\u044b\u0442\u044b\u0445 \u0445\u043e\u043b\u0441\u0442\u043e\u0432 (canvas) \u0438 \u0430\u0434\u0430\u043f\u0442\u0438\u0440\u0443\u0435\u0442\u0441\u044f \u043f\u043e\u0434 \u043e\u0441\u043e\u0431\u0435\u043d\u043d\u043e\u0441\u0442\u0438 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u043e\u0432, \u043e\u0431\u0435\u0441\u043f\u0435\u0447\u0438\u0432\u0430\u044f \u0440\u0435\u0430\u043b\u0438\u0441\u0442\u0438\u0447\u043d\u044b\u0439 \u0438 \u0435\u0441\u0442\u0435\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0439 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442. <\/p>\n<p>\u041a\u0430\u0436\u0434\u044b\u0439 \u0432\u0438\u0437\u0443\u0430\u043b\u044c\u043d\u044b\u0439 \u044d\u0444\u0444\u0435\u043a\u0442 \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d \u0432 \u0432\u0438\u0434\u0435 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e\u0433\u043e \u043a\u043b\u0430\u0441\u0441\u0430 \u0441 \u043c\u0435\u0442\u043e\u0434\u043e\u043c <code>apply()<\/code>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043e\u0442\u0432\u0435\u0447\u0430\u0435\u0442 \u0437\u0430 \u043e\u0442\u0440\u0438\u0441\u043e\u0432\u043a\u0443 \u0438 \u043f\u0440\u0438\u043c\u0435\u043d\u0435\u043d\u0438\u0435 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u0430 \u043d\u0430 \u043a\u0430\u043d\u0432\u0430\u0441. \u0412\u043d\u0443\u0442\u0440\u0438 \u043a\u0430\u0436\u0434\u043e\u0433\u043e \u0442\u0430\u043a\u043e\u0433\u043e \u043a\u043b\u0430\u0441\u0441\u0430 \u043d\u0430\u0445\u043e\u0434\u0438\u0442\u0441\u044f \u043e\u0431\u044a\u0435\u043a\u0442 \u0441 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430\u043c\u0438 \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043c\u043e\u0436\u043d\u043e \u043f\u0435\u0440\u0435\u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u0442\u044c \u0432 \u0440\u0430\u043d\u0442\u0430\u0439\u043c\u0435 \u0432 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u043e\u0442 \u043d\u0443\u0436\u0434 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0438\u043b\u0438 \u0443\u0441\u043b\u043e\u0432\u0438\u0439 \u0432\u0438\u0437\u0443\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438.<\/p>\n<details class=\"spoiler\">\n<summary>\u041f\u0440\u0438\u043c\u0435\u0440 \u0431\u0430\u0437\u043e\u0432\u043e\u0433\u043e \u043e\u0431\u044a\u0435\u043a\u0442\u0430 \u0441 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430\u043c\u0438 \u0434\u043b\u044f \u044d\u0444\u0444\u0435\u043a\u0442\u0430 \u043c\u0430\u0442\u043e\u0432\u043e\u0439 \u043f\u043e\u043c\u0430\u0434\u044b<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"javascript\">const defaults = {     transparency: 0.6,         \/\/ \u0433\u043b\u043e\u0431\u0430\u043b\u044c\u043d\u0430\u044f \u043f\u0440\u043e\u0437\u0440\u0430\u0447\u043d\u043e\u0441\u0442\u044c \u044d\u0444\u0444\u0435\u043a\u0442\u0430     blur: 2,                   \/\/ \u0443\u0440\u043e\u0432\u0435\u043d\u044c \u0440\u0430\u0437\u043c\u044b\u0442\u0438\u044f \u0434\u043b\u044f \u0441\u0433\u043b\u0430\u0436\u0438\u0432\u0430\u043d\u0438\u044f     safariBlur: 1.5            \/\/ \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0431\u043b\u044e\u0440\u0430 \u0434\u043b\u044f Safari }; <\/code><\/pre>\n<\/div>\n<\/details>\n<p>\u0414\u043b\u044f \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u0438\u0437\u0430\u0446\u0438\u0438 \u0438 \u0443\u043d\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043f\u0435\u0440\u0435\u0434\u0430\u044e\u0442\u0441\u044f \u0432 \u0432\u0438\u0437\u0443\u0430\u043b\u044c\u043d\u044b\u0435 \u044d\u0444\u0444\u0435\u043a\u0442\u044b, \u0431\u044b\u043b \u0432\u0432\u0435\u0434\u0451\u043d \u043e\u0431\u044a\u0435\u043a\u0442 <code>effectSettings<\/code>. \u041e\u043d \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u0441\u043e\u0431\u043e\u0439 DTO (Data Transfer Object) \u2014 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443 \u0434\u0430\u043d\u043d\u044b\u0445, \u0441\u043b\u0443\u0436\u0430\u0449\u0443\u044e \u0434\u043b\u044f \u043f\u0435\u0440\u0435\u0434\u0430\u0447\u0438 \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043a \u0438\u0437 \u0432\u043d\u0435\u0448\u043d\u0435\u0433\u043e \u0441\u043b\u043e\u044f (\u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, UI \u0438\u043b\u0438 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f) \u0432 \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u0443\u044e \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044e \u044d\u0444\u0444\u0435\u043a\u0442\u0430.<\/p>\n<p>\u041f\u0435\u0440\u0435\u0434 \u043f\u0440\u0438\u043c\u0435\u043d\u0435\u043d\u0438\u0435\u043c \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432, \u043a\u0430\u0436\u0434\u044b\u0439 \u044d\u0444\u0444\u0435\u043a\u0442 \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u0442 \u0443\u0442\u0438\u043b\u0438\u0442\u0443 <code>isValidEffectSettings()<\/code> \u2014 \u043e\u043d\u0430 \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u0442 \u043d\u0430\u043b\u0438\u0447\u0438\u0435 \u0438 \u0444\u043e\u0440\u043c\u0430\u0442 \u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u0445 \u043f\u043e\u043b\u0435\u0439, \u0442\u0430\u043a\u0438\u0445 \u043a\u0430\u043a <code>value<\/code>. \u042d\u0442\u043e \u0437\u0430\u0449\u0438\u0449\u0430\u0435\u0442 \u043e\u0442 \u0441\u043b\u0443\u0447\u0430\u0439\u043d\u044b\u0445 \u043e\u0448\u0438\u0431\u043e\u043a \u043f\u0440\u0438 \u043d\u0435\u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u043e\u0439 \u043f\u0435\u0440\u0435\u0434\u0430\u0447\u0435 \u0434\u0430\u043d\u043d\u044b\u0445.<\/p>\n<p>\u041f\u043e\u0441\u043a\u043e\u043b\u044c\u043a\u0443 <code>effectSettings<\/code> \u2014 \u043e\u0431\u044b\u0447\u043d\u044b\u0439 JavaScript-\u043e\u0431\u044a\u0435\u043a\u0442, \u0435\u0433\u043e \u043c\u043e\u0436\u043d\u043e \u0430\u0434\u0430\u043f\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043f\u043e\u0434 \u043b\u044e\u0431\u044b\u0435 \u043d\u0443\u0436\u0434\u044b:<\/p>\n<pre><code class=\"javascript\">{     value: '#D93F87',     saturationBoost: 0.3,         \/\/ \u0434\u043b\u044f \u044d\u0444\u0444\u0435\u043a\u0442\u0430 \u0443\u0432\u0435\u043b\u0438\u0447\u0435\u043d\u0438\u044f \u043d\u0430\u0441\u044b\u0449\u0435\u043d\u043d\u043e\u0441\u0442\u0438     useMatteStyle: true,          \/\/ \u043d\u0435\u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u043e\u0435 \u043f\u043e\u0432\u0435\u0434\u0435\u043d\u0438\u0435 \u043e\u0442\u0440\u0438\u0441\u043e\u0432\u043a\u0438     safariFallbackEnabled: false  \/\/ \u043e\u0442\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u0441\u043f\u0435\u0446\u0438\u0444\u0438\u043a\u0438 \u0434\u043b\u044f Safari }<\/code><\/pre>\n<p>\u0412 \u0434\u0430\u043b\u044c\u043d\u0435\u0439\u0448\u0435\u043c \u043c\u043e\u0436\u043d\u043e \u0440\u0430\u0441\u0448\u0438\u0440\u0438\u0442\u044c \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0443 \u044d\u0444\u0444\u0435\u043a\u0442\u043e\u0432 \u043f\u043e\u0434 \u0440\u0430\u0437\u043d\u044b\u0435 \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u0438, \u0432\u043a\u043b\u044e\u0447\u0430\u044f \u0442\u043e\u043d\u043a\u0443\u044e \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0443 \u043d\u0430 \u043e\u0441\u043d\u043e\u0432\u0435 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u043e\u0433\u043e \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0430 (\u043f\u043e\u043b\u0437\u0443\u043d\u043a\u0438, \u043f\u0430\u043b\u0438\u0442\u0440\u044b \u0438 \u0442.\u0434.).<\/p>\n<figure class=\"full-width\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/950\/dee\/c7d\/950deec7de540a48254ba80e01f963d2.png\" alt=\"\u041f\u0440\u0438\u043c\u0435\u0440 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f effectSettings \u043e\u0431\u044a\u0435\u043a\u0442\u0430 \u0432 \u044d\u0444\u0444\u0435\u043a\u0442 \u043a\u043b\u0430\u0441\u0441\u0435\" title=\"\u041f\u0440\u0438\u043c\u0435\u0440 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f effectSettings \u043e\u0431\u044a\u0435\u043a\u0442\u0430 \u0432 \u044d\u0444\u0444\u0435\u043a\u0442 \u043a\u043b\u0430\u0441\u0441\u0435\" width=\"1320\" height=\"865\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/950\/dee\/c7d\/950deec7de540a48254ba80e01f963d2.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/950\/dee\/c7d\/950deec7de540a48254ba80e01f963d2.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/p>\n<div><figcaption>\u041f\u0440\u0438\u043c\u0435\u0440 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f effectSettings \u043e\u0431\u044a\u0435\u043a\u0442\u0430 \u0432 \u044d\u0444\u0444\u0435\u043a\u0442 \u043a\u043b\u0430\u0441\u0441\u0435<\/figcaption><\/div>\n<\/figure>\n<p>\u041e\u0434\u043d\u0430 \u0438\u0437 \u043f\u0435\u0440\u0432\u044b\u0445 \u0441\u043b\u043e\u0436\u043d\u043e\u0441\u0442\u0435\u0439, \u0441 \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u044f \u0441\u0442\u043e\u043b\u043a\u043d\u0443\u043b\u0430\u0441\u044c \u043f\u0440\u0438 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0438 \u0432\u0438\u0440\u0442\u0443\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u043c\u0435\u0439\u043a\u0430\u043f\u0430, \u2014 \u044d\u0442\u043e \u043d\u0435\u0435\u0441\u0442\u0435\u0441\u0442\u0432\u0435\u043d\u043d\u043e\u0435 \u043d\u0430\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0446\u0432\u0435\u0442\u0430. \u041a\u0430\u0437\u0430\u043b\u043e\u0441\u044c \u0431\u044b, \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e \u043f\u0440\u043e\u0441\u0442\u043e \u0432\u0437\u044f\u0442\u044c \u0436\u0435\u043b\u0430\u0435\u043c\u044b\u0439 \u043e\u0442\u0442\u0435\u043d\u043e\u043a (\u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u043f\u043e\u043c\u0430\u0434\u044b \u0438\u043b\u0438 \u0442\u0435\u043d\u0435\u0439) \u0438 &#171;\u0437\u0430\u043a\u0440\u0430\u0441\u0438\u0442\u044c&#187; \u043d\u0443\u0436\u043d\u0443\u044e \u043e\u0431\u043b\u0430\u0441\u0442\u044c \u043d\u0430 \u043b\u0438\u0446\u0435. \u041e\u0434\u043d\u0430\u043a\u043e \u043d\u0430 \u043f\u0440\u0430\u043a\u0442\u0438\u043a\u0435 \u0442\u0430\u043a\u043e\u0439 \u043f\u043e\u0434\u0445\u043e\u0434 \u043f\u0440\u0438\u0432\u043e\u0434\u0438\u0442 \u043a \u043d\u0435\u0443\u0431\u0435\u0434\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u043c\u0443, \u043f\u043b\u043e\u0441\u043a\u043e\u043c\u0443 \u0438 \u0438\u0441\u043a\u0443\u0441\u0441\u0442\u0432\u0435\u043d\u043d\u043e\u043c\u0443 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u0443.<\/p>\n<p>\u041f\u0440\u0438\u043c\u0435\u0440 \u0442\u043e\u0433\u043e, \u043a\u0430\u043a \u044d\u0442\u043e \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442, \u0435\u0441\u043b\u0438 \u043f\u0440\u043e\u0441\u0442\u043e &#171;\u0437\u0430\u043a\u0440\u0430\u0441\u0438\u0442\u044c&#187; \u043e\u0431\u043b\u0430\u0441\u0442\u044c:<\/p>\n<figure class=\"\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/bac\/8ae\/ba4\/bac8aeba40cc04f8dd7abee6582f82e9.png\" width=\"477\" height=\"202\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/bac\/8ae\/ba4\/bac8aeba40cc04f8dd7abee6582f82e9.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/bac\/8ae\/ba4\/bac8aeba40cc04f8dd7abee6582f82e9.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/figure>\n<p>\u0427\u0442\u043e\u0431\u044b \u0434\u043e\u0431\u0438\u0442\u044c\u0441\u044f \u043d\u0430\u0442\u0443\u0440\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u044d\u0444\u0444\u0435\u043a\u0442\u0430, \u044f \u0432\u043d\u0435\u0434\u0440\u0438\u043b\u0430 \u0430\u043b\u0433\u043e\u0440\u0438\u0442\u043c, \u043e\u0441\u043d\u043e\u0432\u0430\u043d\u043d\u044b\u0439 \u043d\u0430 \u0441\u043c\u0435\u0448\u0438\u0432\u0430\u043d\u0438\u0438 \u0440\u0435\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u0446\u0432\u0435\u0442\u0430 \u043e\u0431\u043b\u0430\u0441\u0442\u0438 \u0441 \u0436\u0435\u043b\u0430\u0435\u043c\u044b\u043c \u043e\u0442\u0442\u0435\u043d\u043a\u043e\u043c:<\/p>\n<ol>\n<li>\n<p>\u0421\u043d\u0430\u0447\u0430\u043b\u0430 \u044f \u043f\u043e\u043b\u0443\u0447\u0430\u044e \u0441\u0440\u0435\u0434\u043d\u0438\u0439 \u0446\u0432\u0435\u0442 \u043f\u0438\u043a\u0441\u0435\u043b\u0435\u0439 \u0432 \u043e\u0431\u043b\u0430\u0441\u0442\u0438, \u043a\u043e\u0442\u043e\u0440\u0443\u044e \u043d\u0443\u0436\u043d\u043e \u0437\u0430\u043a\u0440\u0430\u0441\u0438\u0442\u044c \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0444\u0443\u043d\u043a\u0446\u0438\u0438 <code>getAveragePixelsRgbColor()<\/code>. \u042d\u0442\u043e \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u043f\u043e\u043d\u044f\u0442\u044c, \u043a\u0430\u043a\u0438\u0435 \u0446\u0432\u0435\u0442\u0430 \u0443\u0436\u0435 \u043f\u0440\u0438\u0441\u0443\u0442\u0441\u0442\u0432\u0443\u044e\u0442 \u043d\u0430 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0438.<\/p>\n<\/li>\n<li>\n<p>\u0417\u0430\u0442\u0435\u043c \u044f \u0441\u043c\u0435\u0448\u0438\u0432\u0430\u044e \u0435\u0433\u043e \u0441 \u0436\u0435\u043b\u0430\u0435\u043c\u044b\u043c \u0446\u0432\u0435\u0442\u043e\u043c, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f \u043c\u0435\u0442\u043e\u0434 <code>getAverageColor()<\/code>. \u0422\u0430\u043a\u043e\u0439 \u043f\u043e\u0434\u0445\u043e\u0434 \u0441\u043e\u0437\u0434\u0430\u0451\u0442 \u044d\u0444\u0444\u0435\u043a\u0442 &#171;\u0442\u043e\u043d\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u043d\u0430\u043b\u043e\u0436\u0435\u043d\u0438\u044f&#187;, \u0430 \u043d\u0435 \u0437\u0430\u043c\u0435\u0449\u0435\u043d\u0438\u044f.<\/p>\n<\/li>\n<li>\n<p>\u0418\u043d\u043e\u0433\u0434\u0430 \u044f \u0442\u0430\u043a\u0436\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e <code>interpolateColors()<\/code>, \u0447\u0442\u043e\u0431\u044b \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0441\u0442\u0435\u043f\u0435\u043d\u044c \u0432\u043b\u0438\u044f\u043d\u0438\u044f \u043d\u043e\u0432\u043e\u0433\u043e \u0446\u0432\u0435\u0442\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b\u044c\u043d\u044b\u0439 \u2014 \u043e\u0442 \u043b\u0451\u0433\u043a\u043e\u0433\u043e \u043e\u0442\u0442\u0435\u043d\u043a\u0430 \u0434\u043e \u043d\u0430\u0441\u044b\u0449\u0435\u043d\u043d\u043e\u0433\u043e \u043e\u043a\u0440\u0430\u0448\u0438\u0432\u0430\u043d\u0438\u044f.<\/p>\n<\/li>\n<li>\n<p>\u0414\u043b\u044f \u043f\u0440\u0438\u0434\u0430\u043d\u0438\u044f \u0431\u0430\u0440\u0445\u0430\u0442\u0438\u0441\u0442\u043e\u0441\u0442\u0438 \u2014 \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0432 \u0442\u0435\u043d\u044f\u0445 \u0438\u043b\u0438 \u043c\u0430\u0442\u043e\u0432\u043e\u0439 \u043f\u043e\u043c\u0430\u0434\u0435 \u2014 \u044f \u043f\u0440\u0438\u043c\u0435\u043d\u044f\u044e <code>toMatteColor()<\/code>.<\/p>\n<\/li>\n<\/ol>\n<p>\u0412\u043e\u0442 \u043a\u0430\u043a \u0443\u0436\u0435 \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u043c\u0430\u0442\u043e\u0432\u0430\u044f \u043f\u043e\u043c\u0430\u0434\u0430 \u043f\u043e\u0441\u043b\u0435 \u0438\u043d\u0442\u0435\u0440\u043f\u043e\u043b\u044f\u0446\u0438\u0438 \u0446\u0432\u0435\u0442\u043e\u0432:<\/p>\n<figure class=\"\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/d61\/f2d\/dac\/d61f2ddac6b11771c425ed38e5c2a2ed.png\" width=\"477\" height=\"202\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/d61\/f2d\/dac\/d61f2ddac6b11771c425ed38e5c2a2ed.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/d61\/f2d\/dac\/d61f2ddac6b11771c425ed38e5c2a2ed.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/figure>\n<p><em>\u0412\u0410\u0416\u041d\u041e! \u041e\u0431\u0440\u0430\u0442\u0438\u0442\u0435 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435 \u043d\u0430 TODO \u0434\u043b\u044f \u044d\u0444\u0444\u0435\u043a\u0442\u043e\u0432. \u0423\u0431\u0435\u0434\u0438\u0442\u0435\u0441\u044c, \u0447\u0442\u043e \u0432\u044b \u0432\u0435\u0440\u043d\u043e \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u043b\u0438 \u043f\u0443\u0442\u0438!<\/em><\/p>\n<details class=\"spoiler\">\n<summary>BrowsColorEffect &#8212; \u043f\u0440\u0438\u043c\u0435\u0440 \u043d\u0430\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u044d\u0444\u0444\u0435\u043a\u0442\u0430 \u043d\u0430 \u0431\u0440\u043e\u0432\u0438<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"javascript\">import ColorUtility from \"..\/..\/Utility\/ColorUtility.js\"; import SafariUtility from \"..\/..\/Utility\/SafariUtility.js\"; import DrawUtility from \"..\/..\/Utility\/DrawUtility.js\";  \/**  * Apply brows color effect  * @type {{apply: BrowsColorEffect.apply}}  *\/ const BrowsColorEffect = (function () {      \/\/ Private Area     const defaults = {         transparency: 0.33,         blur: 3,         safariBlur: 1, \/\/hardcoded value don't change,     };      let maskCanvasElement = null; \/\/ will be used to make effect \"behind the scene\"     let maskCanvasContext = null; \/\/ keep 2D rendering context for the canvas      \/**      * Validate effect object      * @param obj      * @returns {boolean}      *\/     function isValidEffectSettings(obj) {         return ColorUtility.isHexColor(obj.value);     }      \/**      * Need to create an additional canvas which will be used to make effect \"behind the scene\"      *\/     function initMaskCanvas() {         if (maskCanvasElement == undefined || maskCanvasElement == null) {             maskCanvasElement = document.createElement('canvas');             maskCanvasContext = maskCanvasElement.getContext('2d');         }     }      return { \/\/ Public Area         \/**          * effect settings represents the following object:          * {          *  \"type\": \"color\",          *  \"value\": \"#0000\",          * }          *          * browsData represents the following object:          * {          *     \"rightBrowContourCoordinates\" : [{x: 000.22, y: 555}, .....],          *     \"leftBrowContourCoordinates\": [{x: 000.22, y: 555}, .....];          * }          *          * @param resultCanvasElement          * @param browsData          * @param effectSettings          *\/         apply: function (resultCanvasElement, browsData, effectSettings) {              if (!isValidEffectSettings(effectSettings)) {                 throw new Error('Invalid brows effect settings object.');             }              initMaskCanvas();              let resultCanvasContext = resultCanvasElement.getContext('2d');             let width = resultCanvasElement.width;             let height = resultCanvasElement.height;              \/\/ resize masked canvas aligned with source canvas             maskCanvasElement.width = width;             maskCanvasElement.height = height;              \/\/ uncomment if need to have supernatural effect and comment 2 lines below             \/\/ let averageBrowsColor = ColorUtility.getAveragePixelsRgbColor(             \/\/     browsData.leftBrowContourCoordinates.concat(browsData.rightBrowContourCoordinates),             \/\/     resultCanvasContext             \/\/ );             \/\/ let appliedColor = ColorUtility.getAverageColor(averageBrowsColor, rgbAppliedColor);              let rgbAppliedColor = ColorUtility.getRgbFromHex(effectSettings.value);             let appliedColor = rgbAppliedColor; \/\/ to have more bright effect              maskCanvasContext.clearRect(0, 0, width, height);              \/\/ Draw and fill brows contour             DrawUtility.drawContour(                 maskCanvasContext,                 browsData.rightBrowContourCoordinates,                 {fillStyle: `rgb(${appliedColor.r}, ${appliedColor.g}, ${appliedColor.b})`}             );             DrawUtility.drawContour(                 maskCanvasContext,                 browsData.leftBrowContourCoordinates,                 {fillStyle: `rgb(${appliedColor.r}, ${appliedColor.g}, ${appliedColor.b})`}             );              resultCanvasContext.globalAlpha = defaults.transparency;              if (SafariUtility.isSafari()) {                 SafariUtility.setCanvas(maskCanvasElement);                 SafariUtility.gBlur(defaults.safariBlur);             } else {                 resultCanvasContext.filter = `blur(${defaults.blur}px)`;             }              resultCanvasContext.drawImage(maskCanvasElement, 0, 0, width, height);              \/\/ Reset filters and restore global transparency             resultCanvasContext.filter = 'none';             resultCanvasContext.globalAlpha = 1.0;         }     }; })();  export default BrowsColorEffect; <\/code><\/pre>\n<\/div>\n<\/details>\n<p>\u0412 \u043a\u0430\u0436\u0434\u043e\u043c \u044d\u0444\u0444\u0435\u043a\u0442\u0435 \u044f \u043e\u0441\u0442\u0430\u0432\u0438\u043b\u0430 \u043a\u043e\u043c\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u0438 \u043e \u0442\u043e\u043c, \u043a\u0430\u043a\u043e\u0439 \u0446\u0432\u0435\u0442\/\u0440\u0430\u0437\u043c\u044b\u0442\u0438\u0435 \u0437\u0430 \u0447\u0442\u043e \u043e\u0442\u0432\u0435\u0447\u0430\u0435\u0442.<\/p>\n<p>\u0422\u0430\u043a\u0436\u0435 \u043f\u0440\u043e\u0448\u0443 \u0437\u0430\u043c\u0435\u0442\u0438\u0442\u044c, \u0447\u0442\u043e \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u044d\u0444\u0444\u0435\u043a\u0442\u044b, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0440\u0435\u0441\u043d\u0438\u0446\u044b, \u0442\u0440\u0435\u0431\u0443\u044e\u0442 \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0445 \u043c\u0430\u0441\u043e\u043a \/ \u043f\u043e\u0434\u043b\u043e\u0436\u0435\u043a. \u0422\u0430\u043a\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c, \u044f \u0441\u0434\u0435\u043b\u0430\u043b\u0430 \u043c\u0430\u0441\u043a\u0438 \u0434\u043b\u044f \u044d\u0444\u0444\u0435\u043a\u0442\u0430 \u0442\u0443\u0448\u0438, \u0430 \u0442\u0430\u043a\u0436\u0435 \u0434\u043b\u044f \u043a\u043e\u043d\u0441\u0438\u043b\u0435\u0440\u0430, \u044d\u0442\u043e \u043d\u0430\u0445\u043e\u0434\u044f\u0442\u0441\u044f \u0432 Effect\/Makeup\/assets.<\/p>\n<h2>\u041a\u0430\u043a \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u043c\u0430\u0441\u043a\u0443 \u0434\u043b\u044f \u043d\u0430\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u043e\u0447\u043a\u043e\u0432<\/h2>\n<p><a class=\"anchor\" name=\"glasses-mask\" id=\"glasses-mask\"><\/a><\/p>\n<p>\u0412 \u043c\u043e\u0435\u043c \u043c\u043e\u0434\u0443\u043b\u0435 \u044f \u043d\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e 3D \u043c\u043e\u0434\u0435\u043b\u0438. \u0420\u0430\u0431\u043e\u0442\u0430 \u0441 \u043e\u0447\u043a\u0430\u043c\u0438 \u0437\u0430\u043a\u043b\u044e\u0447\u0430\u0435\u0442\u0441\u044f \u0432 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0438 \u043c\u0430\u0441\u043a\u0438 2D, \u0430 \u0437\u0430\u0442\u0435\u043c \u0435\u0435 \u043f\u043e\u0437\u0438\u0446\u0438\u043e\u043d\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u043e\u0442\u043d\u043e\u0441\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u043b\u0438\u0446\u0430.<\/p>\n<p>\u0427\u0442\u043e\u0431\u044b \u043f\u0440\u0438\u043c\u0435\u043d\u0438\u0442\u044c \u043c\u0430\u0441\u043a\u0443 \u043e\u0447\u043a\u043e\u0432, \u0432\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u0435\u0451 \u0432 \u0444\u043e\u0440\u043c\u0430\u0442\u0435 <strong>PNG<\/strong> \u0441 <strong>\u043f\u0440\u043e\u0437\u0440\u0430\u0447\u043d\u044b\u043c \u0444\u043e\u043d\u043e\u043c<\/strong>, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f \u0441\u043f\u0435\u0446\u0438\u0430\u043b\u044c\u043d\u0443\u044e \u0431\u0430\u0437\u043e\u0432\u0443\u044e \u043c\u0430\u0441\u043a\u0443-\u043d\u0430\u043b\u043e\u0436\u0435\u043d\u0438\u0435.<\/p>\n<p>\u0412 \u043c\u043e\u0435\u0439 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0435 \u0432\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u043d\u0430\u0439\u0442\u0438 \u043f\u0440\u0438\u043c\u0435\u0440 \u0442\u0430\u043a\u043e\u0433\u043e PSD-\u0444\u0430\u0439\u043b\u0430 \u0437\u0434\u0435\u0441\u044c:<\/p>\n<ul>\n<li>\n<p>_documentation\/fixtures\/overlay.psd<\/p>\n<\/li>\n<\/ul>\n<p>\u041e\u0442\u043a\u0440\u043e\u0439\u0442\u0435 \u0444\u0430\u0439\u043b (\u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f Photoshop \u0438\u043b\u0438 GIMP).<\/p>\n<p>\u0412\u044b \u0443\u0432\u0438\u0434\u0438\u0442\u0435 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0441\u043b\u043e\u0435\u0432 \u0441 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f\u043c\u0438, \u0430 \u0442\u0430\u043a\u0436\u0435 \u043e\u0434\u0438\u043d \u0447\u0435\u0440\u043d\u044b\u0439 \u0441\u043b\u043e\u0439 \u0441 \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435\u043c <strong>&#171;Background-with-nose-center&#187;<\/strong> \u0438 \u0434\u0440\u0443\u0433\u043e\u0439 \u0441 \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435\u043c <strong>&#171;overlay&#187;<\/strong>:<\/p>\n<figure class=\"full-width\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/efd\/3d8\/599\/efd3d8599dd865997aaca3a51b053f9e.png\" width=\"1437\" height=\"716\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/efd\/3d8\/599\/efd3d8599dd865997aaca3a51b053f9e.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/efd\/3d8\/599\/efd3d8599dd865997aaca3a51b053f9e.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/figure>\n<p>\u0415\u0441\u043b\u0438 \u0432\u044b \u0445\u043e\u0442\u0438\u0442\u0435 \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u043d\u043e\u0432\u0443\u044e \u043c\u0430\u0441\u043a\u0443, \u0442\u043e <strong>\u043f\u0440\u0435\u0436\u0434\u0435 \u0432\u0441\u0435\u0433\u043e, \u0441\u043a\u0440\u043e\u0439\u0442\u0435 \u0432\u0441\u0435 \u0441\u043b\u043e\u0438, \u043a\u0440\u043e\u043c\u0435 &#171;overlay&#187;<\/strong>.<\/p>\n<figure class=\"full-width\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/a9a\/4fd\/315\/a9a4fd3154261a643a5c2f038e600324.png\" width=\"1369\" height=\"529\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/a9a\/4fd\/315\/a9a4fd3154261a643a5c2f038e600324.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/a9a\/4fd\/315\/a9a4fd3154261a643a5c2f038e600324.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/figure>\n<p>\u0421\u043a\u043e\u043f\u0438\u0440\u0443\u0439\u0442\u0435 \u0438 \u0432\u0441\u0442\u0430\u0432\u044c\u0442\u0435 \u043d\u043e\u0432\u043e\u0435 PNG-\u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435 \u043e\u0447\u043a\u043e\u0432 \u0441 <strong>\u043f\u0440\u043e\u0437\u0440\u0430\u0447\u043d\u044b\u043c \u0444\u043e\u043d\u043e\u043c<\/strong>. \u0418\u0437\u043c\u0435\u043d\u0438\u0442\u0435 \u0440\u0430\u0437\u043c\u0435\u0440 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u0432 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0438\u0438 \u0441 \u0431\u0430\u0437\u043e\u0432\u044b\u043c \u0441\u043b\u043e\u0435\u043c \u043c\u0430\u0441\u043a\u0438.<\/p>\n<figure class=\"full-width\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/a6d\/913\/8c9\/a6d9138c9145bb3a0594ff9f5af87da5.png\" width=\"1401\" height=\"499\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/a6d\/913\/8c9\/a6d9138c9145bb3a0594ff9f5af87da5.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/a6d\/913\/8c9\/a6d9138c9145bb3a0594ff9f5af87da5.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/figure>\n<p>\u0427\u0442\u043e\u0431\u044b \u0443\u0431\u0435\u0434\u0438\u0442\u044c\u0441\u044f, \u0447\u0442\u043e \u043e\u0447\u043a\u0438 \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u043e \u0440\u0430\u0441\u043f\u043e\u043b\u043e\u0436\u0435\u043d\u044b \u043f\u043e \u0446\u0435\u043d\u0442\u0440\u0443 \u043d\u043e\u0441\u0430, <strong>\u0432\u043a\u043b\u044e\u0447\u0438\u0442\u0435 \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0438\u0439 \u0441\u043b\u043e\u0439<\/strong> \u0438 <strong>\u0441\u043e\u0432\u043c\u0435\u0441\u0442\u0438\u0442\u0435 \u0446\u0435\u043d\u0442\u0440 \u043e\u0447\u043a\u043e\u0432 \u0441 \u0431\u0435\u043b\u043e\u0439 \u0442\u043e\u0447\u043a\u043e\u0439<\/strong> \u043d\u0430 \u044d\u0442\u043e\u043c \u0441\u043b\u043e\u0435.<\/p>\n<figure class=\"full-width\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/8bb\/887\/06d\/8bb88706d5cb58f0a868ff6243d51fce.png\" width=\"1312\" height=\"595\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/8bb\/887\/06d\/8bb88706d5cb58f0a868ff6243d51fce.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/8bb\/887\/06d\/8bb88706d5cb58f0a868ff6243d51fce.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/figure>\n<figure class=\"full-width\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/419\/622\/8bc\/4196228bc99630042a3c245bca129ae9.png\" width=\"562\" height=\"479\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/419\/622\/8bc\/4196228bc99630042a3c245bca129ae9.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/419\/622\/8bc\/4196228bc99630042a3c245bca129ae9.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/figure>\n<p>\u041a\u043e\u0433\u0434\u0430 \u043f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0431\u0443\u0434\u0435\u0442 \u043e\u043f\u0442\u0438\u043c\u0430\u043b\u044c\u043d\u044b\u043c, <strong>\u0441\u043a\u0440\u043e\u0439\u0442\u0435 \u0432\u0441\u0435 \u0441\u043b\u043e\u0438, \u043a\u0440\u043e\u043c\u0435 \u0441\u0430\u043c\u0438\u0445 \u043e\u0447\u043a\u043e\u0432<\/strong>.<\/p>\n<figure class=\"full-width\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/b35\/530\/e36\/b35530e365b0dcc0e69803c7636c8e6a.png\" width=\"1476\" height=\"773\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/b35\/530\/e36\/b35530e365b0dcc0e69803c7636c8e6a.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/b35\/530\/e36\/b35530e365b0dcc0e69803c7636c8e6a.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/figure>\n<p>\u0417\u0430\u0442\u0435\u043c <strong>\u044d\u043a\u0441\u043f\u043e\u0440\u0442\u0438\u0440\u0443\u0439\u0442\u0435 \u044d\u0442\u043e \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435 \u0432 \u0444\u043e\u0440\u043c\u0430\u0442\u0435 PNG<\/strong>.<\/p>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435 \u0433\u043e\u0442\u043e\u0432\u043e \u043a \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044e \u0432 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u043c\u0430\u0441\u043a\u0438 \u0434\u043b\u044f \u043e\u0447\u043a\u043e\u0432!<\/p>\n<h2>\u041a\u0430\u043a \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0443<\/h2>\n<p><a class=\"anchor\" name=\"include\" id=\"include\"><\/a><\/p>\n<p>\u041f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u0435 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b\u0439 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u043e\u0440 (FaceMesh\/VisionTask). \u041f\u0440\u043e\u0446\u0435\u0441\u0441\u043e\u0440 \u0431\u0443\u0434\u0435\u0442 \u043e\u0431\u0451\u0440\u043d\u0443\u0442 \u0432 \u043f\u0443\u0431\u043b\u0438\u0447\u043d\u044b\u0439 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 VirtualMirror. \u041e\u0431\u044a\u0435\u043a\u0442 VirtualMirror \u044d\u043a\u0441\u043f\u043e\u0440\u0442\u0438\u0440\u0443\u0435\u0442\u0441\u044f \u0433\u043b\u043e\u0431\u0430\u043b\u044c\u043d\u043e \u0447\u0435\u0440\u0435\u0437 window, \u0447\u0442\u043e\u0431\u044b \u0431\u044b\u0442\u044c \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u044b\u043c \u0432 \u043b\u044e\u0431\u043e\u043c \u043c\u0435\u0441\u0442\u0435 \u0444\u0440\u043e\u043d\u0442\u0435\u043d\u0434\u0430 \u0431\u0435\u0437 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e\u0441\u0442\u0438 \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0445 \u0438\u043c\u043f\u043e\u0440\u0442\u043e\u0432.<\/p>\n<pre><code class=\"javascript\">import FaceMeshProcessor from '.\/Lib\/Mediapipe\/face_mesh\/FaceMeshProcessor.js';  const VirtualMirror = (function () {          return { \/\/ Public Area             apply: function (sourceElementId, resultCanvasElementId, effectObject) {                 let element = document.getElementById(sourceElementId);                 let resultCanvasHTMLObject = document.getElementById(resultCanvasElementId);                  FaceMeshProcessor.process(element, resultCanvasHTMLObject, effectObject);             },              terminate: function () {                 FaceMeshProcessor.terminate();             }          };     } )();  Object.defineProperty(window, 'VirtualMirror', {     value: VirtualMirror,     writable: false,     configurable: false });  export default VirtualMirror; <\/code><\/pre>\n<p>\u0414\u0430\u043b\u0435\u0435 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u043f\u0435\u0440\u0435\u0434\u0430\u0447\u0443 \u0432\u044b\u0431\u0440\u0430\u043d\u043d\u044b\u0445 effectSettings \u0447\u0435\u0440\u0435\u0437 UI, \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c html \u0434\u043b\u044f \u043a\u043e\u0440\u0440\u0435\u043a\u0442\u043d\u043e\u0439 \u0432\u0437\u0430\u0438\u043c\u043e\u0441\u0432\u044f\u0437\u0438 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0430 \u0438 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438. \u041d\u0438\u0436\u0435 \u043f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u044e, \u043a\u0430\u043a \u044d\u0442\u043e \u0441\u0434\u0435\u043b\u0430\u043d\u043e \u0443 \u043c\u0435\u043d\u044f.<\/p>\n<details class=\"spoiler\">\n<summary>\u041f\u0440\u0438\u043c\u0435\u0440 \u043a\u043e\u0434\u0430<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"javascript\">&lt;!DOCTYPE html&gt; &lt;html lang=\"en\"&gt; &lt;head&gt;     &lt;meta charset=\"UTF-8\"&gt;     &lt;meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\"&gt;     &lt;title&gt;Virtual Mirror Library | Virtual Makeup Try-On&lt;\/title&gt;     &lt;script type=\"module\" src=\"main.js\"&gt;&lt;\/script&gt;     &lt;link href=\"https:\/\/fonts.googleapis.com\/css2?family=Roboto:wght@300;400;500;700&amp;display=swap\" rel=\"stylesheet\"&gt;     &lt;style&gt;         body {             font-family: 'Roboto', sans-serif;             display: flex;             flex-direction: column;             align-items: center;             background: #f8f9fa;             margin: 0;             padding: 0;         }         header {             width: 100%;             padding: 20px;             background-color: #343a40;             color: #ffffff;             text-align: center;             box-shadow: 0 4px 6px rgba(0,0,0,0.1);         }         header h1 {             margin: 0;             font-size: 2em;             font-weight: 500;         }         header p {             margin: 5px 0 0;             font-size: 1.2em;             font-weight: 300;         }         .container {             display: flex;             flex-wrap: wrap;             justify-content: center;             width: 100%;             max-width: 1200px;             margin-top: 20px;         }         .column {             box-shadow: 0 4px 6px rgba(0,0,0,0.1);             background: #ffffff;             border-radius: 8px;             padding: 20px;             margin: 10px;             flex: 1;             min-width: 250px;         }         #effectsColumn, #settingsColumn {             max-width: 300px;         }         #modeColumn {             flex: 2;             text-align: center;         }         h2 {             font-size: 1.5em;             margin-bottom: 10px;             color: #495057;         }         label {             font-size: 1.1em;             color: #212529;         }         input[type=\"radio\"], select {             margin-right: 10px;         }         #app {             margin-top: 20px;             position: relative;             display: inline-block;         }         img {             width: 100%;             max-width: 700px;             height: auto;             border: 2px solid #dee2e6;             border-radius: 8px;         }         video {             display: none;             width: 100%;             max-width: 700px;             height: auto;             border: 2px solid #dee2e6;             border-radius: 8px;         }         #mirrorCanvas {             position: absolute;             top: 0;             left: 0;             z-index: 1;         }     &lt;\/style&gt; &lt;\/head&gt; &lt;body&gt;  &lt;header&gt;     &lt;h1&gt;Virtual Mirror: Discover Visual E-commerce&lt;\/h1&gt;     &lt;p&gt;Bring an interactive shopping experience to your customers and set your brand apart.&lt;\/p&gt; &lt;\/header&gt;  &lt;div class=\"container\"&gt;     &lt;div class=\"column\" id=\"effectsColumn\"&gt;         &lt;h2&gt;Lips:&lt;\/h2&gt;         &lt;input type=\"radio\" id=\"lipGloss\" name=\"effect\" value=\"LipGloss\" valueType=\"color\"&gt;         &lt;label for=\"lipGloss\"&gt;Lip Gloss&lt;\/label&gt;&lt;br&gt;          &lt;input type=\"radio\" id=\"lipLiner\" name=\"effect\" value=\"LipLiner\" valueType=\"color\"&gt;         &lt;label for=\"lipLiner\"&gt;Lip Liner&lt;\/label&gt;&lt;br&gt;          &lt;input type=\"radio\" id=\"lipstick\" name=\"effect\" value=\"Lipstick\" valueType=\"color\"&gt;         &lt;label for=\"lipstick\"&gt;Lipstick&lt;\/label&gt;&lt;br&gt;          &lt;input type=\"radio\" id=\"lipstickShimmer\" name=\"effect\" value=\"LipstickShimmer\" valueType=\"color\"&gt;         &lt;label for=\"lipstickShimmer\"&gt;Lipstick Shimmer&lt;\/label&gt;&lt;br&gt;          &lt;input type=\"radio\" id=\"matteLipstick\" name=\"effect\" value=\"MatteLipstick\" valueType=\"color\"&gt;         &lt;label for=\"matteLipstick\"&gt;Matte Lipstick&lt;\/label&gt;&lt;br&gt;          &lt;h2&gt;Eyes:&lt;\/h2&gt;         &lt;input type=\"radio\" id=\"browsColor\" name=\"effect\" value=\"BrowsColor\" valueType=\"color\"&gt;         &lt;label for=\"browsColor\"&gt;Brows Color&lt;\/label&gt;&lt;br&gt;          &lt;input type=\"radio\" id=\"eyeliner\" name=\"effect\" value=\"Eyeliner\" valueType=\"color\"&gt;         &lt;label for=\"eyeliner\"&gt;Eyeliner&lt;\/label&gt;&lt;br&gt;          &lt;input type=\"radio\" id=\"mascara\" name=\"effect\" value=\"Mascara\" valueType=\"color\"&gt;         &lt;label for=\"mascara\"&gt;Mascara&lt;\/label&gt;&lt;br&gt;          &lt;input type=\"radio\" id=\"kajal\" name=\"effect\" value=\"Kajal\" valueType=\"color\"&gt;         &lt;label for=\"kajal\"&gt;Kajal&lt;\/label&gt;&lt;br&gt;          &lt;input type=\"radio\" id=\"eyeshadowsatin\" name=\"effect\" value=\"EyeShadowSatin+\" valueType=\"color\"&gt;         &lt;label for=\"eyeshadowsatin\"&gt;EyeShadow Satin&lt;\/label&gt;&lt;br&gt;          &lt;input type=\"radio\" id=\"eyeshadowmatte\" name=\"effect\" value=\"EyeShadowMatte\" valueType=\"color\"&gt;         &lt;label for=\"eyeshadowmatte\"&gt;EyeShadow Matte&lt;\/label&gt;&lt;br&gt;          &lt;input type=\"radio\" id=\"eyeshadowshimmer\" name=\"effect\" value=\"EyeShadowShimmer\" valueType=\"color\"&gt;         &lt;label for=\"eyeshadowshimmer\"&gt;EyeShadow Shimmer&lt;\/label&gt;&lt;br&gt;          &lt;h2&gt;Face:&lt;\/h2&gt;         &lt;input type=\"radio\" id=\"foundationSatin\" name=\"effect\" value=\"FoundationSatin\" valueType=\"color\"&gt;         &lt;label for=\"foundationSatin\"&gt;Foundation Satin&lt;\/label&gt;&lt;br&gt;          &lt;input type=\"radio\" id=\"foundationMatte\" name=\"effect\" value=\"FoundationMatte\" valueType=\"color\"&gt;         &lt;label for=\"foundationMatte\"&gt;Foundation Matte&lt;\/label&gt;&lt;br&gt;          &lt;input type=\"radio\" id=\"concealer\" name=\"effect\" value=\"Concealer\" valueType=\"color\"&gt;         &lt;label for=\"concealer\"&gt;Concealer&lt;\/label&gt;&lt;br&gt;          &lt;input type=\"radio\" id=\"contour\" name=\"effect\" value=\"Contour\" valueType=\"color\"&gt;         &lt;label for=\"contour\"&gt;Contour\/Bronzer&lt;\/label&gt;&lt;br&gt;          &lt;h2&gt;Accessories:&lt;\/h2&gt;         &lt;input type=\"radio\" id=\"eyeglasses\" name=\"effect\" value=\"Eyeglasses\" valueType=\"image\"&gt;         &lt;label for=\"eyeglasses\"&gt;Eyeglasses&lt;\/label&gt;&lt;br&gt;     &lt;\/div&gt;      &lt;div class=\"column\" id=\"settingsColumn\"&gt;         &lt;div id=\"effectControls\"&gt;             &lt;div id=\"colorControl\" style=\"display: none;\"&gt;                 &lt;label for=\"colorPicker\"&gt;Choose Color:&lt;\/label&gt;&lt;br&gt;                 &lt;input type=\"color\" id=\"colorPicker\" name=\"colorPicker\"&gt;             &lt;\/div&gt;              &lt;div id=\"rangeTransparency\" style=\"display: none;\"&gt;                 &lt;label for=\"transparency\"&gt;Transparency&lt;\/label&gt;&lt;br&gt;                 &lt;input type=\"range\" id=\"transparency\" name=\"transparency\"&gt;             &lt;\/div&gt;              &lt;div id=\"rangeSaturation\" style=\"display: none;\"&gt;                 &lt;label for=\"saturation\"&gt;Saturation&lt;\/label&gt;&lt;br&gt;                 &lt;input type=\"range\" id=\"saturation\" name=\"saturation\"&gt;             &lt;\/div&gt;         &lt;\/div&gt;     &lt;\/div&gt;      &lt;div class=\"column\" id=\"modeColumn\"&gt;         &lt;div&gt;             &lt;label for=\"modeSelect\"&gt;Select Mode:&lt;\/label&gt;             &lt;select id=\"modeSelect\"&gt;                 &lt;option value=\"image\" selected&gt;ModeImage&lt;\/option&gt;                 &lt;option value=\"video\"&gt;ModeVideo&lt;\/option&gt;             &lt;\/select&gt;         &lt;\/div&gt;          &lt;div id=\"app\"&gt;             &lt;canvas id=\"mirrorCanvas\" style=\"display:none\"&gt;&lt;\/canvas&gt;             &lt;video id=\"mirrorVideo\" height=\"auto\" playsinline=\"\" autoplay=\"\" muted=\"\" width=\"700px\" style=\"background: black\"&gt;&lt;\/video&gt;             &lt;img id=\"mirrorImg\" src=\"face.png\"\/&gt;         &lt;\/div&gt;     &lt;\/div&gt; &lt;\/div&gt;  &lt;script&gt;      const constraints = {         video: true     };      let selectedEffectName = null;     let stream = null;     let video = null;      \/**      * Set appropriate values from constants      *\/     function setRadioValues() {         document.getElementById(\"browsColor\").value = window.EFFECT_BROWS_COLOR;         document.getElementById(\"lipstick\").value = window.EFFECT_LIPSTICK;         document.getElementById(\"matteLipstick\").value = window.EFFECT_MATTE_LIPSTICK;         document.getElementById(\"eyeliner\").value = window.EFFECT_EYELINER;         document.getElementById(\"eyeglasses\").value = window.EFFECT_EYEGLASSES;         document.getElementById(\"lipLiner\").value = window.EFFECT_LIPLINER;         document.getElementById(\"lipGloss\").value = window.EFFECT_LIP_GLOSS;         document.getElementById(\"lipstickShimmer\").value = window.EFFECT_LIPSTICK_SHIMMER;         document.getElementById(\"kajal\").value = window.EFFECT_KAJAL;         document.getElementById(\"mascara\").value = window.EFFECT_MASCARA;         document.getElementById(\"foundationSatin\").value = window.EFFECT_FOUNDATION_SATIN;         document.getElementById(\"foundationMatte\").value = window.EFFECT_FOUNDATION_MATTE;         document.getElementById(\"contour\").value = window.EFFECT_CONTOUR;         document.getElementById(\"eyeshadowsatin\").value = window.EFFECT_EYESHADOW_SATIN;         document.getElementById(\"eyeshadowmatte\").value = window.EFFECT_EYESHADOW_MATTE;         document.getElementById(\"eyeshadowshimmer\").value = window.EFFECT_EYESHADOW_SHIMMER;     }      \/**      * Init camera      * @returns {Promise&lt;void&gt;}      *\/     async function initNavigatorMedia() {         if (stream) {             return;         }          if (navigator.mediaDevices &amp;&amp; navigator.mediaDevices.getUserMedia) {             stream = await navigator.mediaDevices.getUserMedia(constraints);             video = document.getElementById(\"mirrorVideo\");             video.srcObject = stream;             window.stream = stream;         } else {             throw new Error(\"getUserMedia() method is not supported by this browser\");         }     }      \/**      * Stop Camera      * @returns {Promise&lt;void&gt;}      *\/     async function stopVideo() {         if (stream) {             stream.getTracks().forEach(track =&gt; track.stop());             stream = null;         }     }      \/**      * Show additional params for effect settings      * like range bars and so on      * @param effectName      * @param effectType      *\/     function showEffectControls(effectName, effectType) {         const colorControlDiv = document.getElementById(\"colorControl\");         const transparencyControlDiv = document.getElementById(\"rangeTransparency\");         const saturationControlDiv = document.getElementById(\"rangeSaturation\");          const transparencyRange = document.getElementById(\"transparency\");         const saturationRange = document.getElementById(\"saturation\");          \/\/ Hide all controls initially         colorControlDiv.style.display = \"none\";         transparencyControlDiv.style.display = \"none\";         saturationControlDiv.style.display = \"none\";          if (effectType === 'color') {             document.getElementById(\"colorControl\").style.display = \"block\";         } else {             document.getElementById(\"colorControl\").style.display = \"none\";         }          switch (effectName) {             case window.EFFECT_LIP_GLOSS:                 transparencyControlDiv.style.display = \"block\";                 transparencyRange.min = 0.15;                 transparencyRange.max = 0.7;                 transparencyRange.step = 0.01;                 transparencyRange.value = 0.15; \/\/ Set default value                 break;              case window.EFFECT_LIPSTICK:                 saturationControlDiv.style.display = \"block\";                 saturationRange.min = 0;                 saturationRange.max = 1;                 saturationRange.step = 0.1;                 saturationRange.value = 0; \/\/ Set default value                  transparencyControlDiv.style.display = \"block\";                 transparencyRange.min = 0.15;                 transparencyRange.max = 0.3;                 transparencyRange.step = 0.01;                 transparencyRange.value = 0.15; \/\/ Set default value                 break;              case window.EFFECT_LIPSTICK_SHIMMER:                 transparencyControlDiv.style.display = \"block\";                 transparencyRange.min = 0.15;                 transparencyRange.max = 0.5;                 transparencyRange.step = 0.01;                 transparencyRange.value = 0.15; \/\/ Set default value                 break;         }     }      \/**      * Apply effect      * @returns {Promise&lt;void&gt;}      *\/     async function applyEffect() {          if (window.VirtualMirror) {             await window.VirtualMirror.terminate();         }          const effectRadio = document.querySelector('input[name=\"effect\"]:checked');          if (!effectRadio) {             return;         }          const mode = document.getElementById(\"modeSelect\").value;         const effectName = effectRadio.value;         const valueType = effectRadio.getAttribute('valueType');         const colorPicker = document.getElementById(\"colorPicker\");         const mirrorCanvas = document.getElementById('mirrorCanvas');         const sourceImage = document.getElementById('mirrorImg');         const sourceVideo = document.getElementById('mirrorVideo');          if (mode === 'video') {             await initNavigatorMedia();             mirrorCanvas.style.display = 'block';             sourceVideo.style.display = 'block';             sourceImage.style.display = 'none';         } else {             await stopVideo();             mirrorCanvas.style.display = 'block';             sourceImage.style.display = 'block';             sourceVideo.style.display = 'none';         }          if (selectedEffectName !== effectName) {             selectedEffectName = effectName;             showEffectControls(effectName, valueType);         }          const effectObject = {             \"effect\": effectName,             \"type\": valueType         };           if (valueType === 'color') {             effectObject.value = colorPicker.value;         }          if (valueType === \"image\") {             effectObject.value = \"https:\/\/your_domain.com\/mirror\/glasses.png\"; \/\/TODO         }          \/\/ Depending on the effect type, update value for range inputs         if (effectName === window.EFFECT_LIP_GLOSS || effectName === window.EFFECT_LIPSTICK_SHIMMER || effectName === window.EFFECT_LIPSTICK) {             effectObject.transparency = document.getElementById(\"transparency\").value;         }         if (effectName === window.EFFECT_LIPLINER || effectName === window.EFFECT_LIPSTICK || effectName === window.EFFECT_HAIR_COLOR) {             effectObject.saturation = document.getElementById(\"saturation\").value;         }          window.VirtualMirror.apply(mode === 'video' ? \"mirrorVideo\" : \"mirrorImg\", \"mirrorCanvas\", effectObject);     }       document.addEventListener('DOMContentLoaded', function () {         setRadioValues(); \/\/ init values for radio buttons          document.querySelectorAll('input[type=radio], select').forEach(item =&gt; {             item.addEventListener('change', applyEffect);         });          document.querySelectorAll('input[type=range]').forEach(item =&gt; {             item.addEventListener('input', applyEffect);         });          \/\/ Listen for the color picker change         document.getElementById(\"colorPicker\").addEventListener('input', applyEffect);     }); &lt;\/script&gt; &lt;\/body&gt; &lt;\/html&gt;<\/code><\/pre>\n<\/div>\n<\/details>\n<h2>\u0417\u0430\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435<\/h2>\n<p><a class=\"anchor\" name=\"sum-up\" id=\"sum-up\"><\/a><\/p>\n<p>\u041f\u043e\u0441\u043b\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438, \u0432\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u043d\u0430\u043a\u043b\u0430\u0434\u044b\u0432\u0430\u0442\u044c \u044d\u0444\u0444\u0435\u043a\u0442\u044b<\/p>\n<figure class=\"full-width\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/e80\/7ef\/d26\/e807efd266c22e7d07c4a304332a3015.gif\" width=\"680\" height=\"396\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/e80\/7ef\/d26\/e807efd266c22e7d07c4a304332a3015.gif 780w,&#10;       https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/e80\/7ef\/d26\/e807efd266c22e7d07c4a304332a3015.gif 781w\" loading=\"lazy\" decode=\"async\"\/><\/figure>\n<p>\u0415\u0441\u043b\u0438 \u0432\u044b \u0434\u043e\u0447\u0438\u0442\u0430\u043b\u0438 \u0434\u043e \u044d\u0442\u043e\u0433\u043e \u043c\u043e\u043c\u0435\u043d\u0442\u0430 &#8212; \u0431\u043e\u043b\u044c\u0448\u043e\u0435 \u0441\u043f\u0430\u0441\u0438\u0431\u043e \u0437\u0430 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435! \u041d\u0430\u0434\u0435\u044e\u0441\u044c, \u0447\u0442\u043e \u043c\u043e\u044f \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u0432\u0430\u043c \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u043e\u0439. \u0415\u0441\u043b\u0438 \u0432\u044b \u0437\u043d\u0430\u0435\u0442\u0435, \u043a\u0430\u043a \u043c\u043e\u0436\u043d\u043e \u0443\u043b\u0443\u0447\u0448\u0438\u0442\u044c \u043a\u043e\u0434 \u0438\u043b\u0438 \u044d\u0444\u0444\u0435\u043a\u0442\u044b &#8212; \u0431\u0443\u0434\u0443 \u0440\u0430\u0434\u0430 \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c \u0432 \u043a\u043e\u043c\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u044f\u0445.<\/p>\n<p>\u041f\u0440\u043e\u0434\u0443\u0431\u043b\u0438\u0440\u0443\u044e<a href=\"https:\/\/github.com\/annysmolyan\/virtual-mirror-library\" rel=\"noopener noreferrer nofollow\"> \u0441\u0441\u044b\u043b\u043a\u0443 \u043d\u0430 GitHub<\/a><\/p>\n<\/div>\n<\/div>\n<\/div>\n<p><!----><!----><\/div>\n<p><!----><!----><br \/> \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/articles\/916332\/\"> https:\/\/habr.com\/ru\/articles\/916332\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<p>\u041f\u0440\u0438\u0432\u0435\u0442! \u042f \u0410\u043d\u044f, \u0438 \u043e\u0447\u0435\u043d\u044c \u043b\u044e\u0431\u043b\u044e \u043f\u0438\u0441\u0430\u0442\u044c \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u044b\u0435 \u0438\u043d\u0442\u0435\u0440\u0435\u043d\u043e\u0441\u0442\u0438 \u043f\u043e\u0434 E-commerce.<\/p>\n<p>\u0420\u0430\u043d\u0435\u0435 \u044f \u0443\u0436\u0435 \u043f\u0438\u0441\u0430\u043b\u0430 \u043e \u0442\u043e\u043c, \u043a\u0430\u043a \u0441\u043e\u0437\u0434\u0430\u043b\u0430 POC \u043c\u043e\u0434\u0443\u043b\u044f <a href=\"https:\/\/habr.com\/ru\/articles\/825622\/\" rel=\"noopener noreferrer nofollow\">\u0432\u0438\u0437\u0443\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u043f\u043e\u0438\u0441\u043a\u0430<\/a>, \u0441\u0435\u0433\u043e\u0434\u043d\u044f \u0445\u043e\u0447\u0443 \u043f\u043e\u0434\u0435\u043b\u0438\u0442\u044c\u0441\u044f \u0441\u0432\u043e\u0435\u0439 \u043d\u0430\u0440\u0430\u0431\u043e\u0442\u043a\u043e\u0439 \u0432\u0438\u0440\u0442\u0443\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u0437\u0435\u0440\u043a\u0430\u043b\u0430.<\/p>\n<p>\u0411\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0443 \u043d\u0430\u043f\u0438\u0441\u0430\u043b\u0430 \u0435\u0449\u0435 \u0433\u043e\u0434-\u043f\u043e\u043b\u0442\u043e\u0440\u0430 \u043d\u0430\u0437\u0430\u0434, \u043d\u0430 \u0442\u043e \u0432\u0440\u0435\u043c\u044f \u0431\u044b\u043b\u043e \u043c\u0430\u043b\u043e \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438 \u043d\u0430 \u044d\u0442\u0443 \u0442\u0435\u043c\u0443, \u043d\u043e \u0437\u0430\u0442\u043e \u0431\u043e\u043b\u044c\u0448\u043e\u0435 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u043f\u0440\u0435\u0434\u043b\u043e\u0436\u0435\u043d\u0438\u0439 \u043e \u043f\u043e\u043a\u0443\u043f\u043a\u0435 \u0433\u043e\u0442\u043e\u0432\u044b\u0445 \u043c\u043e\u0434\u0443\u043b\u0435\u0439. \u041c\u043d\u0435, \u043a\u0430\u043a \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0443, \u0441\u0442\u0430\u043b\u043e \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u043e, \u0430 \u043a\u0430\u043a \u0436\u0435 \u044d\u0442\u043e \u0432\u0441\u0435 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442, \u0438 \u043d\u0430\u0447\u0430\u043b\u0430 \u043f\u043e\u0433\u0440\u0443\u0436\u0430\u0442\u044c\u0441\u044f \u0434\u0435\u0442\u0430\u043b\u044c\u043d\u0435\u0435 \u0432 \u044d\u0442\u0443 \u0442\u0435\u043c\u0443.<\/p>\n<p>\u0414\u043b\u044f \u043d\u0435\u0442\u0435\u0440\u043f\u0435\u043b\u0438\u0432\u044b\u0445 &#8212; <a href=\"https:\/\/github.com\/annysmolyan\/virtual-mirror-library\" rel=\"noopener noreferrer nofollow\">\u0432\u043e\u0442 \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 Github<\/a><\/p>\n<details class=\"spoiler\">\n<summary>\u0412\u0410\u0416\u041d\u041e! \u041f\u0420\u041e\u0427\u0418\u0422\u0410\u0422\u042c \u041f\u0415\u0420\u0415\u0414 \u0414\u0410\u041b\u042c\u041d\u0415\u0419\u0428\u0418\u041c \u0427\u0422\u0415\u041d\u0418\u0415\u041c \u0421\u0422\u0410\u0422\u042c\u0418!!!!<\/summary>\n<div class=\"spoiler__content\">\n<p>1) \u041c\u043e\u0439 \u043a\u043e\u0434 \u043d\u0435 \u043f\u0440\u0435\u0442\u0435\u043d\u0434\u0443\u0435\u0442 \u043d\u0430 \u0438\u0434\u0435\u0430\u043b\u044c\u043d\u044b\u0439.<\/p>\n<p>2) \u0411\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430 <strong>\u043d\u0435 \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f<\/strong> \u0430\u0431\u0441\u043e\u043b\u044e\u0442\u043d\u043e \u0433\u043e\u0442\u043e\u0432\u044b\u043c \u043f\u0440\u043e\u0434\u0443\u043a\u0442\u043e\u043c, \u043c\u043e\u0436\u0435\u0442 \u0438\u043c\u0435\u0442\u044c \u043e\u0448\u0438\u0431\u043a\u0438 \u0432 \u0440\u0430\u0431\u043e\u0442\u0435, \u043d\u0435 \u0438\u0434\u0435\u0430\u043b\u044c\u043d\u044b\u0439 \u043d\u0430\u043a\u043b\u0430\u0434\u044b\u0432\u0430\u0435\u043c\u044b\u0439 \u044d\u0444\u0444\u0435\u043a\u0442, \u0430 \u0442\u0430\u043a \u0436\u0435 \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043d\u044e\u0430\u0441\u044b \u0432 Safari. <s>\u041a\u043e\u043c\u0443 \u043d\u0443\u0436\u0435\u043d \u0438\u0434\u0435\u0430\u043b\u044c\u043d\u043e \u0440\u0430\u0431\u043e\u0442\u0430\u044e\u0449\u0438\u0439 \u043f\u0440\u043e\u0434\u0443\u043a\u0442 &#8212; \u043c\u043e\u0436\u043d\u043e \u043a\u0443\u043f\u0438\u0442\u044c \u0437\u0430 \u043c\u043d\u043e\u0433\u043e \u0434\u0435\u043d\u0435\u0433 \ud83d\ude42<\/s><\/p>\n<p>3) \u042f \u0431\u0443\u0434\u0443 \u0440\u0430\u0434\u0430 \u043f\u043e\u0447\u0438\u0442\u0430\u0442\u044c \u0432\u0430\u0448\u0438\u00a0<strong>\u043a\u043e\u043d\u0441\u0442\u0440\u0443\u043a\u0442\u0438\u0432\u043d\u044b\u0435<\/strong>\u00a0\u0438\u0434\u0435\u0438\/\u0440\u0435\u043a\u043e\u043c\u0435\u043d\u0434\u0430\u0446\u0438\u0438\/\u043f\u0440\u0435\u0434\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0432 \u043a\u043e\u043c\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u044f\u0445.<\/p>\n<p>4) \u041a\u043e\u0434 \u043c\u043e\u0436\u0435\u0442 \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0442\u044c \u043f\u043e\u043c\u0435\u0442\u043a\u0438 \u0441 TODO &#8212; \u044d\u0442\u043e \u043d\u043e\u0440\u043c\u0430\u043b\u044c\u043d\u043e.<\/p>\n<\/div>\n<\/details>\n<h3>\u0421\u043e\u0434\u0435\u0440\u0436\u0430\u043d\u0438\u0435 \u0441\u0442\u0430\u0442\u044c\u0438<\/h3>\n<ol>\n<li>\n<p><a href=\"#intro\" rel=\"noopener noreferrer nofollow\">\u0412\u0432\u0435\u0434\u0435\u043d\u0438\u0435<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#constants\" rel=\"noopener noreferrer nofollow\">\u0421\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0430 \u043f\u0440\u043e\u0435\u043a\u0442\u0430: Constants<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#lib\" rel=\"noopener noreferrer nofollow\">\u0421\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0430 \u043f\u0440\u043e\u0435\u043a\u0442\u0430: Lib &#8212; The Core of The Library<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#utility\" rel=\"noopener noreferrer nofollow\">\u0421\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0430 \u043f\u0440\u043e\u0435\u043a\u0442\u0430: Utility<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#effect\" rel=\"noopener noreferrer nofollow\">\u0421\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0430 \u043f\u0440\u043e\u0435\u043a\u0442\u0430: Effect<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#glasses-mask\" rel=\"noopener noreferrer nofollow\">\u041a\u0430\u043a \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u043c\u0430\u0441\u043a\u0443 \u0434\u043b\u044f \u043d\u0430\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u043e\u0447\u043a\u043e\u0432<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#include\" rel=\"noopener noreferrer nofollow\">\u041a\u0430\u043a \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0443<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#sum-up\" rel=\"noopener noreferrer nofollow\">\u0417\u0430\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435<\/a><\/p>\n<\/li>\n<\/ol>\n<h2>\u0412\u0432\u0435\u0434\u0435\u043d\u0438\u0435:<\/h2>\n<p><a class=\"anchor\" name=\"intro\" id=\"intro\"><\/a><\/p>\n<p>\u042d\u0442\u0430 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f\u043c \u043f\u0440\u0438\u043c\u0435\u0440\u044f\u0442\u044c \u0440\u0430\u0437\u043b\u0438\u0447\u043d\u0443\u044e \u043a\u043e\u0441\u043c\u0435\u0442\u0438\u043a\u0443 \u0438 \u0430\u043a\u0441\u0435\u0441\u0441\u0443\u0430\u0440\u044b, \u0442\u0430\u043a \u0436\u0435, \u043a\u0430\u043a \u043e\u043d\u0438 \u0434\u0435\u043b\u0430\u043b\u0438 \u0431\u044b \u044d\u0442\u043e \u0441 \u0444\u0438\u0437\u0438\u0447\u0435\u0441\u043a\u0438\u043c \u0437\u0435\u0440\u043a\u0430\u043b\u043e\u043c.<\/p>\n<h4>\u041e\u0441\u043d\u043e\u0432\u043d\u044b\u0435 \u0444\u0443\u043d\u043a\u0446\u0438\u0438<\/h4>\n<ul>\n<li>\n<p><strong>\u041f\u043e\u0442\u043e\u043a \u0441 \u043a\u0430\u043c\u0435\u0440\u044b \u0432 \u0440\u0435\u0430\u043b\u044c\u043d\u043e\u043c \u0432\u0440\u0435\u043c\u0435\u043d\u0438<\/strong>: \u0411\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442 \u0432\u0435\u0431-\u043a\u0430\u043c\u0435\u0440\u0443 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0434\u043b\u044f \u0437\u0430\u0445\u0432\u0430\u0442\u0430 \u0432\u0438\u0434\u0435\u043e\u043f\u043e\u0442\u043e\u043a\u0430 \u043b\u0438\u0446\u0430 \u0432 \u0440\u0435\u0430\u043b\u044c\u043d\u043e\u043c \u0432\u0440\u0435\u043c\u0435\u043d\u0438, \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u044f \u0435\u043c\u0443 \u0432\u0438\u0434\u0435\u0442\u044c \u0441\u0435\u0431\u044f \u0432 \u0440\u0435\u0430\u043b\u044c\u043d\u043e\u043c \u0432\u0440\u0435\u043c\u0435\u043d\u0438, \u043f\u0440\u0438\u043c\u0435\u0440\u044f\u044f \u0440\u0430\u0437\u043b\u0438\u0447\u043d\u0443\u044e \u043a\u043e\u0441\u043c\u0435\u0442\u0438\u043a\u0443 \u0438 \u0430\u043a\u0441\u0435\u0441\u0441\u0443\u0430\u0440\u044b.<\/p>\n<\/li>\n<li>\n<p><strong>\u041f\u0440\u0438\u043c\u0435\u043d\u0435\u043d\u0438\u0435 \u044d\u0444\u0444\u0435\u043a\u0442\u043e\u0432 \u043a \u0441\u0442\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u043e\u043c\u0443 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044e<\/strong>: \u0411\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442 \u043f\u0440\u0438\u043c\u0435\u043d\u0435\u043d\u0438\u0435 \u043c\u0430\u043a\u0438\u044f\u0436\u0430 \u0438 \u0430\u043a\u0441\u0435\u0441\u0441\u0443\u0430\u0440\u043e\u0432 \u043a \u0441\u0442\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u043c \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f\u043c.<\/p>\n<\/li>\n<\/ul>\n<h4>\u0414\u043e\u0441\u0442\u0443\u043f\u043d\u044b\u0435 \u044d\u0444\u0444\u0435\u043a\u0442\u044b<\/h4>\n<p>\u0411\u043b\u0435\u0441\u043a \u0434\u043b\u044f \u0433\u0443\u0431, \u041a\u0430\u0440\u0430\u043d\u0434\u0430\u0448 \u0434\u043b\u044f \u0433\u0443\u0431, \u041f\u043e\u043c\u0430\u0434\u0430, \u041f\u043e\u043c\u0430\u0434\u0430 \u0441 \u0448\u0438\u043c\u043c\u0435\u0440\u043e\u043c, \u041c\u0430\u0442\u043e\u0432\u0430\u044f \u043f\u043e\u043c\u0430\u0434\u0430, \u0426\u0432\u0435\u0442 \u0431\u0440\u043e\u0432\u0435\u0439, \u041f\u043e\u0434\u0432\u043e\u0434\u043a\u0430 \u0434\u043b\u044f \u0433\u043b\u0430\u0437, \u0422\u0443\u0448\u044c, \u041a\u0430\u0440\u0430\u043d\u0434\u0430\u0448 \u0434\u043b\u044f \u0433\u043b\u0430\u0437 (\u041a\u0430\u044f\u043b), \u0422\u0435\u043d\u0438 \u0434\u043b\u044f \u0432\u0435\u043a \u0441\u0430\u0442\u0438\u043d\u043e\u0432\u044b\u0435, \u0422\u0435\u043d\u0438 \u0434\u043b\u044f \u0432\u0435\u043a \u043c\u0430\u0442\u043e\u0432\u044b\u0435, \u0422\u0435\u043d\u0438 \u0434\u043b\u044f \u0432\u0435\u043a \u0441 \u0448\u0438\u043c\u043c\u0435\u0440\u043e\u043c, \u0422\u043e\u043d\u0430\u043b\u044c\u043d\u044b\u0439 \u043a\u0440\u0435\u043c \u0441\u0430\u0442\u0438\u043d\u043e\u0432\u044b\u0439, \u0422\u043e\u043d\u0430\u043b\u044c\u043d\u044b\u0439 \u043a\u0440\u0435\u043c \u043c\u0430\u0442\u043e\u0432\u044b\u0439, \u041a\u043e\u043d\u0441\u0438\u043b\u0435\u0440, \u041a\u043e\u043d\u0442\u0443\u0440\/\u0411\u0440\u043e\u043d\u0437\u0435\u0440, \u041e\u0447\u043a\u0438<\/p>\n<h4>\u0421\u0438\u0441\u0442\u0435\u043c\u043d\u044b\u0435 \u0442\u0440\u0435\u0431\u043e\u0432\u0430\u043d\u0438\u044f<\/h4>\n<ul>\n<li>\n<p><strong>\u0414\u043e\u0441\u0442\u0443\u043f\u043d\u0430\u044f \u043a\u0430\u043c\u0435\u0440\u0430 (\u0434\u043b\u044f \u0440\u0435\u0436\u0438\u043c\u0430 \u043a\u0430\u043c\u0435\u0440\u044b)<\/strong>: \u0423\u0431\u0435\u0434\u0438\u0442\u0435\u0441\u044c, \u0447\u0442\u043e \u043a\u0430\u043c\u0435\u0440\u0430 \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u0430.<\/p>\n<\/li>\n<li>\n<p><strong>SSL-\u0441\u0435\u0440\u0442\u0438\u0444\u0438\u043a\u0430\u0442<\/strong>: WebRTC \u043d\u0435 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u0431\u0435\u0437 \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b\u0430 HTTPS.<\/p>\n<\/li>\n<li>\n<p><strong>\u041f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0430 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u043e\u0432<\/strong><\/p>\n<\/li>\n<\/ul>\n<div>\n<div class=\"table\">\n<table>\n<tbody>\n<tr>\n<td>\n<p align=\"left\">\u0411\u0440\u0430\u0443\u0437\u0435\u0440<\/p>\n<\/td>\n<td>\n<p align=\"left\">\u041c\u0438\u043d\u0438\u043c\u0430\u043b\u044c\u043d\u044b\u0435 \u0442\u0440\u0435\u0431\u043e\u0432\u0430\u043d\u0438\u044f \u043a \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0443<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\">Chrome<\/p>\n<\/td>\n<td>\n<p align=\"left\">52+<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\">Firefox<\/p>\n<\/td>\n<td>\n<p align=\"left\">35+<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\">Internet Explorer<\/p>\n<\/td>\n<td>\n<p align=\"left\">\u041d\/\u0414*<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\">Opera<\/p>\n<\/td>\n<td>\n<p align=\"left\">39+<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\">Safari<\/p>\n<\/td>\n<td>\n<p align=\"left\">11+<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/div>\n<\/div>\n<p><em>* Internet Explorer<\/em> \u043d\u0435 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442\u0441\u044f \u043f\u043e\u043b\u043d\u043e\u0441\u0442\u044c\u044e \u0438\u0437-\u0437\u0430 \u043e\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0438\u044f \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0438 \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u0441\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0445 \u0432\u0435\u0431-\u0444\u0443\u043d\u043a\u0446\u0438\u0439 \u0438 WebRTC. \u0420\u0435\u043a\u043e\u043c\u0435\u043d\u0434\u0443\u0435\u0442\u0441\u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0441\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0439 \u0431\u0440\u0430\u0443\u0437\u0435\u0440 \u0434\u043b\u044f \u043b\u0443\u0447\u0448\u0435\u0439 \u0441\u043e\u0432\u043c\u0435\u0441\u0442\u0438\u043c\u043e\u0441\u0442\u0438 \u0438 \u0431\u0435\u0437\u043e\u043f\u0430\u0441\u043d\u043e\u0441\u0442\u0438.<\/p>\n<h4>\u041a\u0440\u0430\u0442\u043a\u0438\u0439 \u043e\u0431\u0437\u043e\u0440 \u043f\u0440\u043e\u0435\u043a\u0442\u0430<\/h4>\n<ul>\n<li>\n<p><strong>_documentation<\/strong>: \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044f.<\/p>\n<\/li>\n<li>\n<p><strong>Constants<\/strong>: \u041e\u0441\u043d\u043e\u0432\u043d\u044b\u0435 \u043a\u043e\u043d\u0441\u0442\u0430\u043d\u0442\u044b, \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u044b\u0435 \u0434\u043b\u044f \u044d\u0442\u043e\u0439 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438.<\/p>\n<\/li>\n<li>\n<p><strong>Effect<\/strong>: \u0417\u0434\u0435\u0441\u044c \u043f\u0440\u0438\u043c\u0435\u043d\u044f\u044e\u0442\u0441\u044f \u0440\u0430\u0437\u043b\u0438\u0447\u043d\u044b\u0435 \u044d\u0444\u0444\u0435\u043a\u0442\u044b. <\/p>\n<\/li>\n<li>\n<p><strong>Lib<\/strong>: \u0418\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u0434\u043b\u044f \u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u00ab\u044f\u0434\u0440\u0430\u00bb \u044d\u0442\u043e\u0433\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0434\u043b\u044f \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0438\u044f \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432 \u043b\u0438\u0446\u0430. <\/p>\n<\/li>\n<li>\n<p><strong>Utility<\/strong>: \u0420\u0430\u0437\u043b\u0438\u0447\u043d\u044b\u0435 \u0432\u0441\u043f\u043e\u043c\u043e\u0433\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u0444\u0443\u043d\u043a\u0446\u0438\u0438.<\/p>\n<\/li>\n<li>\n<p><strong>face.png<\/strong>: \u0414\u0435\u043c\u043e-\u043b\u0438\u0446\u043e \u0434\u043b\u044f \u00ab\u0440\u0435\u0436\u0438\u043c\u0430 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f\u00bb. \u0418\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435 \u0431\u044b\u043b\u043e \u0432\u0437\u044f\u0442\u043e \u0438\u0437 \u0418\u043d\u0442\u0435\u0440\u043d\u0435\u0442\u0430 \u0438\u0437 \u043e\u0442\u043a\u0440\u044b\u0442\u044b\u0445 \u0438\u0441\u0442\u043e\u0447\u043d\u0438\u043a\u043e\u0432.<\/p>\n<\/li>\n<li>\n<p><strong>glasses.png<\/strong>: \u0414\u0435\u043c\u043e-\u043c\u0430\u0441\u043a\u0430 \u043f\u0430\u0440\u044b \u043e\u0447\u043a\u043e\u0432. \u0421\u043c\u043e\u0442\u0440\u0438\u0442\u0435 \u043d\u0438\u0436\u0435, \u043a\u0430\u043a \u0435\u0451 \u0441\u043e\u0437\u0434\u0430\u0442\u044c.<\/p>\n<\/li>\n<li>\n<p><strong>main.js<\/strong>: \u0421\u043a\u0440\u0438\u043f\u0442, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u044b\u0439 \u0434\u043b\u044f \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u043e\u0439.<\/p>\n<\/li>\n<li>\n<p><strong>main.html<\/strong>: \u0414\u0435\u043c\u043e \u0444\u0430\u0439\u043b \u044d\u0442\u043e\u0439 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438.<\/p>\n<\/li>\n<\/ul>\n<h4>\u041e\u0442\u043b\u0430\u0434\u043a\u0430 \u043d\u0430 \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u043e\u0439 \u043c\u0430\u0448\u0438\u043d\u0435<\/h4>\n<p>WebRTC \u043d\u0435 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u0431\u0435\u0437 SSL-\u0441\u0435\u0440\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u0430. \u0415\u0441\u043b\u0438 \u0432\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u043e\u0442\u043b\u0430\u0434\u0438\u0442\u044c \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0443, \u043f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430, \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u0435 \u0444\u043b\u0430\u0433 \u0432 Google Chrome. \u041e\u0442\u043a\u0440\u043e\u0439\u0442\u0435 \u0441\u0441\u044b\u043b\u043a\u0443 \u043d\u0438\u0436\u0435 \u0438 \u0432\u0432\u0435\u0434\u0438\u0442\u0435 \u0441\u0432\u043e\u0439 \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u044b\u0439 \u0434\u043e\u043c\u0435\u043d \u0432 \u043f\u043e\u043b\u0435 &#171;Insecure origins treated as secure&#187; (\u041d\u0435\u0431\u0435\u0437\u043e\u043f\u0430\u0441\u043d\u044b\u0435 \u0438\u0441\u0442\u043e\u0447\u043d\u0438\u043a\u0438, \u0440\u0430\u0441\u0441\u043c\u0430\u0442\u0440\u0438\u0432\u0430\u0435\u043c\u044b\u0435 \u043a\u0430\u043a \u0431\u0435\u0437\u043e\u043f\u0430\u0441\u043d\u044b\u0435).<\/p>\n<p>\u041f\u0440\u0438\u043c\u0435\u0440 \u0434\u043b\u044f GoogleChrome:<\/p>\n<pre><code>   chrome:\/\/flags\/#unsafely-treat-insecure-origin-as-secure<\/code><\/pre>\n<p>\u0411\u0440\u0430\u0443\u0437\u0435\u0440 Mozilla:<\/p>\n<blockquote>\n<p><code>- \u041e\u0442\u043a\u0440\u043e\u0439\u0442\u0435 \u0432 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0435 -&gt; about:config<\/code><\/p>\n<p><code>- \u0423\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \"true\" \u0434\u043b\u044f media.devices.insecure.enabled \u0438 media.getusermedia.insecure.enabled<\/code><\/p>\n<\/blockquote>\n<h2>\u0421\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0430 \u043f\u0440\u043e\u0435\u043a\u0442\u0430: Constants<\/h2>\n<p><a class=\"anchor\" name=\"constants\" id=\"constants\"><\/a><\/p>\n<p>\u0414\u043b\u044f \u043e\u0431\u043b\u0435\u0433\u0447\u0435\u043d\u0438\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u044f \u0440\u0435\u0448\u0438\u043b\u0430 \u0432\u044b\u043d\u0435\u0441\u0442\u0438 \u0432 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0439 \u0444\u0430\u0439\u043b \u043a\u043e\u043d\u0441\u0442\u0430\u043d\u0442\u044b \u0441 \u044d\u0444\u0444\u0435\u043a\u0442\u0430\u043c\u0438. \u041f\u0440\u0438 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e\u0441\u0442\u0438, \u043c\u043e\u0436\u043d\u043e \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u043a\u043e\u043d\u0441\u0442\u0430\u043d\u0442\u044b \u0438 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0430\u0442\u044c \u043f\u043e \u0442\u0440\u0435\u0431\u043e\u0432\u0430\u043d\u0438\u044e.<\/p>\n<details class=\"spoiler\">\n<summary>\u041f\u0440\u0438\u043c\u0435\u0440 \u0444\u0430\u0439\u043b\u0430 EffectConstants.js<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"javascript\">export const EFFECT_BROWS_COLOR = 'BrowsColor';export const EFFECT_LIPSTICK = 'Lipstick';export const EFFECT_MATTE_LIPSTICK = 'MatteLipstick';export const EFFECT_MASCARA = 'Mascara';export const EFFECT_EYELINER = 'Eyeliner';export const EFFECT_KAJAL = 'Kajal';export const EFFECT_EYEGLASSES = 'Eyeglasses';export const EFFECT_LIPLINER = 'LipLiner';export const EFFECT_LIP_GLOSS = 'LipGloss';export const EFFECT_LIPSTICK_SHIMMER = 'LipstickShimmer';export const EFFECT_FOUNDATION_SATIN = 'FoundationSatin';export const EFFECT_FOUNDATION_MATTE = 'FoundationMatte';export const EFFECT_CONCEALER = 'Concealer';export const EFFECT_CONTOUR = 'Contour';export const EFFECT_EYESHADOW_SATIN = 'EyeShadowSatin';export const EFFECT_EYESHADOW_MATTE = 'EyeShadowMatte';export const EFFECT_EYESHADOW_SHIMMER = 'EyeShadowShimmer';Object.defineProperty(window, 'EFFECT_EYESHADOW_SHIMMER', {     value: 'EyeShadowShimmer',     writable: false,     configurable: false });Object.defineProperty(window, 'EFFECT_EYESHADOW_MATTE', {     value: 'EyeShadowMatte',     writable: false,     configurable: false });Object.defineProperty(window, 'EFFECT_EYESHADOW_SATIN', {     value: 'EyeShadowSatin',     writable: false,     configurable: false });Object.defineProperty(window, 'EFFECT_CONTOUR', {     value: 'Contour',     writable: false,     configurable: false });Object.defineProperty(window, 'EFFECT_CONCEALER', {     value: 'Concealer',     writable: false,     configurable: false });Object.defineProperty(window, 'EFFECT_FOUNDATION_MATTE', {     value: 'FoundationMatte',     writable: false,     configurable: false });Object.defineProperty(window, 'EFFECT_FOUNDATION_SATIN', {     value: 'FoundationSatin',     writable: false,     configurable: false });Object.defineProperty(window, 'EFFECT_BROWS_COLOR', {     value: 'BrowsColor',     writable: false,     configurable: false });Object.defineProperty(window, 'EFFECT_MASCARA', {     value: 'Mascara',     writable: false,     configurable: false });Object.defineProperty(window, 'EFFECT_LIPSTICK', {     value: 'Lipstick',     writable: false,     configurable: false });Object.defineProperty(window, 'EFFECT_MATTE_LIPSTICK', {     value: 'MatteLipstick',     writable: false,     configurable: false });Object.defineProperty(window, 'EFFECT_EYELINER', {     value: 'Eyeliner',     writable: false,     configurable: false });Object.defineProperty(window, 'EFFECT_KAJAL', {     value: 'Kajal',     writable: false,     configurable: false });Object.defineProperty(window, 'EFFECT_EYEGLASSES', {     value: 'Eyeglasses',     writable: false,     configurable: false  });Object.defineProperty(window, 'EFFECT_LIPLINER', {     value: 'LipLiner',     writable: false,     configurable: false });Object.defineProperty(window, 'EFFECT_LIP_GLOSS', {     value: 'LipGloss',     writable: false,     configurable: false });Object.defineProperty(window, 'EFFECT_LIPSTICK_SHIMMER', {     value: 'LipstickShimmer',     writable: false,     configurable: false });<\/code><\/pre>\n<\/div>\n<\/details>\n<h2>\u0421\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0430 \u043f\u0440\u043e\u0435\u043a\u0442\u0430: Lib &#8212; The Core of The Library<\/h2>\n<p><a class=\"anchor\" name=\"lib\" id=\"lib\"><\/a><\/p>\n<p>\u0412 \u043e\u0441\u043d\u043e\u0432\u0435 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438 \u043b\u0435\u0436\u0438\u0442 <a href=\"https:\/\/ai.google.dev\/edge\/mediapipe\/solutions\/guide\" rel=\"noopener noreferrer nofollow\"><strong>MediaPipe<\/strong><\/a> \u2014 \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a \u043e\u0442 Google \u0434\u043b\u044f \u0440\u0430\u0441\u043f\u043e\u0437\u043d\u0430\u0432\u0430\u043d\u0438\u044f \u043b\u0438\u0446\u0430 \u0438 \u043a\u043b\u044e\u0447\u0435\u0432\u044b\u0445 \u0442\u043e\u0447\u0435\u043a. \u0421 \u0435\u0433\u043e \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430 \u0442\u043e\u0447\u043d\u043e \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u0442 \u043f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0433\u043b\u0430\u0437, \u0433\u0443\u0431, \u043d\u043e\u0441\u0430 \u0438 \u0434\u0440\u0443\u0433\u0438\u0445 \u043e\u0431\u043b\u0430\u0441\u0442\u0435\u0439, \u0447\u0442\u043e\u0431\u044b \u043d\u0430\u043b\u043e\u0436\u0438\u0442\u044c \u043c\u0430\u043a\u0438\u044f\u0436 \u0438\u043b\u0438 \u0430\u043a\u0441\u0435\u0441\u0441\u0443\u0430\u0440\u044b.<\/p>\n<p>\u041e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0438\u0435 \u0442\u043e\u0447\u0435\u043a \u043d\u0430 \u043b\u0438\u0446\u0435 \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u0432\u043e\u0442 \u0442\u0430\u043a:<\/p>\n<figure class=\"full-width\">\n<div><figcaption>\u0432\u0437\u044f\u0442\u043e \u0441 \u043e\u0444\u0438\u0446\u0438\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u0441\u0430\u0439\u0442\u0430 <a href=\"https:\/\/ai.google.dev\/\" rel=\"noopener noreferrer nofollow\">https:\/\/ai.google.dev\/<\/a><\/figcaption><\/div>\n<\/figure>\n<p>\u041a\u0430\u0436\u0434\u0430\u044f \u0442\u043e\u0447\u043a\u0430 \u0438\u043c\u0435\u0435\u0442 \u0441\u0432\u043e\u0438 \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u044b \u0432 \u043f\u043b\u043e\u0441\u043a\u043e\u0441\u0442\u0438. \u0414\u043b\u044f \u0442\u043e\u0433\u043e, \u0447\u0442\u043e\u0431\u044b \u0437\u0430\u043a\u0440\u0430\u0441\u0438\u0442\u044c \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u043d\u0443\u044e \u043e\u0431\u043b\u0430\u0441\u0442\u044c, \u044f \u0432\u044b\u0431\u0438\u0440\u0430\u043b\u0430 \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u044b, \u0437\u0430\u043c\u044b\u043a\u0430\u043b\u0430 \u0438\u0445 \u0432 \u0444\u0438\u0433\u0443\u0440\u0443, \u0430 \u043f\u043e\u0441\u043b\u0435 &#8212; \u043f\u0440\u0438\u043c\u0435\u043d\u044f\u043b\u0430 \u044d\u0444\u0444\u0435\u043a\u0442\u044b \u0441 \u043d\u0430\u043b\u043e\u0436\u0435\u043d\u0438\u0435\u043c \u0446\u0432\u0435\u0442\u0430\/\u0430\u043a\u0441\u0435\u0441\u0441\u0443\u0430\u0440\u043e\u0432. \u0422\u0443\u0442 \u043c\u043e\u0436\u043d\u043e \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c <a href=\"https:\/\/ai.google.dev\/edge\/mediapipe\/solutions\/vision\/face_landmarker\" rel=\"noopener noreferrer nofollow\">\u043f\u043e\u043b\u043d\u0443\u044e \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044e \u043f\u0440\u043e FaceLandmarks<\/a>.<\/p>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043d\u0435\u043c\u043d\u043e\u0433\u043e \u0434\u0435\u0442\u0430\u043b\u044c\u043d\u0435\u0435, \u0432 \u043f\u0440\u043e\u0435\u043a\u0442\u0435 \u044d\u0442\u043e \u0432\u0441\u0435 \u043d\u0430\u0445\u043e\u0434\u0438\u0442\u0441\u044f \u0432:<\/p>\n<pre><code>- Lib   - Mediapipe       - face_mesh       - vision_task<\/code><\/pre>\n<p>\u041d\u0430 \u043c\u043e\u043c\u0435\u043d\u0442 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438, \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442 \u0434\u0432\u0435 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438: \u0441\u0442\u0430\u0431\u0438\u043b\u044c\u043d\u0443\u044e \u0432\u0435\u0440\u0441\u0438\u044e Face Mesh \u0438 \u044d\u043a\u0441\u043f\u0435\u0440\u0438\u043c\u0435\u043d\u0442\u0430\u043b\u044c\u043d\u0443\u044e Vision Task, \u043e\u0434\u043d\u0430\u043a\u043e \u043f\u0440\u0435\u0434\u043f\u043e\u0447\u0442\u0435\u043d\u0438\u0435 \u043e\u0442\u0434\u0430\u043d\u043e \u043f\u0435\u0440\u0432\u043e\u0439 \u0438\u0437-\u0437\u0430 \u0431\u043e\u043b\u044c\u0448\u0435\u0439 \u043d\u0430\u0434\u0451\u0436\u043d\u043e\u0441\u0442\u0438. \u041a\u0430\u0436\u0434\u044b\u0439 \u0434\u0432\u0438\u0436\u043e\u043a \u0441\u043e\u043f\u0440\u043e\u0432\u043e\u0436\u0434\u0430\u0435\u0442\u0441\u044f \u0441\u0432\u043e\u0438\u043c <code>Processor<\/code> \u2014 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u043e\u043c, \u043e\u0442\u0432\u0435\u0447\u0430\u044e\u0449\u0438\u043c \u0437\u0430 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0443 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u0438\u043b\u0438 \u0432\u0438\u0434\u0435\u043e, \u043c\u0430\u0441\u0448\u0442\u0430\u0431\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0445\u043e\u043b\u0441\u0442\u0430, \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u044e HTML-\u044d\u043b\u0435\u043c\u0435\u043d\u0442\u043e\u0432 \u0438 \u0437\u0430\u043f\u0443\u0441\u043a \u0446\u0438\u043a\u043b\u0430 \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u043c\u0430\u043a\u0438\u044f\u0436\u0430 \u0432 \u0440\u0435\u0430\u043b\u044c\u043d\u043e\u043c \u0432\u0440\u0435\u043c\u0435\u043d\u0438. <\/p>\n<p><em>\u0412\u0430\u0436\u043d\u043e! \u041f\u0440\u0438 \u043f\u0435\u0440\u0435\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0438 \u043c\u0435\u0436\u0434\u0443 \u0434\u0432\u0438\u0436\u043a\u0430\u043c\u0438, \u0438\u043c\u0435\u0439\u0442\u0435 \u0432\u0432\u0438\u0434\u0443, \u0447\u0442\u043e <\/em><strong><em>FaceMesh<\/em><\/strong><em> \u043d\u0435 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442 \u0440\u0430\u0431\u043e\u0442\u0443 \u0441 \u0432\u043e\u043b\u043e\u0441\u0430\u043c\u0438 \u0438 \u043d\u0430 <\/em><strong><em>\u0441\u0435\u0433\u043e\u0434\u043d\u044f\u0448\u043d\u0438\u0439 \u0434\u0435\u043d\u044c \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0443\u0441\u0442\u0430\u0440\u0435\u0432\u0448\u0438\u043c.<\/em><\/strong><\/p>\n<details class=\"spoiler\">\n<summary>FaceMeshProcessor<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"javascript\">import FaceMeshEngine from '.\/engine\/FaceMeshEngine.js';  \/**  * Processor of FaceMesh engine  * @type {{processVideo: ((function(*, *, *): Promise&lt;boolean&gt;)|*), processImage: (function(*, *, *): Promise&lt;boolean&gt;)}}  *\/ const FaceMeshProcessor = (function () {          let isAnimating = false;         let intervalId = null;          \/**          * Validate that given object is &lt;img&gt; element.          * (can be get by document.getElementById(\"ID_STRING\"))          *          * @param imageHtmlObject          *\/         function validateImageHtmlObject(imageHtmlObject) {             if (imageHtmlObject.tagName.toLowerCase() !== 'img') {                 throw new Error(\"Can not process image. The given object doesn't represent img tag\");             }         }          \/**          * Validate that given object is &lt;video&gt; element.          * (can be get by document.getElementById(\"ID_STRING\"))          *          * @param videoHtmlObject          *\/         function validateVideoHtmlObject(videoHtmlObject) {             if (videoHtmlObject.tagName.toLowerCase() !== 'video') {                 throw new Error(\"Can not process video. The given object doesn't represent video tag\");             }         }          \/**          * Validate that given object is &lt;canvas&gt; element.          * (can be get by document.getElementById(\"ID_STRING\"))          *          * @param canvasHtmlObject          *\/         function validateCanvasHtmlObject(canvasHtmlObject) {             if (canvasHtmlObject.tagName.toLowerCase() !== 'canvas') {                 throw new Error(\"Can not process video. The given object doesn't represent canvas tag\");             }         }          \/**          * Process video with requested effect          *          * Where:          *  sourceVideoHtmlObject - &lt;video&gt; html object          *  resultCanvasHTMLObject - canvas where to show the resulting output          *<\/code><\/pre>\n<\/div>\n<\/details>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-465764","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/465764","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=465764"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/465764\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=465764"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=465764"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=465764"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}