├── CNAME ├── LICENSE ├── README.md ├── about.html ├── index.css ├── index.html └── index.js /CNAME: -------------------------------------------------------------------------------- 1 | isthisheadlinefake.com -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 TristanMenzinger & Robin Weitzel 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Is this Headline Fake or Not? 2 | 3 | We live in crazy times - but can you tell how crazy? Visit [isthisheadlinefake.com](https://isthisheadlinefake.com) to find out. 4 | 5 | 6 | 7 | ### Why 8 | We made this to show two things: 9 | * Neural networks have become incredibly good at producing grammatically correct but completely nonsensical text 10 | * There are so many nonsensical headlines nowadays, it's hard to distinguish fake from real 11 | 12 | ### How 13 | * We trained the network on 200'000 real headlines from the [Huffington Post](https://www.huffpost.com) 14 | using OpenAI's [GPT-2](https://github.com/openai/gpt-2!). If you want to experiment yourself, you can find the dataset [here](https://www.kaggle.com/rmisra/news-category-dataset). 15 | * Hosted on Github Pages using Cloudflare Workers & Key-Value store. Using native CSS transforms for the animations. 16 | 17 | 18 | ### Who 19 | Made by [Robin Weitzel](https://github.com/RobinWeitzel) and [Tristan Menzinger](https://github.com/TristanMenzinger). 20 | 21 | ### Disclaimer 22 | Half of the headlines are from the Huffington Post, the other half is machine generated. We do not claim authenticity for any of these headlines. The headlines are posted for comedic purposes only and not meant to attack anyone. If you find a headline offensive please get in touch via GitHub. 23 | -------------------------------------------------------------------------------- /about.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 99 | 100 | 101 |
102 |

Is this Headline

103 |

fake or not?

104 |
105 |
106 |
DISCLAIMER
107 | 108 | Half of the headlines are from the Huffington Post, the other half is machine generated. 109 | We do not claim authenticity for any of these headlines. 110 | The headlines are posted for comedic purposes and not meant to attack anyone. 111 | If you find a headline offensive please get in touch via GitHub. 112 | 113 |
114 |
115 |

Why

116 | We made this to show two things: 117 | 121 |
122 |
123 |

How

124 |
Content
125 | 126 | The fake headlines were generated by a neural network. 127 | We trained the network on 200'000 real headlines from the Huffington Post 128 | using OpenAI's GPT-2. 129 | 130 | 131 | If you want to experiment yourself, you can find the dataset here. 132 | 133 |
Website
134 | 135 | Hosted on Github Pages using Cloudflare Workers & Key-Value store. Using native CSS. 136 | 137 |
138 |
139 |

Who

140 | Made with by Robin Weitzel and Tristan Menzinger. 141 |
142 |
143 |

License

144 | This project is released under the MIT License.
145 | 146 | Copyright (c) 2020 Tristan Menzinger
147 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
148 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
149 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
150 |
151 |
152 | 153 | 154 | 155 | -------------------------------------------------------------------------------- /index.css: -------------------------------------------------------------------------------- 1 | html, 2 | body { 3 | margin: 0px; 4 | padding: 0px; 5 | width: 100vw; 6 | height: 100%; 7 | overflow: hidden; 8 | } 9 | 10 | .swipe-card { 11 | position: absolute; 12 | 13 | margin: auto; 14 | left: 0; 15 | right: 0; 16 | 17 | height: calc(100% - 4em); 18 | width: 80%; 19 | max-width: 400px; 20 | 21 | will-change: transform; 22 | 23 | cursor: pointer; 24 | } 25 | 26 | .swipe-card.smooth { 27 | transition: 0.5s ease-in-out; 28 | } 29 | 30 | .swipe-card.hide { 31 | display: none; 32 | } 33 | 34 | .swipe-card.smooth>.overlay { 35 | transition: 0.65s ease-in-out; 36 | will-change: transform; 37 | } 38 | 39 | .swipe-card.overlay { 40 | display: flex; 41 | justify-content: center; 42 | 43 | font-size: 50pt; 44 | font-weight: 600; 45 | color: white; 46 | 47 | opacity: 0; 48 | 49 | height: 100%; 50 | width: 100%; 51 | margin-top: -20px; 52 | } 53 | 54 | .swipe-card.overlay span { 55 | align-self: center; 56 | } 57 | 58 | .swipe-card.overlay.overlay-left { 59 | background-color: rgb(200, 0, 0); 60 | } 61 | 62 | .swipe-card.overlay.overlay-right { 63 | background-color: rgb(109, 168, 50); 64 | } 65 | 66 | body { 67 | display: flex; 68 | justify-content: center; 69 | } 70 | 71 | #container-wrapper { 72 | display: flex; 73 | flex-direction: column; 74 | align-self: center; 75 | overflow: hidden; 76 | height: 100%; 77 | width: 100vw; 78 | 79 | /*So the overflow: hidden can work;*/ 80 | position: relative; 81 | } 82 | 83 | .rel-max-wrapper { 84 | position: relative; 85 | height: 100%; 86 | width: 100%; 87 | } 88 | 89 | /* Styling */ 90 | .headline { 91 | flex-grow: 0; 92 | flex-shrink: 0; 93 | flex-basis: 40%; 94 | padding: 20px; 95 | overflow: none; 96 | 97 | -webkit-touch-callout: none; 98 | /* iOS Safari */ 99 | -webkit-user-select: none; 100 | /* Safari */ 101 | -khtml-user-select: none; 102 | /* Konqueror HTML */ 103 | -moz-user-select: none; 104 | /* Old versions of Firefox */ 105 | -ms-user-select: none; 106 | /* Internet Explorer/Edge */ 107 | user-select: none; 108 | } 109 | 110 | .swipe-card .card-body { 111 | display: flex; 112 | flex-direction: column; 113 | padding: 0px; 114 | } 115 | 116 | .card-text { 117 | padding: 15px; 118 | max-height: 90%; 119 | overflow: hidden; 120 | 121 | font-size: 20pt; 122 | font-family: Helvetica, Roboto, Arial; 123 | font-weight: 700; 124 | } 125 | 126 | .card-info { 127 | display: flex; 128 | } 129 | 130 | .card-count { 131 | margin-left: auto; 132 | color: darkgray; 133 | float: right; 134 | } 135 | 136 | .card-difficulty { 137 | margin-left: 5px; 138 | } 139 | 140 | .card-difficulty {} 141 | 142 | #difficulty { 143 | position: absolute; 144 | 145 | width: 100%; 146 | height: 100%; 147 | 148 | display: none; 149 | /* flex */ 150 | flex-direction: column; 151 | 152 | align-items: center; 153 | } 154 | 155 | #difficulty a { 156 | margin-bottom: 1.5rem; 157 | width: 100px; 158 | } 159 | 160 | #difficulty .btn-outline-secondary { 161 | border: none; 162 | } 163 | 164 | #info-done { 165 | position: absolute; 166 | 167 | width: 100%; 168 | height: 100%; 169 | 170 | display: none; 171 | /* flex */ 172 | flex-direction: column; 173 | 174 | align-items: center; 175 | justify-content: center; 176 | } 177 | 178 | #info-done>span { 179 | display: flex; 180 | align-items: baseline; 181 | flex-direction: row; 182 | } 183 | 184 | #info-done>span>* { 185 | margin-right: 5px; 186 | } 187 | #container-headlines { 188 | max-height: 800px; 189 | } 190 | 191 | #container-title { 192 | align-self: center; 193 | padding: 3em; 194 | padding-top: 2em; 195 | padding-bottom: 2em; 196 | 197 | width: 100%; 198 | max-width: 500px; 199 | 200 | position: relative; 201 | } 202 | 203 | #container-title h1 { 204 | font-weight: 700; 205 | } 206 | 207 | #container-title>*:not(:first-child) { 208 | margin-top: -15px; 209 | } 210 | 211 | #container-title span.red, 212 | #question span.red { 213 | text-decoration: underline; 214 | text-decoration-color: rgb(200, 0, 0); 215 | } 216 | 217 | #container-title span.green, 218 | #question span.green { 219 | text-decoration: underline; 220 | text-decoration-color: rgb(109, 168, 50); 221 | } 222 | 223 | #container-title .instructions { 224 | font-size: 10pt; 225 | color: darkgray; 226 | } 227 | 228 | #difficulty #difficulty-picker { 229 | display: flex; 230 | flex-direction: column; 231 | /*margin-top: auto;*/ 232 | /*margin-bottom: auto;*/ 233 | 234 | 235 | justify-content: center; 236 | align-items: center; 237 | 238 | height: calc(100% - 3em - 10em); 239 | width: 80%; 240 | max-width: 400px; 241 | } 242 | 243 | #github, 244 | #about { 245 | position: absolute; 246 | text-decoration: underline; 247 | right: 3em; 248 | } 249 | 250 | #about { 251 | top: 3em; 252 | } 253 | 254 | #github { 255 | top: 3em; 256 | right: 6em; 257 | } 258 | 259 | #about>a, 260 | #github>a { 261 | font-size: 10pt; 262 | color: darkgray; 263 | } 264 | 265 | #info-done-results { 266 | display: flex; 267 | width: 100%; 268 | padding: 3em; 269 | flex-direction: column; 270 | } 271 | 272 | #info-done-results .info-card { 273 | width: 100%; 274 | margin-bottom: 0.5em; 275 | } 276 | 277 | .red { 278 | color: rgb(200, 0, 0); 279 | } 280 | 281 | .green { 282 | color: rgb(109, 168, 50); 283 | } 284 | 285 | .info-card i { 286 | position: absolute; 287 | right: 10px; 288 | top: 10px; 289 | color: darkgray; 290 | } 291 | 292 | .info-card .score { 293 | font-size: 10pt; 294 | } 295 | 296 | .info-card .title { 297 | font-weight: 700; 298 | } 299 | 300 | .last-headline { 301 | border: none; 302 | padding: 10px; 303 | transform: rotate(1deg); 304 | } 305 | 306 | .last-headline .card { 307 | padding: 8px; 308 | border-color: darkgray; 309 | height: 100%; 310 | } 311 | 312 | .last-headline>.card { 313 | background-color: white; 314 | background-image: url("data:image/svg+xml,%3Csvg width='6' height='6' viewBox='0 0 6 6' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='%23000000' fill-opacity='0.4' fill-rule='evenodd'%3E%3Cpath d='M5 0h1L0 6V5zM6 5v1H5z'/%3E%3C/g%3E%3C/svg%3E"); 315 | } 316 | 317 | .confetti { 318 | position: absolute; 319 | height: 10px; 320 | width: 10px; 321 | z-index: 10000; 322 | will-change: transform; 323 | transition-timing-function: linear; 324 | } 325 | 326 | #celebration_container { 327 | position: absolute; 328 | width: 100%; 329 | height: 10px; 330 | } 331 | 332 | #container-streak { 333 | position: absolute; 334 | /*bottom: 15px;*/ 335 | top: min(calc(100% - 25px), 965px); 336 | width: 100%; 337 | height: 10px; 338 | text-align: center; 339 | } 340 | 341 | .progressbar { 342 | display: inline-block; 343 | border-radius: 10px; 344 | border: 1px solid lightgray; 345 | height: 100%; 346 | width: 80%; 347 | max-width: 400px; 348 | } 349 | 350 | .progressbar .count { 351 | position: absolute; 352 | /*bottom: 7px;*/ 353 | bottom: -4px; 354 | margin-left: -20px; 355 | color: darkgray; 356 | font-size: 10pt; 357 | text-align: left; 358 | text-align: right; 359 | } 360 | 361 | .progressbar .count.highlight { 362 | -webkit-animation: highlightkf 0.5s cubic-bezier(0.455, 0.030, 0.515, 0.955) both; 363 | animation: highlightkf 0.5s cubic-bezier(0.455, 0.030, 0.515, 0.955) both; 364 | } 365 | 366 | .progressbar .description { 367 | position: absolute; 368 | color: darkgray; 369 | font-size: 10pt; 370 | margin-top: -20px; 371 | } 372 | 373 | @-webkit-keyframes highlightkf { 374 | 375 | 0%, 376 | 100% { 377 | transform: scale(1, 1); 378 | } 379 | 380 | 50% { 381 | transform: scale(4, 4); 382 | } 383 | 384 | 100% { 385 | transform: scale(1, 1); 386 | } 387 | } 388 | 389 | .progressbar .progress { 390 | height: 100%; 391 | width: 0%; 392 | transition: 0.5s ease-in-out; 393 | 394 | background: rgb(0, 0, 0); 395 | background: linear-gradient(90deg, rgba(0, 0, 0, 1) 0%, rgba(255, 98, 0, 1) 100%); 396 | background-size: max(100%, 400px); 397 | } 398 | 399 | 400 | /*TEXT FLY-IN*/ 401 | #flyin { 402 | position: absolute; 403 | 404 | top: 75%; 405 | left: calc(50% - min(50vw, 300px)); 406 | 407 | height: 100px; 408 | width: 100vw; 409 | max-width: 600px; 410 | 411 | text-align: center; 412 | 413 | transform: rotate(0deg); 414 | transition: transform 0s linear; 415 | } 416 | 417 | #flyin.fly { 418 | z-index: 1000; 419 | } 420 | 421 | #flyin > #flyin-text { 422 | position: absolute; 423 | 424 | text-align: center; 425 | 426 | left: 0; 427 | top: 0; 428 | width: 100%; 429 | color: darkgray; 430 | font-size: 25pt; 431 | font-weight: 600; 432 | opacity: 0; 433 | } 434 | 435 | #flyin.fly #flyin-text { 436 | animation: fly-in-animation 1s linear; 437 | } 438 | 439 | @-webkit-keyframes fly-in-animation { 440 | 0% { 441 | opacity: 0; 442 | transform: scale(1.3); 443 | } 444 | 20% { 445 | opacity: 1; 446 | transform: scale(1.3); 447 | } 448 | 449 | 80% { 450 | opacity: 1; 451 | } 452 | 453 | 100% { 454 | transform: scale(0.75); 455 | opacity: 0; 456 | } 457 | } 458 | 459 | 460 | /*ANIMATIONS */ 461 | 462 | .shake-bottom1 { 463 | -webkit-animation: shake-bottom1 0.8s cubic-bezier(0.455, 0.030, 0.515, 0.955) both; 464 | animation: shake-bottom1 0.8s cubic-bezier(0.455, 0.030, 0.515, 0.955) both; 465 | } 466 | 467 | .shake-bottom2 { 468 | -webkit-animation: shake-bottom2 0.8s cubic-bezier(0.455, 0.030, 0.515, 0.955) both; 469 | animation: shake-bottom2 0.8s cubic-bezier(0.455, 0.030, 0.515, 0.955) both; 470 | } 471 | 472 | .shake-bottom3 { 473 | -webkit-animation: shake-bottom3 0.8s cubic-bezier(0.455, 0.030, 0.515, 0.955) both; 474 | animation: shake-bottom3 0.8s cubic-bezier(0.455, 0.030, 0.515, 0.955) both; 475 | } 476 | 477 | /* ---------------------------------------------- 478 | * Generated by Animista on 2020-6-9 20:30:46 479 | * Licensed under FreeBSD License. 480 | * See http://animista.net/license for more info. 481 | * w: http://animista.net, t: @cssanimista 482 | * ---------------------------------------------- */ 483 | 484 | /** 485 | * ---------------------------------------- 486 | * animation shake-bottom 487 | * ---------------------------------------- 488 | */ 489 | @-webkit-keyframes shake-bottom1 { 490 | 491 | 0%, 492 | 100% { 493 | -webkit-transform: rotate(0deg); 494 | transform: rotate(0deg); 495 | -webkit-transform-origin: 50% 100%; 496 | transform-origin: 50% 100%; 497 | } 498 | 499 | 10% { 500 | -webkit-transform: rotate(0.125deg); 501 | transform: rotate(0.125deg); 502 | } 503 | 504 | 20%, 505 | 40%, 506 | 60% { 507 | -webkit-transform: rotate(-0.25deg); 508 | transform: rotate(-0.25deg); 509 | } 510 | 511 | 30%, 512 | 50%, 513 | 70% { 514 | -webkit-transform: rotate(0.25deg); 515 | transform: rotate(0.25deg); 516 | } 517 | 518 | 80% { 519 | -webkit-transform: rotate(-0.125deg); 520 | transform: rotate(-0.125deg); 521 | } 522 | 523 | 90% { 524 | -webkit-transform: rotate(0.125deg); 525 | transform: rotate(0.125deg); 526 | } 527 | } 528 | 529 | @-webkit-keyframes shake-bottom2 { 530 | 531 | 0%, 532 | 100% { 533 | -webkit-transform: rotate(0deg); 534 | transform: rotate(0deg); 535 | -webkit-transform-origin: 50% 100%; 536 | transform-origin: 50% 100%; 537 | } 538 | 539 | 10% { 540 | -webkit-transform: rotate(0.25deg); 541 | transform: rotate(0.25deg); 542 | } 543 | 544 | 20%, 545 | 40%, 546 | 60% { 547 | -webkit-transform: rotate(-0.5deg); 548 | transform: rotate(-0.5deg); 549 | } 550 | 551 | 30%, 552 | 50%, 553 | 70% { 554 | -webkit-transform: rotate(0.25deg); 555 | transform: rotate(0.25deg); 556 | } 557 | 558 | 80% { 559 | -webkit-transform: rotate(-0.5deg); 560 | transform: rotate(-0.5deg); 561 | } 562 | 563 | 90% { 564 | -webkit-transform: rotate(0.25deg); 565 | transform: rotate(0.25deg); 566 | } 567 | } 568 | 569 | @-webkit-keyframes shake-bottom3 { 570 | 571 | 0%, 572 | 100% { 573 | -webkit-transform: rotate(0deg) scale(1.0); 574 | transform: rotate(0deg) scale(1.0); 575 | -webkit-transform-origin: 50% 100%; 576 | transform-origin: 50% 100%; 577 | } 578 | 579 | 10% { 580 | -webkit-transform: rotate(0.3deg) scale(1.05); 581 | transform: rotate(0.3deg) scale(1.05); 582 | } 583 | 584 | 20%, 585 | 40%, 586 | 60% { 587 | -webkit-transform: rotate(-0.7deg) scale(1.1); 588 | transform: rotate(-0.7deg) scale(1.1); 589 | } 590 | 591 | 30%, 592 | 50%, 593 | 70% { 594 | -webkit-transform: rotate(0.3deg) scale(1.05); 595 | transform: rotate(0.3deg) scale(1.05); 596 | } 597 | 598 | 80% { 599 | -webkit-transform: rotate(-0.7deg) scale(1.1); 600 | transform: rotate(-0.7deg) scale(1.1); 601 | } 602 | 603 | 90% { 604 | -webkit-transform: rotate(0.3deg) scale(1.05); 605 | transform: rotate(0.3deg) scale(1.05); 606 | } 607 | } 608 | 609 | .bounce-right { 610 | -webkit-animation: bounce-right 0.8s both; 611 | animation: bounce-right 0.8s both; 612 | } 613 | 614 | /* ---------------------------------------------- 615 | * Generated by Animista on 2020-6-9 20:34:13 616 | * Licensed under FreeBSD License. 617 | * See http://animista.net/license for more info. 618 | * w: http://animista.net, t: @cssanimista 619 | * ---------------------------------------------- */ 620 | 621 | /** 622 | * ---------------------------------------- 623 | * animation bounce-right 624 | * ---------------------------------------- 625 | */ 626 | @-webkit-keyframes bounce-right { 627 | 0% { 628 | -webkit-transform: translateX(48px) scale(1.2); 629 | transform: translateX(48px) scale(1.2); 630 | -webkit-animation-timing-function: ease-in; 631 | animation-timing-function: ease-in; 632 | opacity: 1; 633 | } 634 | 635 | 24% { 636 | opacity: 1; 637 | } 638 | 639 | 40% { 640 | -webkit-transform: translateX(26px); 641 | transform: translateX(26px); 642 | -webkit-animation-timing-function: ease-in; 643 | animation-timing-function: ease-in; 644 | } 645 | 646 | 65% { 647 | -webkit-transform: translateX(13px); 648 | transform: translateX(13px); 649 | -webkit-animation-timing-function: ease-in; 650 | animation-timing-function: ease-in; 651 | } 652 | 653 | 82% { 654 | -webkit-transform: translateX(6.5px); 655 | transform: translateX(6.5px); 656 | -webkit-animation-timing-function: ease-in; 657 | animation-timing-function: ease-in; 658 | } 659 | 660 | 93% { 661 | -webkit-transform: translateX(4px); 662 | transform: translateX(4px); 663 | -webkit-animation-timing-function: ease-in; 664 | animation-timing-function: ease-in; 665 | } 666 | 667 | 25%, 668 | 55%, 669 | 75%, 670 | 87%, 671 | 98% { 672 | -webkit-transform: translateX(0px); 673 | transform: translateX(0px); 674 | -webkit-animation-timing-function: ease-out; 675 | animation-timing-function: ease-out; 676 | } 677 | 678 | 100% { 679 | -webkit-transform: translateX(0px) scale(1.0); 680 | transform: translateX(0px) scale(1.0); 681 | -webkit-animation-timing-function: ease-out; 682 | animation-timing-function: ease-out; 683 | opacity: 1; 684 | } 685 | } 686 | 687 | /*FIRE*/ 688 | 689 | #container-streak.max-1 .mo-fire { 690 | width: 30px; 691 | height: 30px; 692 | margin-top: -45px; 693 | margin-left: calc(50vw + min(40%, 200px) - 20px); 694 | } 695 | 696 | #container-streak.max-2 .mo-fire { 697 | width: 45px; 698 | height: 45px; 699 | margin-top: -67px; 700 | margin-left: calc(50vw + min(40%, 200px) - 27px); 701 | } 702 | 703 | #container-streak.max-3 .mo-fire { 704 | width: 60px; 705 | height: 60px; 706 | margin-top: -90px; 707 | margin-left: calc(50vw + min(40%, 200px) - 40px); 708 | } 709 | 710 | #container-streak.max-4 .mo-fire { 711 | width: 80px; 712 | height: 80px; 713 | margin-top: -120px; 714 | margin-left: calc(50vw + min(40%, 200px) - 55px); 715 | } 716 | 717 | #container-streak.max .mo-fire { 718 | opacity: 1; 719 | transition: 0.1s ease-in !important; 720 | } 721 | 722 | .mo-fire { 723 | align-items: baseline; 724 | text-align: center; 725 | 726 | transition: width 1s ease-in, height 1s ease-in, opacity 0.2s ease-in; 727 | opacity: 0; 728 | margin-left: calc(50vw + min(40%, 200px) - 5px); 729 | 730 | margin-top: 0px; 731 | height: 0px; 732 | width: 0px; 733 | left: 50%; 734 | } 735 | 736 | .mo-fire svg { 737 | width: 100%; 738 | height: auto; 739 | position: relative 740 | } 741 | 742 | .flame { 743 | animation-name: flamefly; 744 | animation-duration: 2s; 745 | animation-timing-function: linear; 746 | animation-iteration-count: infinite; 747 | opacity: 0; 748 | transform-origin: 50% 50% 0; 749 | } 750 | 751 | .flame.one { 752 | animation-delay: 1s; 753 | animation-duration: 3s; 754 | } 755 | 756 | .flame3.two { 757 | animation-duration: 5s; 758 | animation-delay: 1s; 759 | } 760 | 761 | .flame-main { 762 | animation-name: flameWobble; 763 | animation-duration: 3s; 764 | animation-timing-function: linear; 765 | animation-iteration-count: infinite; 766 | } 767 | 768 | .flame-main.one { 769 | animation-duration: 4s; 770 | animation-delay: 1s; 771 | } 772 | 773 | .flame-main.two { 774 | animation-duration: 3s; 775 | animation-delay: 2s; 776 | } 777 | 778 | .flame-main.three { 779 | animation-duration: 2.1s; 780 | animation-delay: 3s; 781 | } 782 | 783 | .flame-main.four { 784 | animation-duration: 3.2s; 785 | animation-delay: 4s; 786 | } 787 | 788 | .flame-main.five { 789 | animation-duration: 2.5s; 790 | animation-delay: 5s; 791 | } 792 | 793 | @keyframes flameWobble { 794 | 50% { 795 | transform: scale(1, 1.2) translate(0, -30px) rotate(-2deg); 796 | } 797 | } 798 | 799 | @keyframes flamefly { 800 | 0% { 801 | transform: translate(0) rotate(180deg); 802 | } 803 | 804 | 50% { 805 | opacity: 1; 806 | } 807 | 808 | 100% { 809 | transform: translate(-20px, -100px) rotate(180deg); 810 | opacity: 0; 811 | } 812 | } 813 | 814 | /*Seriously small screen handling */ 815 | @media only screen and (max-height: 700px) { 816 | .card-text { 817 | font-size: 15pt; 818 | } 819 | 820 | #container-title .instructions { 821 | font-size: 8pt; 822 | } 823 | 824 | #container-title { 825 | padding: 2em; 826 | } 827 | #container-title h4 { 828 | font-size: 1.2rem; 829 | } 830 | #container-title h1 { 831 | font-size: 2rem; 832 | } 833 | 834 | #github a, #about a { 835 | font-size: 8pt; 836 | } 837 | #about { 838 | right: 2em; 839 | } 840 | #github { 841 | right: 4.5em; 842 | } 843 | 844 | .progressbar .description { 845 | font-size: 8pt; 846 | } 847 | } 848 | 849 | 850 | 851 | 852 | 853 | 854 | 855 | 856 | 857 | 858 | 859 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Is This Headline Fake or Not? 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 |
21 | 22 |
23 |
24 |
25 | 26 |
27 |
28 |

Is this Headline

29 |

fake or not?

30 |
31 |
32 | 33 | Easy 34 | 35 | 36 | Medium 37 | 38 | 39 | Hard 40 | 41 | 42 | Mixed 43 | 44 |
45 |
46 |
47 |

All done.

48 | 49 |
You got
50 |
3 / 10
51 |
right.
52 |
53 | 54 |
Play again
55 |
or
56 |
Change difficulty
57 |
58 |
59 | 73 | 85 |
86 |
87 |
88 |
89 |

Is this Headline

90 |

fake or not?

91 | 92 | We live in crazy times - but can you tell how crazy? 93 |
94 | Swipe right if you think it's real, left if fake. 95 |
96 | 97 | 98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
Streak
111 |
112 |
113 |
114 | 115 | 118 | 120 | 122 | 124 | 126 | 127 | 128 | 129 | 130 | 131 |
132 |
133 |
134 | 135 | 136 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | let DEV = false; 2 | 3 | const FALSE_POSITIVE = 100; 4 | const FALSE_NEGATIVE = 200; 5 | 6 | const FALSE_NEGATIVE_SENTENCES = [ 7 | "That one was fake", 8 | "That didn't happen", 9 | "Nope, that was fake", 10 | "That didn't happen", 11 | "Could've happened" 12 | ]; 13 | 14 | const FALSE_POSITIVE_SENTENCES = [ 15 | "That really happend", 16 | "Yea, that was real", 17 | "Nope, that was real", 18 | "That one was real", 19 | "Uhh, that actually happened" 20 | ]; 21 | 22 | 23 | class Card { 24 | constructor(headline_json, count, settings) { 25 | this.title = headline_json.text; 26 | this.is_real = headline_json.is_real; 27 | this.id = headline_json.id; 28 | 29 | this.count = count; 30 | 31 | this._create_div(); 32 | 33 | this.overlay_left = this.div.querySelector(".overlay-left"); 34 | this.overlay_right = this.div.querySelector(".overlay-right"); 35 | 36 | settings = settings ? settings : {}; 37 | this.threshold_go = settings["threshold_go"] ? settings["threshold_go"] : 0.4; 38 | this.overlay_multiplier = settings["overlay_multiplier"] ? settings["overlay_multiplier"] : 2; 39 | this.fade_multiplier = settings["fade_multiplier"] ? settings["fade_multiplier"] : 0.5; 40 | 41 | this.initial_rotation = (Math.random() - 0.5) * 5; 42 | this.initial_x_offset = (Math.random() - 0.5) * 5; 43 | this.initial_y_offset = (Math.random() - 0.5) * 5; 44 | 45 | // Hide out of view by default 46 | this.div.style.transform = `translateX(${screen.width+Math.random()*200}px) translateY(${(Math.random()-0.5)*screen.height/3}px) translateZ(1000px) rotate(${(Math.random()-0.5)*20}deg) rotateX(40deg) rotateY(30deg)`; 47 | 48 | this.gone = false;; 49 | 50 | } 51 | 52 | _create_div() { 53 | let template = ` 54 |
55 |
Fake
56 |
Real
57 |
58 | 59 |

${this.title}

60 |
61 |
`; 62 | this.div = div_from_template(template); 63 | } 64 | 65 | show() { 66 | this.div.classList.add("smooth"); 67 | this.div.style.transform = `translateX(${this.initial_x_offset}px) translateY(${this.initial_y_offset}px) rotate(${this.initial_rotation}deg)`; 68 | } 69 | 70 | activate() { 71 | 72 | this.div.ontouchstart = (event) => { 73 | this.div.classList.remove("smooth"); 74 | this.start_x = event.touches[0].clientX; 75 | this.start_y = event.touches[0].clientY; 76 | this.width = this.div.getBoundingClientRect().width; 77 | } 78 | this.div.ontouchmove = (event) => { 79 | const percentage_progress = (event.touches[0].clientX - this.start_x) / this.width; 80 | this.set_progress(percentage_progress); 81 | } 82 | this.div.ontouchend = (event) => { 83 | const percentage_progress = (event.changedTouches[0].clientX - this.start_x) / this.width; 84 | if (Math.abs(percentage_progress) > this.threshold_go) { 85 | this.go(percentage_progress / Math.abs(percentage_progress)); 86 | } else { 87 | this.comeback(); 88 | } 89 | } 90 | 91 | let drag = false; 92 | this.div.onmousedown = (event) => { 93 | drag = true; 94 | this.div.classList.remove("smooth"); 95 | this.start_x = event.clientX; 96 | this.start_y = event.clientY; 97 | this.width = this.div.getBoundingClientRect().width; 98 | } 99 | this.div.onmousemove = (event) => { 100 | if (drag) { 101 | const percentage_progress = (event.clientX - this.start_x) / this.width; 102 | this.set_progress(percentage_progress); 103 | } 104 | } 105 | this.div.onmouseup = (event) => { 106 | drag = false; 107 | 108 | const percentage_progress = (event.clientX - this.start_x) / this.width; 109 | if (Math.abs(percentage_progress) > this.threshold_go) { 110 | this.go(percentage_progress / Math.abs(percentage_progress)); 111 | } else { 112 | this.comeback(); 113 | } 114 | } 115 | } 116 | 117 | deactivate() { 118 | this.div.ontouchstart = () => {} 119 | this.div.ontouchmove = () => {} 120 | this.div.ontouchend = () => {} 121 | } 122 | 123 | set_progress(percentage_progress) { 124 | if (!this.gone) { 125 | const x_transform = this.initial_x_offset + percentage_progress * this.width; 126 | const rotation = this.initial_rotation + 10 * percentage_progress; 127 | 128 | this.div.style.opacity = 1 - sigmoid((Math.abs(this.fade_multiplier * percentage_progress) - 0.75) * 10); 129 | this.div.style.transform = `translateX(${x_transform}px) translateY(${this.initial_y_offset}px) rotate(${rotation}deg)`; 130 | 131 | const opacity = sigmoid((Math.abs(this.overlay_multiplier * percentage_progress) - 0.5) * 5); 132 | // console.log(opacity); 133 | if (percentage_progress < 0) { 134 | this.overlay_left.style.opacity = opacity; 135 | } else { 136 | this.overlay_right.style.opacity = opacity; 137 | } 138 | } 139 | } 140 | 141 | comeback() { 142 | this.div.classList.add("smooth"); 143 | 144 | this.overlay_left.style.opacity = 0; 145 | this.overlay_right.style.opacity = 0; 146 | 147 | this.div.style.opacity = 1; 148 | this.div.style.transform = `translateX(${this.initial_x_offset}px) translateY(${this.initial_y_offset}px) rotate(${this.initial_rotation}deg)`; 149 | } 150 | 151 | go(direction) { 152 | if (!this.gone) { 153 | this.div.classList.add("smooth"); 154 | this.set_progress(direction * 3); 155 | setTimeout(() => { 156 | this.div.classList.add("hide"); 157 | }, 1000); 158 | if (this.stack) { 159 | this.stack.next(); 160 | } 161 | 162 | this.gone = true; 163 | 164 | if (direction < 0) 165 | this.left(); 166 | else 167 | this.right(); 168 | } 169 | } 170 | 171 | // Opinion Real 172 | left() { 173 | if(this.is_real) { 174 | delete_progress(FALSE_POSITIVE); 175 | 176 | submit_answer(this.id, false); 177 | } else { 178 | add_progress(); 179 | submit_answer(this.id, true); 180 | } 181 | } 182 | 183 | // Opinion Fake 184 | right() { 185 | if(!this.is_real) { 186 | delete_progress(FALSE_NEGATIVE); 187 | 188 | submit_answer(this.id, false); 189 | } else { 190 | add_progress(); 191 | submit_answer(this.id, true); 192 | } 193 | } 194 | } 195 | 196 | function submit_answer(id, was_correct) { 197 | if(!DEV) { 198 | return fetch("https://fakeornot.menzinger.workers.dev", { 199 | method: "post", 200 | body: JSON.stringify({ 201 | "id": id, 202 | "correct": was_correct 203 | }) 204 | }); 205 | }else { 206 | console.log("process not submitted - dev mode") 207 | } 208 | } 209 | 210 | function add_progress(percent_to_add = 10) { 211 | let progress = document.querySelector(".progress"); 212 | let count = document.querySelector(".count"); 213 | let progressbar = document.querySelector(".progressbar"); 214 | let container = document.querySelector("#container-streak"); 215 | 216 | if (progress.percent == null) 217 | progress.percent = 0; 218 | 219 | old_progress = progress.percent 220 | new_progress = progress.percent + percent_to_add; 221 | progress.percent = new_progress; 222 | 223 | // console.log(old_progress, new_progress) 224 | 225 | if (old_progress < 90 && new_progress >= 90) 226 | animate(container, "shake-bottom3"); 227 | 228 | if (old_progress < 80 && new_progress >= 80) 229 | animate(container, "shake-bottom2"); 230 | 231 | if (old_progress < 70 && new_progress >= 70) 232 | animate(container, "shake-bottom1"); 233 | 234 | if (old_progress < 110 && new_progress >= 110) { 235 | container.classList.add("max", "max-1"); 236 | animate(container, "shake-bottom1"); 237 | } else if (old_progress < 150 && new_progress >= 150) { 238 | container.classList.add("max", "max-2"); 239 | animate(container, "shake-bottom2"); 240 | } else if (old_progress < 200 && new_progress >= 200) { 241 | container.classList.add("max", "max-3"); 242 | animate(container, "shake-bottom3"); 243 | } else if (old_progress < 250 && new_progress >= 250) { 244 | container.classList.add("max", "max-4"); 245 | animate(container, "shake-bottom3"); 246 | } else if (new_progress > 100) { 247 | animate(container, "shake-bottom1"); 248 | } 249 | 250 | progress.style.width = (progress.percent >= 100 ? 100 : progress.percent) + "%"; 251 | 252 | count.textContent = new_progress / percent_to_add; 253 | animate(count, "highlight"); 254 | 255 | } 256 | 257 | function animate(element, animation_class, time = 500) { 258 | element.classList.add(animation_class) 259 | setTimeout(() => { 260 | element.classList.remove(animation_class) 261 | }, time); 262 | } 263 | 264 | function delete_progress(type) { 265 | 266 | 267 | if(type) { 268 | let text; 269 | 270 | if(type == FALSE_NEGATIVE) 271 | text = FALSE_NEGATIVE_SENTENCES[Math.floor(Math.random()*FALSE_NEGATIVE_SENTENCES.length)]; 272 | else 273 | text = FALSE_POSITIVE_SENTENCES[Math.floor(Math.random()*FALSE_POSITIVE_SENTENCES.length)]; 274 | 275 | let flyin = document.getElementById("flyin"); 276 | let flyin_text = flyin.querySelector("#flyin-text"); 277 | flyin_text.textContent = text; 278 | 279 | flyin.style.transform = `rotate(${(Math.random()-0.5)*30}deg)`; 280 | 281 | animate(flyin, "fly", 1000); 282 | } 283 | 284 | let progress = document.querySelector(".progress"); 285 | let progressbar = document.querySelector(".progressbar"); 286 | let container = document.querySelector("#container-streak"); 287 | let count = document.querySelector(".count"); 288 | 289 | container.classList.remove("max"); 290 | container.classList.remove("max-1"); 291 | container.classList.remove("max-2"); 292 | container.classList.remove("max-3"); 293 | container.classList.remove("max-4"); 294 | 295 | animate(progressbar, "bounce-right"); 296 | 297 | progress.percent = 0; 298 | progress.style.width = progress.percent + "%"; 299 | 300 | count.textContent = ""; 301 | } 302 | 303 | async function confetti_rain(color = "red", amount = 150) { 304 | let celebration_container = document.getElementById("celebration_container"); 305 | let confetti = []; 306 | for (let x = 0; x < amount; x++) { 307 | let c = document.createElement("div") 308 | c.classList.add("confetti") 309 | 310 | c.style.opacity = 0.8; 311 | 312 | let height = 5 + 20 * Math.random() 313 | 314 | c.style.height = `${5+5*Math.random()}px`; 315 | c.style.width = `${50 / height}px`; 316 | // c.style.backgroundColor = `rgb(${255*Math.random()}, ${255*Math.random()}, ${255*Math.random()})`; 317 | c.style.backgroundColor = color; 318 | c.style.top = -10 + "px"; 319 | c.style.left = 10 + screen.width * Math.random() * 0.92 + "px"; 320 | 321 | let drop = (1 + Math.random() * 2) / 1.5; 322 | let start = 2 * Math.random(); 323 | let offset = 0.75; 324 | c.style.transition = `all ${drop}s linear ${start}s, opacity linear ${drop-offset}s ${start}s`; 325 | 326 | celebration_container.appendChild(c) 327 | confetti.push(c); 328 | } 329 | 330 | for (let c of confetti) { 331 | setTimeout(() => { 332 | c.style.transform = `translateY(${screen.height/4 + Math.random() * screen.height}px) rotate(${(Math.random()-0.5)*180}deg)`; 333 | c.style.opacity = 0; 334 | }, 10); 335 | } 336 | 337 | setTimeout(() => { 338 | console.log(confetti.length) 339 | for (let c of confetti) { 340 | c.remove(); 341 | } 342 | }, 3000); 343 | } 344 | 345 | class Stack { 346 | constructor(cards, empty_callback) { 347 | this.cards = cards; 348 | for (let card of cards) { 349 | card.stack = this; 350 | } 351 | 352 | this.empty_callback = empty_callback; 353 | 354 | this.index = this.cards.length - 1; 355 | this.current = this.cards[this.index]; 356 | } 357 | 358 | add_to_container() { 359 | let container = document.querySelector("#container-headlines"); 360 | for (let card of this.cards) { 361 | container.appendChild(card.div); 362 | } 363 | } 364 | 365 | async show_all() { 366 | for (let card of this.cards) { 367 | await sleep(75 + Math.random() * 50); 368 | card.show() 369 | } 370 | this.current.activate(); 371 | } 372 | 373 | next() { 374 | this.index = this.index - 1; 375 | if (this.index >= 0) { 376 | this.current = this.cards[this.index]; 377 | this.current.activate(); 378 | } else { 379 | this.empty_callback(); 380 | } 381 | } 382 | } 383 | 384 | let div_from_template = (filled_template_string) => { 385 | let wrapper = document.createElement("div"); 386 | wrapper.innerHTML = filled_template_string; 387 | return wrapper.firstElementChild; 388 | } 389 | 390 | function sigmoid(t) { 391 | return 1 / (1 + Math.exp(-t)); 392 | } 393 | 394 | function sleep(ms) { 395 | return new Promise(resolve => setTimeout(resolve, ms)); 396 | } 397 | 398 | 399 | function headlines_to_cards(headlines) { 400 | let cards = [] 401 | for (let i = 0; i < 10; i++) { 402 | cards.push(new Card(headlines[i], headlines.length - i)); 403 | } 404 | return cards 405 | } 406 | 407 | function get_new_headlines() { 408 | return fetch("https://get-headlines.fake-or-not.workers.dev/").then(response => response.json()); 409 | } 410 | 411 | async function play(empty_callback = play) { 412 | let cards = headlines_to_cards(await get_new_headlines()); 413 | let stack = new Stack(cards, empty_callback = empty_callback); 414 | stack.add_to_container(); 415 | stack.show_all(); 416 | } 417 | 418 | async function init() { 419 | const queryParams = new URLSearchParams(window.location.search); 420 | const difficulty = queryParams.get('difficulty'); 421 | 422 | play() 423 | } 424 | --------------------------------------------------------------------------------